手动调整位图大小
Resizing a Bitmap manually
我们正在使用每秒采集高达 60 帧的相机,提供位图供我们在代码库中使用。
根据我们的 wpf-app 要求,这些位图是根据缩放因子缩放的;在实际显示 60 fps 时,缩放过程是迄今为止最大的限制因素。我知道 new Bitmap(Bitmap source, int width, int height)
这显然是调整位图大小的最简单方法;
尽管如此,我正在尝试使用 BitmapData 和指针实现“手动”方法。我想出了以下内容:
public static Bitmap /*myMoBetta*/ResizeBitmap(this Bitmap bmp, double scaleFactor)
{
int desiredWidth = (int)(bmp.Width * scaleFactor),
desiredHeight = (int)(bmp.Height * scaleFactor);
var scaled = new Bitmap(desiredWidth, desiredHeight, bmp.PixelFormat);
int formatSize = (int)Math.Ceiling(Image.GetPixelFormatSize(bmp.PixelFormat)/8.0);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
BitmapData scaledData = scaled.LockBits(new Rectangle(0, 0, scaled.Width, scaled.Height), ImageLockMode.WriteOnly, scaled.PixelFormat);
unsafe
{
var srcPtr = (byte*)bmpData.Scan0.ToPointer();
var destPtr = (byte*)scaledData.Scan0.ToPointer();
int scaledDataSize = scaledData.Stride * scaledData.Height;
int nextPixel = (int)(1 / scaleFactor)*formatSize;
Parallel.For(0, scaledDataSize - formatSize,
i =>
{
for (int j = 0; j < formatSize; j++)
{
destPtr[i + j] = srcPtr[i * nextPixel + j];
}
});
}
bmp.UnlockBits(bmpData);
bmp.Dispose();
scaled.UnlockBits(scaledData);
return scaled;
}
给定的缩放因子 < 1。
但是,实际上使用此算法似乎不起作用。每个像素的位在内存中到底是如何排列的?我的猜测是调用 Image.GetPixelFormatSize()
并将其结果除以每个像素的字节数 8 returns;但是继续只复制 formatSize
个字节,每个 1 / scaleFactor * formatSize
字节会导致图像损坏。
我错过了什么?
经过更多研究后,我发现了 OpenCV,它有自己的 .NET 实现 Emgu.CV,其中包含用于更快调整大小的相关方法。
我的ResizeBitmap()
-功能明显缩小了:
public static Bitmap ResizeBitmap(this Bitmap bmp, int width, int height)
{
var desiredSize = new Size(width, height);
var src = new Emgu.CV.Image<Rgb, byte>(bmp);
var dest = new Emgu.CV.Image<Rgb, byte>(desiredSize);
Emgu.CV.CvInvoke.Resize(src, dest, desiredSize);
bmp.Dispose();
src.Dispose();
return dest.ToBitmap();
}
我没有彻底测试性能,但在调试时,此实现将执行时间从 new Bitmap(source, width, height)
的 22 毫秒减少到大约 7 毫秒。
我们正在使用每秒采集高达 60 帧的相机,提供位图供我们在代码库中使用。
根据我们的 wpf-app 要求,这些位图是根据缩放因子缩放的;在实际显示 60 fps 时,缩放过程是迄今为止最大的限制因素。我知道 new Bitmap(Bitmap source, int width, int height)
这显然是调整位图大小的最简单方法;
尽管如此,我正在尝试使用 BitmapData 和指针实现“手动”方法。我想出了以下内容:
public static Bitmap /*myMoBetta*/ResizeBitmap(this Bitmap bmp, double scaleFactor)
{
int desiredWidth = (int)(bmp.Width * scaleFactor),
desiredHeight = (int)(bmp.Height * scaleFactor);
var scaled = new Bitmap(desiredWidth, desiredHeight, bmp.PixelFormat);
int formatSize = (int)Math.Ceiling(Image.GetPixelFormatSize(bmp.PixelFormat)/8.0);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
BitmapData scaledData = scaled.LockBits(new Rectangle(0, 0, scaled.Width, scaled.Height), ImageLockMode.WriteOnly, scaled.PixelFormat);
unsafe
{
var srcPtr = (byte*)bmpData.Scan0.ToPointer();
var destPtr = (byte*)scaledData.Scan0.ToPointer();
int scaledDataSize = scaledData.Stride * scaledData.Height;
int nextPixel = (int)(1 / scaleFactor)*formatSize;
Parallel.For(0, scaledDataSize - formatSize,
i =>
{
for (int j = 0; j < formatSize; j++)
{
destPtr[i + j] = srcPtr[i * nextPixel + j];
}
});
}
bmp.UnlockBits(bmpData);
bmp.Dispose();
scaled.UnlockBits(scaledData);
return scaled;
}
给定的缩放因子 < 1。
但是,实际上使用此算法似乎不起作用。每个像素的位在内存中到底是如何排列的?我的猜测是调用 Image.GetPixelFormatSize()
并将其结果除以每个像素的字节数 8 returns;但是继续只复制 formatSize
个字节,每个 1 / scaleFactor * formatSize
字节会导致图像损坏。
我错过了什么?
经过更多研究后,我发现了 OpenCV,它有自己的 .NET 实现 Emgu.CV,其中包含用于更快调整大小的相关方法。
我的ResizeBitmap()
-功能明显缩小了:
public static Bitmap ResizeBitmap(this Bitmap bmp, int width, int height)
{
var desiredSize = new Size(width, height);
var src = new Emgu.CV.Image<Rgb, byte>(bmp);
var dest = new Emgu.CV.Image<Rgb, byte>(desiredSize);
Emgu.CV.CvInvoke.Resize(src, dest, desiredSize);
bmp.Dispose();
src.Dispose();
return dest.ToBitmap();
}
我没有彻底测试性能,但在调试时,此实现将执行时间从 new Bitmap(source, width, height)
的 22 毫秒减少到大约 7 毫秒。