C# 为什么PixelFormat.Format48bppRgb图像的RGB值仅在0到255之间?

C# 为什么PixelFormat.Format48bppRgb图像的RGB值仅在0到255之间?,c#,C#,我有一个简短的脚本,可以创建另一个位图的位图副本,并将PixelFormat从Format32bppArgb(原始输入图像)更改为Format48bppRgb。然后创建一个红色值列表并输出它们 Format32bppArgb每个通道使用8位,而Format48bppRgb16。但是对于这两种格式,我的红色通道输出值都在0和255之间,为什么会这样 Bitmap bitmap = (Bitmap) Bitmap.FromFile(@"minibottle.png");

我有一个简短的脚本,可以创建另一个位图的位图副本,并将
PixelFormat
Format32bppArgb
(原始输入图像)更改为
Format48bppRgb
。然后创建一个
红色
值列表并输出它们

Format32bppArgb
每个通道使用
8位
,而
Format48bppRgb
16
。但是对于这两种格式,我的红色通道输出值都在
0
255
之间,为什么会这样

            Bitmap bitmap = (Bitmap) Bitmap.FromFile(@"minibottle.png");
            Bitmap bitmapCopy = new Bitmap(bitmap.Width, bitmap.Height ,PixelFormat.Format48bppRgb);
            using (Graphics gr = Graphics.FromImage(bitmapCopy)) {
                gr.DrawImage(bitmap, new Rectangle(0, 0, bitmapCopy.Width, bitmapCopy.Height));
            }
            Console.WriteLine("Original PixelFormat: " + bitmap.PixelFormat); // Format32bppArgb
            Console.WriteLine("Copy PixelFormat: " + bitmapCopy.PixelFormat); //Format48bppRgb

            List<int> rPixels = new List<int>();
            for (int row = 0; row < bitmapCopy.Width; row++) {
                for (int column = 0; column < bitmapCopy.Height; column++) {
                    var r = bitmapCopy.GetPixel(row, column).R;
                    rPixels.Add(r);
                }
            }
            bitmap.Dispose();
            bitmapCopy.Dispose();

            var str = "";
            foreach (int px in rPixels) {
                str = str + px + ", ";

            }
            Console.WriteLine("R values of all pixels: " + str);  // 237, 238, 238, 236, 232, 234,...
        }
Bitmap Bitmap=(Bitmap)Bitmap.FromFile(@“miniball.png”);
位图bitmapCopy=新位图(Bitmap.Width、Bitmap.Height、PixelFormat.Format48bppRgb);
使用(Graphics gr=Graphics.FromImage(位图复制)){
gr.DrawImage(位图,新矩形(0,0,bitmapCopy.Width,bitmapCopy.Height));
}
Console.WriteLine(“原始像素格式:“+bitmap.PixelFormat”);//格式化32bpparGB
Console.WriteLine(“复制像素格式:”+bitmapCopy.PixelFormat)//格式化48bpprgb
List rPixels=新列表();
对于(int row=0;row
两种格式的红色通道输出值都在0到255之间,为什么会这样

            Bitmap bitmap = (Bitmap) Bitmap.FromFile(@"minibottle.png");
            Bitmap bitmapCopy = new Bitmap(bitmap.Width, bitmap.Height ,PixelFormat.Format48bppRgb);
            using (Graphics gr = Graphics.FromImage(bitmapCopy)) {
                gr.DrawImage(bitmap, new Rectangle(0, 0, bitmapCopy.Width, bitmapCopy.Height));
            }
            Console.WriteLine("Original PixelFormat: " + bitmap.PixelFormat); // Format32bppArgb
            Console.WriteLine("Copy PixelFormat: " + bitmapCopy.PixelFormat); //Format48bppRgb

            List<int> rPixels = new List<int>();
            for (int row = 0; row < bitmapCopy.Width; row++) {
                for (int column = 0; column < bitmapCopy.Height; column++) {
                    var r = bitmapCopy.GetPixel(row, column).R;
                    rPixels.Add(r);
                }
            }
            bitmap.Dispose();
            bitmapCopy.Dispose();

            var str = "";
            foreach (int px in rPixels) {
                str = str + px + ", ";

            }
            Console.WriteLine("R values of all pixels: " + str);  // 237, 238, 238, 236, 232, 234,...
        }
由于用于获取值的方法,返回的
Color
值不能表示16位颜色通道。
Color
的每个组件属性,例如返回一个
字节
值,该值只能存储8位数据。因此,位图的内部像素值被转换回
Color
使用的每像素8位范围

如果要查看实际的颜色通道值,则需要直接查看像素数据。无论如何,在处理完整位图的数据时,这是一种更好的方法,因为
GetPixel()
方法非常慢。
LockBits()
方法将返回指向位图数据的指针,该指针允许您以更高效的方式直接查看像素数据

以下是一个版本的代码,它将返回48位/像素位图的实际颜色通道值:

使用(位图位图=(位图)Bitmap.FromFile(@“miniball.png”))
使用(位图bitmapCopy=新位图(Bitmap.Width、Bitmap.Height、PixelFormat.Format48bppRgb))
{
使用(Graphics gr=Graphics.FromImage(位图复制))
{
gr.DrawImage(位图,bitmapCopy.Size.ToRect());
}
Console.WriteLine($“原始像素格式:{bitmap.PixelFormat}”);//格式32bppargb
Console.WriteLine($“复制像素格式:{bitmapCopy.PixelFormat}”);//格式48BPPRGB
List rPixels=GetRedValuesWithLockBits(位图复制);
Console.WriteLine($”所有像素的R值:{string.Join(“,”,rPixels)}”);//237、238、238、236、232、234,。。。
}
其中,我已将位图处理移到其自己的方法:

private static List GetRedValuesWithLockBits(位图)
{
列表结果=新列表();
BitmapData BitmapData=bitmap.LockBits(bitmap.Size.ToRect(),ImageLockMode.Read,bitmap.PixelFormat);
尝试
{
不安全的
{
字节*ppixelRow=(字节*)bitmapData.Scan0;
对于(int y=0;y
并包含一个小助手扩展方法:

静态类扩展
{
公共静态矩形删除(此大小)
{
返回新矩形(新点(),大小);
}
}
注:

  • 您必须在项目设置(在“构建”选项卡下)中启用“允许不安全代码”设置
  • 您将注意到返回的值的范围仅为0到8192。人们可能期望看到0到65535之间的值,这是无符号16位值的完整范围。但是,在您使用的.NET代码中,您使用GDI+作为底层图形支持,并且: 像素格式48BPPRGB、像素格式64BPPARGB和像素格式64BPPPARGB每个颜色分量(通道)使用16位。GDI+版本1.0和1.1可以读取每通道16位的图像,但这些图像被转换为每通道8位的格式,用于处理、显示和保存每个16位颜色通道可以保存0到2^13范围内的值。 [我的重点]

换句话说,GDI+在颜色通道中只允许0到8192之间的值。如果你尝试一下,你可以通过实验确定,实际上,它也允许8193到32767之间的值,从技术上讲,这是15位的颜色精度。但任何大于8192的值都被视为8192。大于32767的值将显示为黑色

当GDI+本身生成位图时,如您的示例中所示,它将不会使用大于其记录的最大值8192的值


最后,值得强调的是,这是严格的GDI+限制。Windows提供其他位图