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



我需要对任意图像进行分析。我想从最简单的例子开始——只需将图像复制到picturebox。

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_imageAfter:After_image。我做错了什么?

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

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

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

应该改为:

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

根据图像的不同,下一个可能会出错也可能不会出错的假设是一个隐含的假设,即源图像PixelFormat的大小正好是3个字节,并且对应于PixelFormat.Format24bppRgb格式(或者3个字节的倍数,因为我不知道Pixel结构中的红、绿或蓝通道的大小,它可能是PixelFormat.Format48bppRgb格式),因此字节从源图像复制到基于该假设的目的地图像。

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

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

下面是一个代码列表,其中包含使用ulong作为carrier复制图像的代码。我添加了返回Pixel字节大小的函数,用于计算图像字节大小并执行精确复制。然而,它可以用于选择匹配的CCD_ 11结构,该结构可以用于分析图像数据。例如,如果图像具有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);
}
}
}

最新更新