【问题标题】:C# LockBitmap - xy-coordinate to Pixel byte conversionC# LockBitmap - xy 坐标到像素字节的转换
【发布时间】:2016-03-22 09:18:08
【问题描述】:

我正在尝试学习用于图像处理的 LockBitmap 类,我发现了下面发布的这段代码。基本上它返回 x-y 坐标的颜色。

当然这种方法只有在我执行source.LockBits()Marshal.Copy()/unsafe context之后才有效。

public Color GetPixel(int x, int y, Bitmap source)
{
    int Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
    int Width = source.Width;
    int Height = source.Height;
    Color clr = Color.Empty;

    // Get color components count
    int cCount = Depth / 8;
    int PixelCounts = Width * Height;
    byte[] Pixels = new byte[cCount * PixelCounts];

    // Get start index of the specified pixel
    int i = ((y * Width) + x) * cCount;

    byte b = Pixels[i];
    byte g = Pixels[i + 1];
    byte r = Pixels[i + 2];
    byte a = Pixels[i + 3]; // a
    clr = Color.FromArgb(a, r, g, b);

    return clr;
}
  1. 什么是cCount,为什么总是Depth / 8
  2. int i = ((y * Width) + x) * cCount,这是从 (x,y) 坐标转换为 Pixels[i] 的固定公式吗?为什么?

【问题讨论】:

  • 这非常简单,并且仅适用于简单的 32 位位图(忽略 Pixels 从一开始就不会获取任何数据)。它不处理调色板、对齐、位对齐(并非所有位图都是“字节对齐”)等。如果你想绕过安全的 GDI+ 方法,你需要做一些研究——快速编码的关键通常会忽略与您无关的边缘情况,但这意味着首先要知道边缘情况是什么,并了解您是否可以安全地忽略它们。
  • @Luaan,感谢您的回复。我真的很想对此做更多的研究。你有什么教程/网站可以推荐吗?我什至不知道谷歌用什么关键字。
  • 问题是,所有这些都是古老的,低级的东西。您必须从许多不同的来源拼凑出大部分知识,而且很可能根本不值得。如今,甚至游戏都在使用 32 位 ARGB(好吧,除了那些转向 HDR 的游戏:P)。例如,如果您可以假设某种像素格式,您可以忽略几乎所有的复杂性 - 确保这一点的一种可能方法是简单地选择最常见的格式(MSPaint 的 32 位是当今 Windows 上最常见的格式),并回退到 GDI+ 以从其他格式转换。

标签: c# bitmap theory lockbits


【解决方案1】:

首先,cCount 是一个像素有多少字节(bytes per pixel),Depth 是一个像素有多少位(bits per pixel)。除以 8 以将位转换为字节。 (虽然Depth / 8效率低下,还是改用(int)Math.Ceiling(Depth / 8d)吧)

为了回答您的第二个问题,像素是逐行排列的。由于每一行都是width 像素,那么每行的大小将是width * cCount 字节。如果你想获得第二行的位置,你可以使用((2-1) * width) * cCount。如果要获取该行中第四个像素的位置,可以使用((2-1) * width + (4-1)) * cCount。因此,要使用坐标(x, y) 获取像素的位置,您可以使用公式(y * width + x) * cCount

【讨论】:

    【解决方案2】:
    1. 并非总是如此,请查看 this. 这可能比您的实现更好。

      可以是 8、16、24、32 等,因为颜色信息需要 8(或 16 等)字节。

    来自发布源的示例代码:

     // Get start index of the specified pixel
            int i = ((y * Width) + x) * cCount;
    
            if (i > Pixels.Length - cCount)
                throw new IndexOutOfRangeException();
    
            if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                byte a = Pixels[i + 3]; // a
                clr = Color.FromArgb(a, r, g, b);
            }
            if (Depth == 24) // For 24 bpp get Red, Green and Blue
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                clr = Color.FromArgb(r, g, b);
            }
            if (Depth == 8)
            // For 8 bpp get color value (Red, Green and Blue values are the same)
            {
                byte c = Pixels[i];
                clr = Color.FromArgb(c, c, c);
    
    1. 因为位图作为数组存储在内存中。

      (y * Width) 是维度,+ x) 是维度中的像素,* cCount 是每像素的步长。 (每个像素都需要内存中的 cCount 个字节。)

    把它想象成你把所有的像素放在一条线上,它从左下角开始,到左上角,从左下角到第二行,从左下角到第二行,一直到右上角。

    【讨论】:

    • 根据站点,它总是Depth / 8。我实际上想知道这是什么color component。据我了解,depth 这里是bpp。那么为什么 cCount 总是bpp / 8
    • @Liren 深度以位为单位,您正在寻址字节。一个字节有 8 位。当然,例如,当深度为 7 或 2 或 14 时,这会失效:)
    • @Liren 是的,dw,这发生在我们所有人身上
    • 我解释了为什么它的((y * Width) + x) * cCount 有帮助吗?
    • 我现在了解* cCount 部分。但是为什么(y * Width) + x?为什么要乘以宽度? y + (x * Height) 也一样吗?
    猜你喜欢
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多