对于图像中的每个像素,获取颜色、像素位置和相对于图像的坐标
For each pixel in a image, get the color, pixel position, and coordinates relatively to the image
场景
我正在尝试编写一个将使用以下结构的函数,PixelInfo
,到 return 源图像中每个像素的集合,将存储 Color
、像素位置和 Point
以及相对于图像的坐标位置:
<Serializable>
<StructLayout(LayoutKind.Sequential)>
Public Structure PixelInfo
Public ReadOnly Property Color As Color
Get
Return Me.colorB
End Get
End Property
Private ReadOnly colorB As Color
Public ReadOnly Property Position As Integer
Get
Return Me.positionB
End Get
End Property
Private ReadOnly positionB As Integer
Public ReadOnly Property Location As Point
Get
Return Me.locationB
End Get
End Property
Private ReadOnly locationB As Point
<DebuggerStepThrough>
Public Sub New(ByVal color As Color,
ByVal position As Integer,
ByVal location As Point)
Me.colorB = color
Me.positionB = position
Me.locationB = location
End Sub
End Structure
这是函数:
<DebuggerStepThrough>
<Extension>
Iterator Function GetPixelInfo(ByVal sender As Image) As IEnumerable(Of PixelInfo)
Dim bytesPerPixel As Integer = (Image.GetPixelFormatSize(sender.PixelFormat) \ 8)
If (bytesPerPixel <> 3) AndAlso (bytesPerPixel <> 4) Then
Throw New NotImplementedException(
message:="Only PixelFormats that has 3 or 4 bytes-per-pixel are supported.")
Else
' Lock the bitmap's bits.
Dim bmp As Bitmap = DirectCast(sender, Bitmap)
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As BitmapData = bmp.LockBits(rect, ImageLockMode.ReadWrite,
bmp.PixelFormat)
' Get the address of the first line.
Dim ptr As IntPtr = bmpData.Scan0
' Declare an array to hold the bytes of the bitmap.
Dim numBytes As Integer = (Math.Abs(bmpData.Stride) * rect.Height)
Dim rgbData(numBytes - 1) As Byte
' Copy the RGB values into the array.
Marshal.Copy(ptr, rgbData, 0, numBytes)
' Unlock the bits.
bmp.UnlockBits(bmpData)
' Iterate the pixels.
For i As Integer = 0 To (rgbData.Length - bytesPerPixel) Step bytesPerPixel
Dim color As Color = color.FromArgb(red:=rgbData(i + 2),
green:=rgbData(i + 1),
blue:=rgbData(i))
Dim position As Integer = (i \ bytesPerPixel)
Dim location As Point =
New Point(X:=(position Mod rect.Width),
Y:=(position - (position Mod rect.Width)) \ rect.Width)
Yield New PixelInfo(color, position, location)
Next i
End If
End Function
问题
我已经针对像素格式为 PixelFormat.Format32bppRgb
和 PixelFormat.Format32bppArgb
的图像对其进行了测试,它似乎按预期工作。
我遇到的问题是,如果我使用像素格式为 PixelFormat.Format24bppRgb
的图像,一切都会出错,我得到的 Color
与真实颜色不对应,而且我还得到更多像素比图像有。
问题
我的计算哪里失败了?如何修复函数以使用 PixelFormat.Format24bppRgb
图像?
演示
在下面的使用示例中,我创建了一个 2x2 大小(4 像素)的 PixelFormat.Format24bppRgb
格式的位图,我用指定的纯色填充位图,然后使用我的函数测试结果。
Dim color As Color = color.FromArgb(255, 117, 228, 26)
Dim bmp As Bitmap =
ImageUtil.CreateSolidcolorBitmap(New Size(2, 2), color, PixelFormat.Format24bppRgb)
Dim pxInfoCol As IEnumerable(Of PixelInfo) = bmp.GetPixelInfo()
For Each pxInfo As PixelInfo In pxInfoCol
Console.WriteLine(String.Format("Position: {0}, Location: {1}, Color: {2}",
pxInfo.Position, pxInfo.Location, pxInfo.Color.ToString))
Next
这是意外的执行结果:
Position: 0, Location: {X=0,Y=0}, Color: Color [A=255, R=117, G=228, B=26]
Position: 1, Location: {X=1,Y=0}, Color: Color [A=255, R=117, G=228, B=26]
Position: 2, Location: {X=0,Y=1}, Color: Color [A=255, R=26, G=0, B=0]
Position: 3, Location: {X=1,Y=1}, Color: Color [A=255, R=26, G=117, B=228]
Position: 4, Location: {X=0,Y=2}, Color: Color [A=255, R=0, G=117, B=228]
有 5 个元素(5 个像素,当图像只有 4 个像素时)并且元素 2、3 和 4 的颜色不相同。
我做错了。
我建议你学习 BMP file format article over at wikipedia, especially the Pixel storage 部分。
The pixel format is defined by the DIB header or Extra bit masks. Each row in the Pixel array is padded to a multiple of 4 bytes in size
因此 2x2 24bppRgb 位图的每一行中的字节如下所示。如您所见,每一行的末尾都有两个"padding bytes"。
B G R B G R P P B G R B G R P P
4x2 24bppRgb 位图中没有填充,因为每行中的字节恰好是 3 个 DWORD。
B G R B G R B G R B G R B G R B G R B G R B G R
在位图中的行之间移动时,您需要考虑 BitmapData.Stride 属性。在 32bpp 中,步幅将始终等于 Width*4,这就是您的代码正常工作的原因,但在 24bpp 中情况并非总是如此。
场景
我正在尝试编写一个将使用以下结构的函数,PixelInfo
,到 return 源图像中每个像素的集合,将存储 Color
、像素位置和 Point
以及相对于图像的坐标位置:
<Serializable>
<StructLayout(LayoutKind.Sequential)>
Public Structure PixelInfo
Public ReadOnly Property Color As Color
Get
Return Me.colorB
End Get
End Property
Private ReadOnly colorB As Color
Public ReadOnly Property Position As Integer
Get
Return Me.positionB
End Get
End Property
Private ReadOnly positionB As Integer
Public ReadOnly Property Location As Point
Get
Return Me.locationB
End Get
End Property
Private ReadOnly locationB As Point
<DebuggerStepThrough>
Public Sub New(ByVal color As Color,
ByVal position As Integer,
ByVal location As Point)
Me.colorB = color
Me.positionB = position
Me.locationB = location
End Sub
End Structure
这是函数:
<DebuggerStepThrough>
<Extension>
Iterator Function GetPixelInfo(ByVal sender As Image) As IEnumerable(Of PixelInfo)
Dim bytesPerPixel As Integer = (Image.GetPixelFormatSize(sender.PixelFormat) \ 8)
If (bytesPerPixel <> 3) AndAlso (bytesPerPixel <> 4) Then
Throw New NotImplementedException(
message:="Only PixelFormats that has 3 or 4 bytes-per-pixel are supported.")
Else
' Lock the bitmap's bits.
Dim bmp As Bitmap = DirectCast(sender, Bitmap)
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As BitmapData = bmp.LockBits(rect, ImageLockMode.ReadWrite,
bmp.PixelFormat)
' Get the address of the first line.
Dim ptr As IntPtr = bmpData.Scan0
' Declare an array to hold the bytes of the bitmap.
Dim numBytes As Integer = (Math.Abs(bmpData.Stride) * rect.Height)
Dim rgbData(numBytes - 1) As Byte
' Copy the RGB values into the array.
Marshal.Copy(ptr, rgbData, 0, numBytes)
' Unlock the bits.
bmp.UnlockBits(bmpData)
' Iterate the pixels.
For i As Integer = 0 To (rgbData.Length - bytesPerPixel) Step bytesPerPixel
Dim color As Color = color.FromArgb(red:=rgbData(i + 2),
green:=rgbData(i + 1),
blue:=rgbData(i))
Dim position As Integer = (i \ bytesPerPixel)
Dim location As Point =
New Point(X:=(position Mod rect.Width),
Y:=(position - (position Mod rect.Width)) \ rect.Width)
Yield New PixelInfo(color, position, location)
Next i
End If
End Function
问题
我已经针对像素格式为 PixelFormat.Format32bppRgb
和 PixelFormat.Format32bppArgb
的图像对其进行了测试,它似乎按预期工作。
我遇到的问题是,如果我使用像素格式为 PixelFormat.Format24bppRgb
的图像,一切都会出错,我得到的 Color
与真实颜色不对应,而且我还得到更多像素比图像有。
问题
我的计算哪里失败了?如何修复函数以使用 PixelFormat.Format24bppRgb
图像?
演示
在下面的使用示例中,我创建了一个 2x2 大小(4 像素)的 PixelFormat.Format24bppRgb
格式的位图,我用指定的纯色填充位图,然后使用我的函数测试结果。
Dim color As Color = color.FromArgb(255, 117, 228, 26)
Dim bmp As Bitmap =
ImageUtil.CreateSolidcolorBitmap(New Size(2, 2), color, PixelFormat.Format24bppRgb)
Dim pxInfoCol As IEnumerable(Of PixelInfo) = bmp.GetPixelInfo()
For Each pxInfo As PixelInfo In pxInfoCol
Console.WriteLine(String.Format("Position: {0}, Location: {1}, Color: {2}",
pxInfo.Position, pxInfo.Location, pxInfo.Color.ToString))
Next
这是意外的执行结果:
Position: 0, Location: {X=0,Y=0}, Color: Color [A=255, R=117, G=228, B=26]
Position: 1, Location: {X=1,Y=0}, Color: Color [A=255, R=117, G=228, B=26]
Position: 2, Location: {X=0,Y=1}, Color: Color [A=255, R=26, G=0, B=0]
Position: 3, Location: {X=1,Y=1}, Color: Color [A=255, R=26, G=117, B=228]
Position: 4, Location: {X=0,Y=2}, Color: Color [A=255, R=0, G=117, B=228]
有 5 个元素(5 个像素,当图像只有 4 个像素时)并且元素 2、3 和 4 的颜色不相同。
我做错了。
我建议你学习 BMP file format article over at wikipedia, especially the Pixel storage 部分。
The pixel format is defined by the DIB header or Extra bit masks. Each row in the Pixel array is padded to a multiple of 4 bytes in size
因此 2x2 24bppRgb 位图的每一行中的字节如下所示。如您所见,每一行的末尾都有两个"padding bytes"。
B G R B G R P P B G R B G R P P
4x2 24bppRgb 位图中没有填充,因为每行中的字节恰好是 3 个 DWORD。
B G R B G R B G R B G R B G R B G R B G R B G R
在位图中的行之间移动时,您需要考虑 BitmapData.Stride 属性。在 32bpp 中,步幅将始终等于 Width*4,这就是您的代码正常工作的原因,但在 24bpp 中情况并非总是如此。