我的 RGB 字节数组到图像转换颠倒了颜色分量顺序

问题描述 投票:0回答:3

我有一个函数可以将普通图像转换为 RGB 字节数组。然后我编写了一个函数来将这个字节数组转回正常图像。不幸的是,颜色分量发生了偏移,即 {R=0, G=94, B=255}(蓝色)变成了 {R=255, G=94, B=0}(橙色),依此类推。 我尝试了几个答案,包括this一个和this一个。我什至尝试更换组件,但这也不起作用。我做错了什么?


编辑:
我还尝试交换颜色组件作为试验,希望它能纠正颜色问题。


这些代码行是一个测试项目。我想稍后在其他地方实现

RGB_Byte_Array_To_Image
函数。

Public Class FormMain
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim filePath As String = "C:\Users\...\Pictures\Testbild mit Gimp erstellen\Test, nicht optimiert, nicht progressiv 2.jpg"
        Dim rgbByteArray As Byte() = RGB_Byte_Array_From_Image(filePath)

        Dim width As Integer
        Dim height As Integer
        Dim pixelformat As Imaging.PixelFormat

        Using image As New Bitmap(filePath)
            width = image.Width
            height = image.Height
            pixelformat = image.PixelFormat
        End Using
        Using normal_Image As System.Drawing.Bitmap = RGB_Byte_Array_To_Image(rgbByteArray, width, height, pixelformat)
            normal_Image.Save("C:\Users\...\Desktop\Test.png", Imaging.ImageFormat.Png)
        End Using
    End Sub

    Private Shared Function RGB_Byte_Array_To_Image(rgbByteArray() As Byte, width As Integer, height As Integer, pixelformat As Imaging.PixelFormat) As Bitmap
        Dim b As New Bitmap(width, height, pixelformat)
        Dim BoundsRect As New Rectangle(0, 0, width, height)
        Dim bmpData As Imaging.BitmapData = b.LockBits(BoundsRect, Imaging.ImageLockMode.WriteOnly, pixelformat)

        Dim bytesPerPixel As Integer = Image.GetPixelFormatSize(pixelformat) \ 8
        Dim stride As Integer = bmpData.Stride

        Dim scan0 As IntPtr = bmpData.Scan0

        For y As Integer = 0 To height - 1
            Dim rowOffset As Integer = y * stride
            Dim sourceOffset As Integer = y * width * bytesPerPixel

            For x As Integer = 0 To width - 1
                Dim pixelOffset As Integer = x * bytesPerPixel

                ' Reverse the order of color components (BGR instead of RGB)
                Dim blue As Byte = rgbByteArray(sourceOffset + pixelOffset)
                Dim green As Byte = rgbByteArray(sourceOffset + pixelOffset + 1)
                Dim red As Byte = rgbByteArray(sourceOffset + pixelOffset + 2)

                Runtime.InteropServices.Marshal.WriteByte(scan0, rowOffset + pixelOffset, blue)
                Runtime.InteropServices.Marshal.WriteByte(scan0, rowOffset + pixelOffset + 1, green)
                Runtime.InteropServices.Marshal.WriteByte(scan0, rowOffset + pixelOffset + 2, red)
            Next
        Next

        b.UnlockBits(bmpData)
        Return b
    End Function


    Private Function RGB_Byte_Array_From_Image(filePath As String) As Byte()
        Dim imageBytes As Byte()

        Using image As New Bitmap(filePath)
            imageBytes = (New Byte(image.Width * image.Height * 3 - 1) {})

            For y As Integer = 0 To image.Height - 1
                For x As Integer = 0 To image.Width - 1
                    Dim pixelColor As Color = image.GetPixel(x, y)
                    Dim pixelIndex As Integer = (y * image.Width + x) * 3

                    imageBytes(pixelIndex) = pixelColor.R
                    imageBytes(pixelIndex + 1) = pixelColor.G
                    imageBytes(pixelIndex + 2) = pixelColor.B
                Next
            Next
        End Using

        Return imageBytes
    End Function
End Class
vb.net
3个回答
1
投票

如果您尝试将图像保存为字节数组...

    'image to bytes
    Dim img As Image = Image.FromFile(pathOfImg)
    Dim ms As New IO.MemoryStream
    img.Save(ms, Drawing.Imaging.ImageFormat.Jpeg)
    Dim imageData() As Byte = ms.GetBuffer()



    'image from bytes
    Using RDms As New IO.MemoryStream(imageData, 0, imageData.Length)
        RDms.Write(imageData, 0, imageData.Length)
        PictureBox1.Image = Image.FromStream(ms, True)
    End Using

1
投票

在我的解决方案中,我现在有一个字节数组,它表示 BGR(蓝-绿-红)顺序的颜色分量。这很重要,因为

LockBits
方法期望在使用某些像素格式时按 BGR 顺序排列像素数据。

Private Shared Function BGR_Byte_Array_To_Image(rgbByteArray As Byte(),
                                                width As Integer,
                                                height As Integer,
                                                pixelformat As Imaging.PixelFormat) As Bitmap
    ' ‘LockBits’ expects the color components to be in the order BGR
    Dim b As New Bitmap(width, height, pixelformat)
    Dim bitmapData As Imaging.BitmapData = b.LockBits(New Rectangle(0, 0, width, height),
                                                      Imaging.ImageLockMode.ReadOnly,
                                                      pixelformat)
    Dim bytes_per_Pixel As Integer = Image.GetPixelFormatSize(b.PixelFormat) \ 8
    Dim stride As Integer = CInt(Math.Floor(
                                            (bytes_per_Pixel * 8.0 * width + 31.0) / 32.0
                                            ) * 4.0
                                 )

    Runtime.InteropServices.Marshal.Copy(rgbByteArray, 0, bitmapData.Scan0, stride * height)

    b.UnlockBits(bitmapData)
    Return b
End Function

这样称呼吧

Using normal_Image As System.Drawing.Bitmap = BGR_Byte_Array_To_Image(rgbByteArray, width, height, pixelformat)
    normal_Image.Save("C:\Users\...\Desktop\Test.png", Imaging.ImageFormat.Png)
End Using

0
投票

@Daniel 提供的解决方案似乎有一个错误。

根据输入图像的宽度,位图的每个扫描线需要填充最多 3 个额外字节。 提供的代码仅在 (bytes_per_Pixel * width) Mod 4 = 0

时才起作用

解决办法是逐行复制bitmapData:

Private Shared Function BGR_Byte_Array_To_Image(bgrByteArray As Byte(),
                                            width As Integer,
                                            height As Integer,
                                            pixelformat As Imaging.PixelFormat) As Bitmap
    ' ‘LockBits’ expects the color components to be in the order BGR
    Dim b As New Bitmap(width, height, pixelformat)
    Dim bitmapData As Imaging.BitmapData = b.LockBits(New Rectangle(0, 0, width, height),
                                                  Imaging.ImageLockMode.ReadOnly,
                                                  pixelformat)

    Dim array_bytes_per_row As Integer = width * Image.GetPixelFormatSize(b.PixelFormat) \ 8
    Dim ptr As IntPtr = bitmapData.Scan0
    For i As Integer = 0 To height - 1
        Runtime.InteropServices.Marshal.Copy(bgrByteArray, i * array_bytes_per_row, ptr, array_bytes_per_row)
        ptr += bitmapData.Stride
    Next

    b.UnlockBits(bitmapData)
    Return b
End Function
© www.soinside.com 2019 - 2024. All rights reserved.