使用通过 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);
}
}
}
我需要对任意图像进行分析。我想从最简单的示例开始 - 只需将图像复制到图片框即可。
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);
}
}
}