对于图像中的每个像素,获取相对于图像的颜色,像素位置和坐标

时间:2015-11-08 07:47:45

标签: .net vb.net colors bitmap gdi+

方案

我尝试编写一个函数,使用下面的结构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.Format32bppRgbPixelFormat.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上的颜色不同。

我做错了什么。

2 个答案:

答案 0 :(得分:2)

我建议您在维基百科上学习BMP file format文章,尤其是Pixel storage部分。

  

像素格式由DIB标头或额外位掩码定义。像素阵列中的每一行填充为4字节的倍数

因此,2x2 24bppRgb位图的每一行中的字节将如下所示。如您所见,每行最后都有两个“填充字节”。

2x2 24bppRgb

B G R B G R P P B G R B G R P P

4x2 24bppRgb位图中没有填充,因为每行中的字节正好是3个DWORD。

4x2 24bppRgb

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中并非总是如此。