圖1. 被鎖定圖像像素數(shù)組基本布局
如圖1所示,,數(shù)組的寬度并不一定等于圖像像素數(shù)組的寬度,,還有一部分未用區(qū)域。這是為了提高效率,,系統(tǒng)要確定每行的字節(jié)數(shù)必須為4的倍數(shù),。例如一幅24位、寬為17個像素的圖像,,它需要每行占有的空間為51(3
* 17)個字節(jié),,但51不是4的倍數(shù),因此還需要擴(kuò)充1個字節(jié),,從而使每行的字節(jié)數(shù)擴(kuò)展為52(4 * 13,,即Stride=52),這樣就滿足了每行字節(jié)數(shù)是4的倍數(shù)的條件,。需要擴(kuò)展多少個字節(jié)不僅是由圖像的寬度決定,,而且還由圖像像素的格式?jīng)Q定。
如果處理的是任意寬度的圖像,,那么在進(jìn)行下一行掃描的時候,,需要把不含圖像數(shù)據(jù)、僅起對齊作用的擴(kuò)展字節(jié)去掉,。此時對圖像像素數(shù)組的遍歷的形式如下:
(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; // 像素個數(shù),,包括未用空間
-
byte[] grayValues = new byte[bytes];
-
System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
-
for (int i = 0; i < bmpData.Height; i++)
-
{
-
// 僅處理每行中為圖像像素的數(shù)據(jù),舍棄未用空間
-
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);
或者:
-
// 獲取圖像參數(shù)
-
int stride = bmpData.Stride; // 掃描線的寬度
-
int offset = stride - width; // 顯示寬度與掃描線寬度的間隙
-
IntPtr iptr = bmpData.Scan0; // 獲取bmpData的內(nèi)存起始位置
-
int scanBytes = stride * height; // 用stride寬度,,表示這是內(nèi)存區(qū)域的大小
-
-
// 位置指針,指向源數(shù)組
-
int posScan = 0;
-
byte[] grayValues = new byte[scanBytes]; // <span style="font-family: Arial; ">為目標(biāo)數(shù)組分配內(nèi)存</span>
-
-
for (int x = 0; x < height; x++)
-
{
-
// 下面的循環(huán)節(jié)是模擬行掃描
-
for (int y = 0; y < width; y++)
-
{
-
// grayValues[posScan++]
-
}
-
posScan += offset; // 行掃描結(jié)束,,跳過未用空間字節(jié)
-
}
(2).24位RGB圖像:
-
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; // 像素個數(shù),包括未用空間
-
byte[] rgbValues = new byte[bytes];
-
System.Runtime.InteropServices.Marshal.Copy(ptr, rbgValues, 0, bytes);
-
for (int i = 0; i < bmpData.Height; i++)
-
{
-
// 僅處理每行中為圖像像素的數(shù)據(jù),,舍棄未用空間
-
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);
-
bitmap.UnlockBits(bmpData);
以上代碼將圖像的整個像素數(shù)組都復(fù)制到數(shù)組grayValue或者rgbValue中,,包括像素和僅用于對齊的未用空間,然后再在數(shù)組grayValue或者rgbValue中只對像素數(shù)據(jù)進(jìn)行處理,,跳過每一行的未用空間(跳過末尾幾列),。
以下實驗中,對任意尺度(每行字節(jié)數(shù)不是4字節(jié)的整數(shù)倍)的圖像進(jìn)行灰度直方圖繪制,,如果不考慮未用空間的話會導(dǎo)致錯誤結(jié)果(本實驗中錯誤程度較?。?/span>
圖2.未考慮圖像中未用空間的錯誤結(jié)果
用Matlab進(jìn)行灰度直方圖計算會發(fā)現(xiàn)真實結(jié)果中最大灰度的像素個數(shù)不是52811,,而是53195,。
-
f = imread('GrayTest.bmp');
-
h = imhist(f)
圖3.Matlab計算出的最大頻率灰度像素個數(shù)
錯誤的原因是,,因為圖像的字節(jié)寬度不是4字節(jié)的整數(shù)倍,,圖像的像素數(shù)組包括了未用空間(每一行中都包括了未用空間)程序只從規(guī)模為bmpData.Stride * bmpData.Height字節(jié)的像素數(shù)組中復(fù)制了curBitmap.Width * curBitmap.Height規(guī)模的字節(jié)(灰度像素,,1字節(jié)),這些字節(jié)中既包含了像素也包含了未用空間,如圖4所示:
圖4.出錯時對像素數(shù)組復(fù)制的字節(jié)
最后在代碼中考慮未用空間能得到正確結(jié)果:
圖5.正確結(jié)果
|