3

以下のコードを使用して画像から 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

正しい方向に私を指摘してください、ところで私のソース bmp は PixelFormat.Format24bppRgb にもあるので、それが問題だとは思いません。また、C# でしか答えられない場合でも問題ありません。

4

1 に答える 1

5

問題は、ストライド値を考慮していないことです。画像行ごとのバイト配列の幅が 4 で割り切れるように、ストライドは常にパディングされます。これは、メモリ コピーと CPU の動作に関連する最適化であり、何十年も前から有効です。

F.ex、1 つの画像の幅が 13 ピクセルの場合、ストライドは次のようになります (1 つのコンポーネントに簡略化されます)。

=============    (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()
于 2012-12-17T22:13:31.063 に答える