手动调整位图大小

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