C# BitmapData和Marshal.Copy()用法
//此函數用法例子如下:
public static byte[] GetGrayArray(Bitmap srcBmp, Rectangle rect) { //將Bitmap鎖定到系統內存中,獲得BitmapData //這里的第三個參數確定了該圖像信息時rgb存儲還是Argb存儲 BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //位圖中第一個像素數據的地址。它也可以看成是位圖中的第一個掃描行 IntPtr srcPtr = srcBmpData.Scan0; //將Bitmap對象的信息存放到byte數組中 int scanWidth = srcBmpData.Width * 3; int src_bytes = scanWidth * rect.Height; //int srcStride = srcBmpData.Stride; byte[] srcValues = new byte[src_bytes]; byte[] grayValues = new byte[rect.Width * rect.Height]; //RGB[] rgb = new RGB[srcBmp.Width * rows]; //復制GRB信息到byte數組 Marshal.Copy(srcPtr, srcValues, 0, src_bytes); //LogHelper.OutputArray(srcValues, rect.Width * 3, rect.Height, true); //Marshal.Copy(dstPtr, dstValues, 0, dst_bytes); //解鎖位圖 srcBmp.UnlockBits(srcBmpData); //灰度化處理 int m = 0, j = 0; int k = 0; byte gray; //根據Y = 0.299*R + 0.587*G + 0.114*B,intensity為亮度 for (int i = 0; i < rect.Height; i++) //行 { for (j = 0; j < rect.Width; j++) //列 { //注意位圖結構中RGB按BGR的順序存儲 k = 3 * j; gray = (byte)(srcValues[i * scanWidth + k + 2] * 0.299 + srcValues[i * scanWidth + k + 1] * 0.587 + srcValues[i * scanWidth + k + 0] * 0.114); grayValues[m] = gray; //將灰度值存到byte一維數組中 m++; } } return grayValues; }
/*
以上方法,主要是實現一個圖像指定矩形區域的信息進行處理,
以一維數組形式返回指定矩形區域的灰度值,
此方法會遇到一個很棘手的問題
假設一張圖像的大小256*256,想要處理圖像的上下部分區域為256*10,左右部分區域10*256的信息,
則創建矩形區域為:
*/
// 上部分區域:
Rectangle rectTop = new Rectangle(0,0,256,10);
// 下部分區域:
Rectangle rectBottom = new Rectangle(0,256-10,256,10);
// 左部分區域:
Rectangle rectLeft = new Rectangle(0,0,10,256);
// 有部分區域:
Rectangle rectRight = new Rectangle(256-10,0,10,256);
/*
此時上下部分沒有問題,左右部分則會出現問題,原因很簡單
闡述如下:
是BitmapData,Marshal.Copy()函數造成的
C#處理圖像時使用BitmapData,這個是將處理的圖像鎖定到內存中,為了提高效率,將圖像鎖定要內存
IntPtr srcPtr = srcBmpData.Scan0;這個是指向鎖定圖像的第一個地址,然后按照順序讀取像素數據
,這時,左右部分區域的寬度只有10,如果這一行讀取完之后,應該此時換行讀取下一行像素數據,
但是實際上它沒有,而是一直按照順序讀取,沒有換行,此時問題就出現了,
因為左區域是圖像的一部分,整個圖像比方如下:
“-”表示左區域,即要處理的部分;“+”表示圖像的剩余部分
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
掃描時第一行讀取到第三個“-”號之后,應該換行,但是沒有,它繼續讀取“++++”,一直讀取到行尾,
這才換行,問題就是這么產生的,
上圖如果沒有理解的,接下來我畫一個圖如下:
思考一下,兩種辦法如下
[A]首先,想到的最好的辦法我要鎖定哪一部分區域就要取得相應區域的像素數據,但是目前這個
我沒有實現
[B]其次,我用了兩外一種方法解決了,就是將源圖像的要計算的區域或者要處理的區域圖像裁切好,
因為源圖像后面是要用的,所以裁切后得到的圖像另保存之,傳遞給方法,然后將上述方法修改為
*/
///這里的srcBmp是裁切后要處理的區域的圖像
public static byte[] GetGrayArray(Bitmap srcBmp)
{
//將Bitmap鎖定到系統內存中,獲得BitmapData
//這里的第三個參數確定了該圖像信息時rgb存儲還是Argb存儲
Rectangle rect = new Rectangle(0,0,srcBmp.Width,srcBmp.Height); //表示要鎖定全圖
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//位圖中第一個像素數據的地址。它也可以看成是位圖中的第一個掃描行
IntPtr srcPtr = srcBmpData.Scan0;
//將Bitmap對象的信息存放到byte數組中
int scanWidth = srcBmpData.Width * 3;
int src_bytes = scanWidth * rect.Height;
//int srcStride = srcBmpData.Stride;
byte[] srcValues = new byte[src_bytes];
byte[] grayValues = new byte[rect.Width * rect.Height];
//RGB[] rgb = new RGB[srcBmp.Width * rows];
//復制GRB信息到byte數組
Marshal.Copy(srcPtr, srcValues, 0, src_bytes);
//LogHelper.OutputArray(srcValues, rect.Width * 3, rect.Height, true);
//Marshal.Copy(dstPtr, dstValues, 0, dst_bytes);
//解鎖位圖
srcBmp.UnlockBits(srcBmpData);
//灰度化處理
int m = 0, j = 0;
int k = 0;
byte gray;
//根據Y = 0.299*R + 0.587*G + 0.114*B,intensity為亮度
for (int i = 0; i < rect.Height; i++) //行
{
for (j = 0; j < rect.Width; j++) //列
{
//注意位圖結構中RGB按BGR的順序存儲
k = 3 * j;
gray = (byte)(srcValues[i * scanWidth + k + 2] * 0.299
+ srcValues[i * scanWidth + k + 1] * 0.587
+ srcValues[i * scanWidth + k + 0] * 0.114);
grayValues[m] = gray; //將灰度值存到byte一維數組中
m++;
}
}
return grayValues;
}
//這樣也能解決上述問題,求哪位大神賜教方法[A]的實現,謝謝,這個問題先記錄到此。
//修改部分:
//今天又發現一個問題,左右部分圖像裁切后處理的時候自動才每一行后面加上兩個0,0
//這就導致計算出現問題了,為了解決此問題,可以講上面的方法再進行修改
//只需要將上述方法中的
int scanWidth = srcBmpData.Width * 3;
//修改為
int scanWidth = srcBmpData.Stride;
//為什么用srcBmpData.Width * 3作為掃描行寬度我當時是這么理解的,上面的
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//用的是PixelFormat.Format24bppRgb這個,這個表示每個像素是24為,紅色,綠色,藍色各使用8位
//那么byte[]數組用來存儲的時候相當於寬度*3,其實這里如果是截圖圖像的水平區域的話沒有問題
//要是截取的是圖像的左右兩側部分區域則會出現上述問題,即每一行后面會多出兩個字節,值為0,0
//至此測試通過,問題也算完美的解決了