c# - .Net 使用 Lockbits 从位图中获取 RGB 值

标签 c# vb.net bitmap rgb lockbits

我正在使用下面的代码从图像中提取 RGB 值,有时这可行,但是在某些文件上(似乎 Stride 不能被位图的宽度整除)它返回混合值:

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# 回答,那也不是问题。

最佳答案

问题是您没有考虑步幅值。 Stride 始终被填充,以便每个图像行的字节数组的宽度可以被 4 整除。这是与内存复制和 CPU 工作方式相关的优化,几十年前仍然有用。

例如,如果一张图像的宽度为 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()

关于c# - .Net 使用 Lockbits 从位图中获取 RGB 值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13920428/

相关文章:

mysql - 将月总收入放入 vb.net 的图表中

mysql - 这是为数据库应用程序提供服务的安全方法吗?

vb.net - AppendLine 和替换

android - 如何在设置为 imageview 之前将位图从相机的 10mb 图像压缩到 300kb

c# - C 缓冲区到 C# 位图对象

java - 使用 android 显示来自互联网的图像

c# - 如何将一个列表分成两个可以容纳奇数个计数的列表?

c# - 如何转置矩阵?

c# - 检测 SplitContainer 何时折叠更改

c# - 聚合与任意,用于扫描 IEnumerable<bool> 等对象