我尝试编写一个函数,使用下面的结构PixelInfo
来返回一个集合,其中包含源图像中每个像素的位置,将存储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上的颜色不同。
我做错了什么。
答案 0 :(得分:2)
我建议您在维基百科上学习BMP file format文章,尤其是Pixel storage部分。
像素格式由DIB标头或额外位掩码定义。像素阵列中的每一行填充为4字节的倍数
因此,2x2 24bppRgb位图的每一行中的字节将如下所示。如您所见,每行最后都有两个“填充字节”。
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
答案 1 :(得分:1)
在位图中的行之间移动时,需要考虑BitmapData.Stride属性。当在32bpp时,Stride将始终等于Width * 4,这就是你的代码工作的原因,但在24bpp中并非总是如此。