天天看點

C#數字圖像處理時注意圖像的未用區域

C#數字圖像處理時注意圖像的未用區域

圖1. 被鎖定圖像像素數組基本布局

        如圖1所示,數組的寬度并不一定等于圖像像素數組的寬度,還有一部分未用區域。這是為了提高效率,系統要确定每行的位元組數必須為4的倍數。例如一幅24位、寬為17個像素的圖像,它需要每行占有的空間為51(3

* 17)個位元組,但51不是4的倍數,是以還需要擴充1個位元組,進而使每行的位元組數擴充為52(4 * 13,即Stride=52),這樣就滿足了每行位元組數是4的倍數的條件。需要擴充多少個位元組不僅是由圖像的寬度決定,而且還由圖像像素的格式決定。

        如果處理的是任意寬度的圖像,那麼在進行下一行掃描的時候,需要把不含圖像資料、僅起對齊作用的擴充位元組去掉。此時對圖像像素數組的周遊的形式如下:

(1).灰階圖像:

Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);  

BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);  

IntPtr ptr = bmpData.Scan0; // 首位址  

int bytes = bmpData.Stride * bmpData.Height;    // 像素個數,包括未用空間  

byte[] grayValues = new byte[bytes];  

System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);  

for (int i = 0; i < bmpData.Height; i++)  

{  

        // 僅處理每行中為圖像像素的資料,舍棄未用空間  

        for (int j = 0; j < bmpData.Width; j++)  

        {  

                // use of grayValues[i * bmpData.Stride + j];  

        }  

}  

System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes);  

bitmap.UnlockBits(bmpData);  

或者:

// 擷取圖像參數  

int stride = bmpData.Stride;            // 掃描線的寬度  

int offset = stride - width;            // 顯示寬度與掃描線寬度的間隙  

IntPtr iptr = bmpData.Scan0;            // 擷取bmpData的記憶體起始位置  

int scanBytes = stride * height;        // 用stride寬度,表示這是記憶體區域的大小  

// 位置指針,指向源數組  

int posScan = 0;  

byte[] grayValues = new byte[scanBytes];    // <span style="font-family: Arial; ">為目标數組配置設定記憶體</span>  

for (int x = 0; x < height; x++)  

        // 下面的循環節是模拟行掃描  

        for (int y = 0; y < width; y++)  

                // grayValues[posScan++]  

        posScan += offset;               // 行掃描結束,跳過未用空間位元組  

(2).24位RGB圖像:

byte[] rgbValues = new byte[bytes];  

System.Runtime.InteropServices.Marshal.Copy(ptr, rbgValues, 0, bytes);  

        for (int j = 0; j < bmpData.Width * 3; j += 3)  

                // R: rgbValues[i * bmpData.Stride + j + 2]  

                // G: rgbValues[i * bmpData.Stride + j + 1]  

                // B: rgbValues[i * bmpData.Stride + j]  

System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);  

        以上代碼将圖像的整個像素數組都複制到數組grayValue或者rgbValue中,包括像素和僅用于對齊的未用空間,然後再在數組grayValue或者rgbValue中隻對像素資料進行處理,跳過每一行的未用空間(跳過末尾幾列)。

        以下實驗中,對任意尺度(每行位元組數不是4位元組的整數倍)的圖像進行灰階直方圖繪制,如果不考慮未用空間的話會導緻錯誤結果(本實驗中錯誤程度較小)。

C#數字圖像處理時注意圖像的未用區域

圖2.未考慮圖像中未用空間的錯誤結果

        用Matlab進行灰階直方圖計算會發現真實結果中最大灰階的像素個數不是52811,而是53195。

f = imread('GrayTest.bmp');  

h = imhist(f)  

C#數字圖像處理時注意圖像的未用區域

圖3.Matlab計算出的最大頻率灰階像素個數

        錯誤的原因是,因為圖像的位元組寬度不是4位元組的整數倍,圖像的像素數組包括了未用空間(每一行中都包括了未用空間)程式隻從規模為bmpData.Stride * bmpData.Height位元組的像素數組中複制了curBitmap.Width * curBitmap.Height規模的位元組(灰階像素,1位元組),這些位元組中既包含了像素也包含了未用空間,如圖4所示:

C#數字圖像處理時注意圖像的未用區域

圖4.出錯時對像素數組複制的位元組

        最後在代碼中考慮未用空間能得到正确結果:

C#數字圖像處理時注意圖像的未用區域

圖5.正确結果

繼續閱讀