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

标签 .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 图像?


演示

通过下面的使用示例,我使用 PixelFormat.Format24bppRgb 格式创建一个 2x2 大小(4 像素)的位图,用指定的纯色填充位图,然后使用我的函数进行测试结果。

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维基百科上的文章,尤其是 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 位图每行中的字节如下所示。正如您所看到的,每行末尾都有两个“填充字节”。

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

关于.net - 对于图像中的每个像素,获取相对于图像的颜色、像素位置和坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33591834/

相关文章:

c# - Windows Phone 8 开发 - 日志记录和 IOC 设计模式

.net - 如果该文件夹不存在,如何通过创建文件夹将文件从一个目录复制到另一个目录

graphics - 匹配两种颜色的亮度

java - 在 Java 类之间传递数据

jQuery:HEX 到 RGB 计算在浏览器之间不同?

c# - 无法在单元测试中加载文件或程序集“System.Web.Mvc”

.net - Visual Studio Online 是否需要在您的机器上安装 Node.js、.Net Core 或 .Net Framework 才能调试和运行?

c# - C# 中的卷影复制 (VSS) 示例?

vb.net - 使用BackgroundWorker ProgressChanged 事件更新图表?

html - 标签很长时标签重叠文本框 - css