最近要轉開發平台正研究C#C#好是好不過處理圖片時一個像素一個像素的操作像素不是一般的慢其實Delphi也一樣但好在Delphi的Bitmap類提供了ScanLines可以一行一行的讀圖效率比較高C#應該也有類似的東東經過一番搜索終於發現了BitmapData類
先看個例子這是對一張位圖的每個像素按FF取補然後輸出到一個新圖(代碼有點啰嗦不過應該可以說明問題了)
int h = m_BmpHeight;
int w = m_BmpWidth;
Bitmap bmpOut = new Bitmap(w h PixelFormatFormatbppRgb);
BitmapData dataIn = m_BmpLockBits(new Rectangle(wh)ImageLockModeReadOnlyPixelFormatFormatbppRgb);
BitmapData dataOut = bmpOutLockBits(new Rectangle( w h) ImageLockModeReadWrite PixelFormatFormatbppRgb);
unsafe
{
byte* pIn = (byte*)(dataInScanToPointer());
byte * pOut = (byte*)(dataOutScanToPointer());
for (int y = ; y < dataInHeight; y++)
{
for (int x = ; x < dataInWidth; x++)
{
pOut[] = (byte)( pIn[]);
pOut[] = (byte)( pIn[]);
pOut[] = (byte)( pIn[]);
pIn += ;
pOut += ;
}
pIn += dataInStride dataInWidth * ;
pOut += dataOutStride dataOutWidth * ;
}
}
bmpOutUnlockBits(dataOut);
m_BmpUnlockBits(dataIn);
貌似比Delphi復雜得多難道我真的天生對指針過敏?還是Delphi的比較好理解就是掃描每一行然後對當前像素點的三個分量做處理非常方便而且C#代碼中的Stride是個什麼東東?
查找了不少資料現在我是這麼理解的假設有一張圖片寬度為因為是FormatbppRgb格式(每像素字節在以下的討論中除非特別說明否則Bitmap都被認為是位RGB)的顯然每一行需要*=個字節存儲對於Bitmap就是如此但對於BitmapData雖然BitmapDataWidth還是等於BitmapWidth但大概是出於顯示性能的考慮每行的實際的字節數將變成大於等於它的那個離它最近的的整倍數此時的實際字節數就是Stride就此例而言不是的整倍數而比大的離最近的的倍數是所以這個BitmapDataStride = 顯然當寬度本身就是的倍數時BitmapDataStride = BitmapWidth * 畫個圖可能更好理解
RGB 分別代表個原色分量字節BGR就表示一個像素為了看起來方便我在每個像素之間插了個空格實際上是沒有的X表示補足的倍數而自動插入的字節為了符合人類的閱讀習慣我分行了其實在計算機內存中應該看成連續的一大段
Scan
Stride
Width 注Width是圖片(BGR作為一個單位)寬度
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
現在應該很好理解了首先用 BitmapDataScan找到第個像素的第個分量的地址這個地址指向的是個byte類型所以當時定義為byte* pIn
行掃描時在當前指針位置(不妨看成當前像素的第個顏色分量)連續取出三個值(個原色分量注意 代表的次序是B G R在取指針指向的值時貌似p[n]和p += n再取p[]是等價的)然後下移個位置(pIn += 看成指到下一個像素的第個顏色分量)做過BitmapWidth次操作後就到達了BitmapWidth * 的位置應該要跳過圖中標記為X的字節了(共有Stride Width * 個字節)代碼中就是 pIn += dataInStride dataInWidth * ;
跳過以後指針就到達下行的第個像素了按照此算法一共需要做BitmapHeight次行掃描(代碼就是 for (int y = ; y < dataInHeight; y++))
另外因為使用了unsafe所以編譯的時候需要設置允許不安全的代碼
From:http://tw.wingwit.com/Article/program/ASP/201311/21881.html