圖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位元組的整數倍)的圖像進行灰階直方圖繪制,如果不考慮未用空間的話會導緻錯誤結果(本實驗中錯誤程度較小)。
圖2.未考慮圖像中未用空間的錯誤結果
用Matlab進行灰階直方圖計算會發現真實結果中最大灰階的像素個數不是52811,而是53195。
f = imread('GrayTest.bmp');
h = imhist(f)
圖3.Matlab計算出的最大頻率灰階像素個數
錯誤的原因是,因為圖像的位元組寬度不是4位元組的整數倍,圖像的像素數組包括了未用空間(每一行中都包括了未用空間)程式隻從規模為bmpData.Stride * bmpData.Height位元組的像素數組中複制了curBitmap.Width * curBitmap.Height規模的位元組(灰階像素,1位元組),這些位元組中既包含了像素也包含了未用空間,如圖4所示:
圖4.出錯時對像素數組複制的位元組
最後在代碼中考慮未用空間能得到正确結果:
圖5.正确結果