使用通过 Bitmap.LockBits 函数访问的底层缓冲区在 C# 中处理位图

Process Bitmap in C# using underlying buffer accessed with Bitmap.LockBits function

我需要对任意图像进行分析。我想从最简单的示例开始 - 只需将图像复制到图片框即可。

Bitmap foreImg = new Bitmap("input.jpg");
//output image
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);
unsafe
{
    BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
    BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);

    System.Threading.Tasks.Parallel.For(0, foreImg.Width * foreImg.Height, j =>
    {
        Pixel* pxOne = (Pixel*)((byte*)oneBits.Scan0 + j * sizeof(Pixel));
        Pixel* pxRes = (Pixel*)((byte*)thrBits.Scan0 + j * sizeof(Pixel));
        pxRes->Green = pxOne->Green;
        pxRes->Red = pxOne->Red;
        pxRes->Blue = pxOne->Blue;

     });

    foreImg.UnlockBits(oneBits);
    resImg.UnlockBits(thrBits);
}

在我的程序中,图像失真了 原文:original_image 之后:after_image。我做错了什么?

谢谢!问题是输入图像的 PixelFormat 与我的结构像素不匹配。事实上,我没有添加字母字节,在这种情况下我应该使用 Format24bppRgb.

你的图像复制代码有几个错误,因为假设对于复制的特定图像来说是不正确的。首先假设我们,当您为复制操作创建新的目标图像时,它将具有与源图像完全相同的像素表示,这有时可能是正确的,但在许多情况下不会:

Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);

应该改为:

Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);

下一个可能会或可能不会出错的假设取决于图像是一个隐含的假设,即源图像 PixelFormat 的大小恰好是 3 个字节并且对应于 PixelFormat.Format24bppRgb 格式(或3 个字节,因为我不知道你的像素结构中红色、绿色或蓝色通道的大小是多少,它可能是 PixelFormat.Format48bppRgb 格式),因此字节会根据此从源图像复制到目标图像假设。

要执行精确复制,需要将完全相同数量的字节从源图像复制到目标图像,它不需要使用底层 Pixel 结构,而是可以基于整数复制。最后但同样重要的是,如果目标是复制图像而不是逐个像素地分析图像内容,最快的方法是使用专门的内存复制功能:

System.Buffer.MemoryCopy((void*)oneBits.Scan0, (void*)thrBits.Scan0, byteLength, byteLength);

下面是一个代码清单,其中包含使用 ulong 作为 carrier 复制图像的代码。我添加了 returns Pixel 大小(以字节为单位)的函数,用于计算图像大小(以字节为单位)并执行精确复制。然而,它可以用于 select 匹配 Pixel 结构,然后可用于分析图像数据。例如,如果图像具有 PixelFormat.Format24bppRgb 格式,则可以使用 3 字节大小和 RGB 颜色的 Pixel 结构。对于其他格式,有必要定义其他 Pixel 结构,这些结构将直接复制图像 Pixel 格式。

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace DrawingImagingOperations
{
    class Program
    {
        static void Main(string[] args)
        {
            Bitmap foreImg = new Bitmap(@"..\..\YaHI9.jpg");
            //output image
            Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);
            unsafe
            {
                BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
                BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);
                int pixelSize = GetPixelSize(foreImg.PixelFormat);

                var byteLength = foreImg.Width * foreImg.Height * pixelSize;
                var length = byteLength / sizeof(UInt64);
                var reminder = byteLength % sizeof(UInt64);


                System.Threading.Tasks.Parallel.For(0, length, j =>
                {
                    ulong* pxOne = (ulong*)((byte*)oneBits.Scan0 + j * sizeof(UInt64));
                    ulong* pxRes = (ulong*)((byte*)thrBits.Scan0 + j * sizeof(UInt64));
                    *pxRes = *pxOne;
                });

                if (reminder > 0)
                {
                    byte* pSrc = (byte*)oneBits.Scan0 + (pixelSize * length);
                    byte* pDst = (byte*)thrBits.Scan0 + (pixelSize * length);
                    for (int j = length; j < byteLength; j++)
                        *pDst++ = *pSrc++;

                }

                foreImg.UnlockBits(oneBits);
                resImg.UnlockBits(thrBits);
            }

            resImg.Save(@"..\..\imgCopy.jpg");
        }

        internal static int GetPixelSize(PixelFormat data)
        {
            switch (data)
            {
                case PixelFormat.Format8bppIndexed:
                    return 1;
                case PixelFormat.Format16bppGrayScale:
                case PixelFormat.Format16bppRgb555:
                case PixelFormat.Format16bppRgb565:
                case PixelFormat.Format16bppArgb1555:
                    return 2;
                case PixelFormat.Format24bppRgb:
                    return 3;
                case PixelFormat.Canonical:
                case PixelFormat.Format32bppArgb:
                case PixelFormat.Format32bppPArgb:
                case PixelFormat.Format32bppRgb:
                    return 4;
                case PixelFormat.Format48bppRgb:
                    return 6;
                case PixelFormat.Format64bppArgb:
                case PixelFormat.Format64bppPArgb:
                    return 8;
            }

            throw new FormatException("Unsupported image format: " + data);
        }
    }
}