【问题标题】:Slanted bitmap, stride calculation for RGB565 C#倾斜位图,RGB565 C#的步幅计算
【发布时间】:2016-08-19 19:18:10
【问题描述】:

我生成的一些图像是倾斜的,有些则不是。

预期结果:(529x22)

实际结果:(529x22)

不要介意不同的图像尺寸,这些是屏幕截图。它们都是 529x22。

我正在使用的代码,我刚刚从 SO 的一个问题的答案中得到这个。

// some other method
byte[] pixels = new byte[size - 16];
Array.Copy(this.data, offset, pixels, 0, pixels.Length);
this.ByteToImage(w, h, pixels);

// builds the pixels to a image
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
    var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);

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

    // bytes => not using this because it gives error
    // eg. pixel.Length = 16032, bytes = 16064
    int bytes = bmpData.Stride * bmp.Height;

    Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
    bmp.UnlockBits(bmpData);

    return bmp;
}

我很困惑,因为有些工作正常,而不是倾斜。但其他人是倾斜的。我错过了什么?

更新

如 cmets 和答案中所述,问题在于我如何计算步幅。我仍然对如何做到这一点感到困惑,但我尝试了这个:

public static void RemovePadding(this Bitmap bitmap)
{
    int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;

    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
    var pixels = new byte[bitmapData.Width * bitmapData.Height * bytesPerPixel];

    for (int row = 0; row < bitmapData.Height; row++)
    {
        var dataBeginPointer = IntPtr.Add(bitmapData.Scan0, row * bitmapData.Stride);
        Marshal.Copy(dataBeginPointer, pixels, row * bitmapData.Width * bytesPerPixel, bitmapData.Width * bytesPerPixel);
    }

    Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
    bitmap.UnlockBits(bitmapData);
}

但结果是(更倾斜):

【问题讨论】:

  • 您显然有步幅问题。如果您需要帮助解决您的错误,请发布一个带有良好minimal reproducible example 的问题,该问题可以可靠地重现问题,并更具体地说明运行代码时发生的错误(如果有)。您可能只是错误地计算了步幅;某些图像之所以有效,是因为它们的宽度与您的错误计算兼容(Windows 位图格式中的步幅四舍五入到最接近的 32 位......也许它只有 16 位,我不记得肯定是在我的头顶上)。
  • 对不起,我不知道它叫什么。位图新手。我会尝试解决这个问题。
  • @TaW 4 个字节?填充?
  • 是或者更确切地说:扫描线被填充到下一个四字节的倍数,即stride比净像素线长0-3个字节。
  • 您不必计算Stride也不必移除padding,系统会计算Stride并插入padding。您一次性移动未填充数据的方式很可能是错误。越过线的循环似乎更有可能起作用。

标签: c# .net image-processing bitmap stride


【解决方案1】:

这似乎在这里工作:

private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
    var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
    byte bpp = 2;
    var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData = bmp.LockBits(BoundsRect,
                                    ImageLockMode.WriteOnly,
                                    bmp.PixelFormat);
    // copy line by line:
    for (int y = 0; y < h; y++ )
        Marshal.Copy(pixels, y * w * bpp, bmpData.Scan0 + bmpData.Stride * y, w * bpp);
    bmp.UnlockBits(bmpData);

    return bmp;
}

我使用循环将每行数据放置在正确的位置。数据不包括填充,但目标地址必须这样做。

因此,我们需要将数据访问乘以实际的width * bytePerPixel,但目标地址乘以Stride,即扫描线的长度,填充为四个字节的下一个倍数。对于width=300,它是stride=300,对于width=301,它是stride=304..

一步移动所有像素数据只能在没有填充的情况下工作,即当宽度是4的倍数时。

【讨论】:

    【解决方案2】:

    这期望步幅对应于宽度,没有填充。可以有填充。填充会“吃掉”下一行的一些内容,因此看起来会向左移动。

    由于填充打破了行,处理它的唯一真正方法(除了在所有地方使用相同的填充)是逐行复制。您可以使用bmpData.Scan0 + y * bmpData.Stride 计算一行的起始地址。从那里开始复制,每个y

    // 字节 => 不使用它,因为它给出了错误

    是的,因为您的数组没有填充。所以你告诉它要复制比所保存的数组更多的数据。

    【讨论】:

    • 对不起,我不明白你逐行复制的意思。你能举个例子吗?谢谢。
    • @majidarif 然后逐行,但是你想调用它。关键是,填充就在图像的右侧,所以你可以说那里有虚拟像素。应该跳过它们,否则部分图像会在那里丢失。
    • 这是否意味着我需要创建一个新的字节数组并将pixels 复制到那里,跳过填充?
    • 你可以这样做,但我的意思是直接将它逐块复制到位图中。不需要使用临时数组作为额外步骤。
    • 你说的我试过了,还是不行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    相关资源
    最近更新 更多