gpt4 book ai didi

c# - 对 LockBits、BitmapData 和 PixelFormat.Format48bppRgb 感到困惑

转载 作者:行者123 更新时间:2023-12-04 13:08:05 25 4
gpt4 key购买 nike

考虑以下代码:

public static Bitmap Create3x3Bitmap(PixelFormat pixelFormat)
{
var bmp = new Bitmap(3, 3, pixelFormat);

// I know SetPixel does not perform well, this is strictly for learning purposes

bmp.SetPixel(0, 0, Color.Red);
bmp.SetPixel(1, 0, Color.Lime);
bmp.SetPixel(2, 0, Color.Blue);
bmp.SetPixel(0, 1, Color.White);
bmp.SetPixel(1, 1, Color.Gray);
bmp.SetPixel(2, 1, Color.Black);
bmp.SetPixel(0, 2, Color.Cyan);
bmp.SetPixel(1, 2, Color.Fuchsia);
bmp.SetPixel(2, 2, Color.Yellow);

return bmp;
}

上面的代码应该生成一个 3 x 3 的 Bitmap 实例,如果放大,它应该看起来像这样:

3 x 3 bitmap

我发现我可以使用 Bitmap.LockBits 方法“扫描”位图的像素信息。我成功地使用基于 24 位和 32 位的 PixelFormat 值作为 pixelFormat 参数。

但是,当使用 PixelFormat.Format48bppRgb 时,我还没有理解如何计算像素信息:

public void Test()
{
using (var bmp = Create3x3Bitmap(PixelFormat.Format48bppRgb))
{
var lockRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var data = bmp.LockBits(lockRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
var absStride = Math.Abs(data.Stride); // will be equal to 20
var size = absStride * data.Height; // will be equal to 60

byte[] scanData = new byte[size];

Marshal.Copy(data.Scan0, scanData, 0, size);

// ...
// more stuff here, irrelevant for the actual question
// ...

bmp.UnlockBits(bitmapData);
}
}

如果我使用调试器运行上面的代码并在调用 Marshal.Copy 后立即中断,我可以看到 scanData 字节数组包含 60 个字节。我的理解是,对于三个 RGB channel 中的每一个,都需要两个字节。这意味着每个像素 6 个字节。 y 轴上的每个“行”还有两个额外的未使用字节,在本例中为 3 行。

所以如果我做对了,这里是我应该如何解释数组第一行的内容:

array content

现在让我困惑的是我应该如何解释每个 channel 的字节对并将其转换回原始颜色。有意义的是,对于第一个像素(红色),蓝色和绿色 channel 都会找到一对 0。我不太确定如何将 0 和 32 作为一对应该表示“全红”,但环顾网络我开始明白范围是 0 到 8192 而不是 0 到 65535,due to a limitation of GDI+ .

果然,使用 BitConverter.ToUInt16 为我得到了那对字节的值 8192。所以红色像素的结果是有道理的。

但是,对于在灰色像素的每个 channel 上找到的“232, 6”对(上图中的索引 26 到 31),它也给出 1768。 那是我感到困惑的地方。由于灰色的 8 位 channel 在 0 到 255 范围的中间,我期望是 4095 或 4096,它在 0 到 8192 之间。🤷‍♂️

我对表示 channel 的字节对的理解是否正确?如果是这样,那么我如何获得这些灰色 channel 值?

最佳答案

小傻瓜:你写了这个:

There's also two additional empty pixels for each "row" on the y-axis, which in this case is 3 rows.

每行有两个额外的字节,使步幅为 20 字节,而不是三个 6 字节像素值所需的 18 字节。这满足了 GDI+ 要求,即位图步长是 4 的倍数。

就问题本身而言......

如评论中所述,问答 Why are RGB values of a PixelFormat.Format48bppRgb image only from 0 to 255?应该对你有帮助。事实上,这个陈述至少可以解决您的难题的一半:

GDI+ allows only values from 0 to 8192 in the color channel.

这意味着当您将像素设置为例如红色时,其 24 位 RGB 值为 (255,0,0),通过创建值 (8192, 0,0).这与每个像素 16 位的 (65535,0,0) 不同。

另一部分是像素 channel 存储为两字节、小端、16 位值。当您看到字节 0 (0x00) 后跟字节 32 (0x20) 时,解释方法是将第一个字节存储在 ushort 中 变量,然后将第二个字节左移 8 位并将其与同一个变量中的第一个字节组合。例如:

byte[] redChannelBytes = { 0x00, 0x20 };
ushort redChannel = redChannelBytes[0] | (redChannelBytes[1] << 8);

或者您可以调用 BitConverter.ToUInt16(byte[], int)

byte[] redChannelBytes = { 0x00, 0x20 };
ushort redChannel = BitConverter.ToUInt16(redChannelBytes, 0);

关于c# - 对 LockBits、BitmapData 和 PixelFormat.Format48bppRgb 感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68446921/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com