【问题标题】:How can I copy the pixel data from a Bitmap with negative stride?如何从具有负步幅的位图中复制像素数据?
【发布时间】:2011-07-26 18:51:46
【问题描述】:

我正在寻找将位图转换为 8bpp 的最快方法。 我找到了两种方法:

1.

        public static System.Drawing.Image ConvertTo8bpp(Bitmap oldbmp)
    {
        using (var ms = new MemoryStream())
        {
            oldbmp.Save(ms, ImageFormat.Gif);
            ms.Position = 0;
            return System.Drawing.Image.FromStream(ms);
        }
    }

2. http://www.wischik.com/lu/programmer/1bpp.html

但是: 1. 导致质量非常低的结果(坏托盘)

2 给了我一个负步幅的位图,当我尝试锁定位并将数据复制到字节数组时,我得到一个异常:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

        this.stride = bmpData.Stride;
        this.bytesPerPixel = GetBytesPerPixel(bmp.PixelFormat);
        int length = bmpData.Stride * bmp.Height;
        if (this.stride < 0)
            this.data = new byte[-length];
        else
            this.data = new byte[length];
        Marshal.Copy(bmpData.Scan0, data, 0, length);

        //Unlock the bitmap
        bmp.UnlockBits(bmpData);

我怎样才能使 2 取得积极进展?或者如何使用负跨度的锁定位复制数据?

【问题讨论】:

  • 第一个 sn-p 有一个 bug,使用 using 会破坏位图。

标签: c# bitmap stride


【解决方案1】:

这里的问题是Scan0指向第一扫描线的开始,而不是数据第一个字节的开始。在自底向上位图中,第一扫描线是位图数据末尾的Stride 个字节。

当您调用Marshal.CopyScan0 复制数据时,它会尝试从((Height-1)*Stride) 位置开始复制(Height*Stride) 字节。显然,这会跑到杂草中。

如果你只是想复制位图数据,你必须用Scan0 - (Height-1)*Stride计算起始地址。这将从位图数据的开头开始。您可以将该计算出的地址传递给Marshal.Copy

如果要按顺序复制扫描行(即顶部、下一个、下一个、...底部),则必须一次复制一行:从 Scan0 复制 Stride 字节,然后添加Stride(是否定的),复制那行,等等。里克布鲁斯特在那里有正确的答案:https://stackoverflow.com/a/10360753/56778

【讨论】:

  • 好的,它回答了这个问题。但我不想逐行复制......我想避免消极的步伐发生。这就是我创建新问题 (stackoverflow.com/questions/17115555/…) 的原因
  • (我正在使用 C++,而不是 C#,但我猜这仍然有用。)LockBits 本身可以为您复制数据,如果您将 ImageLockModeUserInputBuf 位包含在内的标志。为此,步幅应为负,Scan0 应为 data - (Height - 1) * Stride。 (注意“减号”实际上增加了指针,因为Stride &lt; 0。)
【解决方案2】:

一次复制 1 行,将行的起始指针计算为 ((byte*)scan0 + (y * stride))。无论是正步还是负步,代码都是相同的。

【讨论】:

【解决方案3】:

我不知道为什么FromHbitmap 方法创建的位图有些奇怪,但我知道您可以通过使用Bitmap bmpClone = (Bitmap)bmp.Clone(); 并在 bmpClone 上执行 LockBits 来修复它。

另外,我发现如果你使用bmp.Clone(),在你完成克隆之前你不能Dispose() bmp。

这也有效,让您尽早处理负步幅图像:

        Bitmap bmp = null;
        using (Bitmap bmpT = CopyToBpp(bmpO, 1))
        {
            bmp = new Bitmap(bmpT);
        }

【讨论】:

  • 是的..这是一个可能的解决方案。
  • 根据记忆,我认为这应该会有所帮助。您必须克隆源位图。我曾经使用不安全的代码直接将结果从一个位图写入具有所需像素格式等的新位图。
  • 我又回到了这个问题,不,它现在是一个解决方案,因为它会产生一个 32bpp 的图像!!从 24bpp 到 8bpp 的要点!
【解决方案4】:

来自 BitmapData 上的 C# 文档:步幅是单行像素(扫描线)的宽度,四舍五入到四字节边界。如果步幅为正,则位图是自上而下的。如果步幅为负,则位图为自下而上

【讨论】:

  • 您在 this.stride 中记录的步幅是 bmpData.Stride 属性的绝对值。旧的 Windows API 在将额外信息填充到这样的数据中非常糟糕。
  • 我在这里找到了一个:stackoverflow.com/questions/4816956/…。但它会遍历所有图像高度。谁有更好的解决方案?
  • 为什么解决方案“2”将步幅变为负数??
  • 设置整数的高位只是微软在位图结构中添加“我颠倒了”标志而不增加任何空间的方式。 BMP 非常古老,过去保存的空间非常重要。从那时起,位图代码就不得不担心它。
  • 我不知道如何投票作为答案,但它不能回答我的问题。只有 ctrl+c ctrl+v 的 stride 定义我已经知道了!
【解决方案5】:

我猜你得到的异常是由于

this.data = new byte[-length];

然后尝试将数据复制到一个负大小的字节数组中(我看不出它是如何编译的……)。

【讨论】:

  • 不是这个。再看看他的代码。他试图应用 -length 将负数变为正数,前提是负步幅的结果长度小于 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-08
  • 2019-08-07
  • 2016-03-10
  • 2016-11-22
相关资源
最近更新 更多