【问题标题】:Crop image using memcpy fails on 24bpp image使用 memcpy 裁剪图像在 24bpp 图像上失败
【发布时间】:2014-02-14 16:56:19
【问题描述】:

我正在尝试使用 memcpy 裁剪 24bpp 图像,就像我在这里读到的那样:cropping an area from BitmapData with C#。我遇到的问题是它仅在我的 sourceImage 为 32bpp 时才有效。当我的 sourceImage 为 24bpp 时,它给了我一个损坏的图像。

class Program
{
    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static unsafe extern int memcpy(byte* dest, byte* src, long count);

    static void Main(string[] args)
    {
        var image = new Bitmap(@"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\adsadas.png");

        //Creates a 32bpp image - Will work eventhough I treat it as a 24bpp image in the CropBitmap method...
        //Bitmap newBitmap = new Bitmap(image);

        //Creates a 24bpp image - Will produce a corrupt cropped bitmap
        Bitmap newBitmap = (Bitmap)image.Clone();

        var croppedBitmap = CropBitmap(newBitmap, new Rectangle(0, 0, 150, 150));
        croppedBitmap.Save(@"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\PieceOfShit.png", ImageFormat.Png);

        Console.ReadLine();
    }

    static public Bitmap CropBitmap(Bitmap sourceImage, Rectangle rectangle)
    {
        Console.WriteLine("Bits per pixel of sourceImage: {0}", Image.GetPixelFormatSize(sourceImage.PixelFormat));

        var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format24bppRgb);
        var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
            byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
            memcpy(croppedImagePointer, sourceImagePointer, croppedBitmapData.Stride * rectangle.Height);
        }
        sourceImage.UnlockBits(sourceBitmapdata);
        croppedImage.UnlockBits(croppedBitmapData);
        return croppedImage;
    }
}

我很困惑,因为我唯一要更改的是 sourceImage PixelFormat,而不是 CropBitmap 方法中的任何代码。所以我总是使用 24bpp Pixelformat 调用 LockBits,即使 sourceImage 是 32bpp。

我尝试了不同的方法来计算我正在复制的字节数,但一切都或多或少地导致了相同的损坏图像。

感谢任何帮助!

【问题讨论】:

  • 你调试过代码并检查过值吗?注意Stride 的值可以是负数,它的位图倒置存储在内存中。
  • 如果我使用 32bpp sourceImage 或 24bpp sourceImage,步幅是相同的。 (这可能是因为我在锁定位时将 32bpp sourceImage 视为 24bpp 图像?)
  • 您不需要使用memcpy,而是使用Marshal 类。更安全。

标签: c# .net image-processing memcpy


【解决方案1】:

您正试图复制数据,就好像它是一个连续的块一样,但事实并非如此。

图像数据按扫描线排列,但是当您选择图像的一部分时,您不需要每条扫描线的所有数据,您只需要代表您选择的像素的数据。一条扫描线包含您在调用LockBits 时指定的像素数据,还包含该区域之外的像素数据。

Stride 值是从一条扫描线到下一条扫描线的内存地址差异。 Stride 值还可以包括扫描线之间的填充。另请注意,Stride 的值可能是,当图像数据倒置存储在内存中时会发生这种情况。

您想将相关数据从源图像的一行复制到目标图像中的行。由于源数据和目标数据都可能存在间隙,因此您不能将数据复制为单个数据块。

你需要遍历这些行并分别复制每一行,我没有测试过这段代码,但是像这样:

byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
int width = rectange.Width * 3; // for 24 bpp pixel data
for (int y = 0; y < rectangle.Height; y++) {
  memcpy(croppedImagePointer, sourceImagePointer, width);
  sourceImagePointer += sourceBitmapdata.Stride;
  croppedImagePointer += croppedBitmapData.Stride;
}

【讨论】:

  • 非常感谢您的详细解释!我已经测试了你的代码,它运行良好!
猜你喜欢
  • 2012-01-04
  • 1970-01-01
  • 2015-08-10
  • 2017-05-22
  • 2023-03-22
  • 2015-02-02
  • 2023-04-02
相关资源
最近更新 更多