【问题标题】:Resizing a Bitmap manually手动调整位图大小
【发布时间】:2021-10-12 03:10:45
【问题描述】:

我们正在使用每秒最多采集 60 帧的相机,提供位图供我们在代码库中使用。

根据我们的 wpf 应用程序的要求,这些位图是基于缩放因子进行缩放的;到目前为止,在实际显示 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;
        }

给定缩放因子 Image.GetPixelFormatSize() 并将其结果除以 8 会返回每个像素的字节数;但如果继续每1 / scaleFactor * formatSize 字节仅复制formatSize 字节数量,则会导致图像损坏。

我错过了什么?

【问题讨论】:

  • 也许您没有注意到,但是当您的原始位图的PixelFormatFormat24bppRgb 并且您使用例如var resized = new Bitmap(original, 100, 100) 创建一个新位图时,新图像PixelFormat现在是Format32bppArgb。这是为什么? -- 在此处阅读关于 Stride 的重要说明Analyze colors of an Image。 -- 既然你有 WPF 应用程序,为什么要使用 GDI+ 来缩放位图?
  • @Jimi 非常有见地,我实际上认为在这种情况下用零填充是一个问题。关于 GDI+,new Bitmap(original, width, height) 调用 graphics.drawimage 反过来又在外部调用 gdi+。 (referencesource.microsoft.com/#System.Drawing/commonui/System/… | referencesource.microsoft.com/#System.Drawing/commonui/System/…)
  • @OlivierRogier 网上找到的大多数答案只需要参考 GDI+
  • 由于硬件对齐(总是4字节对齐),需要填充。 -- 我在问你为什么在有 WPF 应用程序时使用 GDI+。本平台使用其他工具),GDI+在WinForms中很常见(因为它的Controls可以使用Bitmap对象,WPF不能)。

标签: c# bitmap scaling image-scaling bitmapdata


【解决方案1】:

经过更多研究后,我偶然发现了 OpenCV,它有自己的 .NET 实现和 Emgu.CV,包含更快调整大小的相关方法。

我的ResizeBitmap()-function 明显缩小了:

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 毫秒。

【讨论】:

    猜你喜欢
    • 2012-03-23
    • 2010-11-10
    • 2019-08-01
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 2014-02-17
    相关资源
    最近更新 更多