2012-12-17 54 views
3

我使用下面的代碼從圖像中提取RGB值,有時這適用於某些文件(看起來Stride不能被位圖寬度整除)在返回混合起來價值觀:.net從一個位圖使用Lockbits獲取RGB值

Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height) 
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb) 
Dim ptr As IntPtr = bmpData.Scan0 
Dim cols As New List(Of Color) 
Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height 
Dim rgbValues(bytes - 1) As Byte 
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes) 

' Retrieve RGB values 
For i = modByte To rgbValues.Length Step 3 
    cols.Add(Color.FromArgb(rgbValues(i + 2), rgbValues(i + 1), rgbValues(i))) 
Next 

bmp.UnlockBits(bmpData) 
bmp.Dispose() 
Dim colsCnt As List(Of RgbPixels) = cols.GroupBy(Function(g) New With {Key .R = g.R, Key .G = g.G, Key .B = g.B}).Select(Function(s) New RgbPixels With {.Colour = Color.FromArgb(s.Key.R, s.Key.G, s.Key.B), .Amount = s.Count()}).ToList() 

分組所產生的顏色後,這些值是這樣的:

R G B 
255 255 255 
255 255 0 
255 0 0 
0 0 255 
0 255 255 

或一些變化,當他們應該只是:

R G B 
255 255 255 
0 0 0 

請指點我正確的方向,BTW我的源bmp也在PixelFormat.Format24bppRgb中,所以我不認爲這是問題所在。此外,如果你只能在C#中回答,那不是問題。

回答

5

問題是你沒有考慮步幅值。 Stride總是被填充,因此每個圖像行的字節數組的寬度可以被4除。這是一個與內存複製相關的優化,以及CPU如何工作,這種優化幾十年前就已經發生,並且仍然有用。

F.ex,如果一個圖像具有的13個像素的寬度,所述步幅會是這樣(簡化爲一個組件):

============= (width 13 pixels = 13 bytes when using RGB) 
================ (stride would be 16) 

爲14個像素,將是這樣的圖像:

============== (width 14 pixels = 14 bytes when using RGB) 
================ (stride would still be 16) 

因此,在你的代碼,你需要處理一個箭步行,而不是一個字節數組,除非你使用的是固定的,確定圖像的寬度。

我修改您的代碼,以便它通過步幅跳過行:

Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height) 
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb) 
Dim ptr As IntPtr = bmpData.Scan0 
Dim cols As New List(Of Color) 
Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height 
Dim rgbValues(bytes - 1) As Byte 
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes) 

Dim x, y, dx, l as Integer 

For y = 0 To rect.Height - 1 

    l = y * bmpData.Stride 'calulate line based on stride 

    For x = 0 To rect.Width - 1 

     dx = l + x * 3 '3 for RGB, 4 for ARGB, notice l is used as offset 

     cols.Add(Color.FromArgb(rgbValues(dx + 2), _ 
           rgbValues(dx + 1), _ 
           rgbValues(dx))) 
    Next 
Next 

' Retrieve RGB values 
'For i = modByte To rgbValues.Length Step 3 
'  cols.Add(Color.FromArgb(rgbValues(i + 2), rgbValues(i + 1), rgbValues(i))) 
'Next 

bmp.UnlockBits(bmpData) 
bmp.Dispose() 
+0

謝謝,TWEAK似乎已經完成了帽子戲法。一個小問題雖然通過確認,但我注意到您正在閱讀BGR中的數組值,我在此看到了相互矛盾的觀點;有人說它絕對是RGB,請確認。 – Azza

+0

在這種情況下,BGR是正確的。英特爾/ Win平臺與小端/大端/字節順序相關的特性以及指針和整數之間的轉換。 – K3N