C# 获取64bpp图像颜色

C# 获取64bpp图像颜色,c#,.net,image-processing,bitmap,C#,.net,Image Processing,Bitmap,我想使用BitmapData访问64bpp图像的颜色信息。我已经成功地为32bpp的图像完成了这项工作,但对于64bpp的图像来说似乎并不容易 static void Main(string[] args) { Bitmap bmp; using (Stream imageStreamSource = new FileStream(bitmapPath, FileMode.Open, FileAccess.Read, FileShare.Read)) //Should I u

我想使用BitmapData访问64bpp图像的颜色信息。我已经成功地为32bpp的图像完成了这项工作,但对于64bpp的图像来说似乎并不容易

static void Main(string[] args)
{
    Bitmap bmp;
    using (Stream imageStreamSource = new FileStream(bitmapPath, FileMode.Open, FileAccess.Read, FileShare.Read))   //Should I use "using" here? or do I need to keep the stream open for as long as I use the Bitmap?
    {
        PngBitmapDecoder decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
        BitmapSource bitmapSource = decoder.Frames[0];
        bmp = _bitmapFromSource(bitmapSource);
    }

    unsafe
    {
        int xIncr = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;      //8
        xIncr /= 2;

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

        ushort* bData1Scan0Ptr = (ushort*)bData1.Scan0.ToPointer();
        ushort* nextBase = bData1Scan0Ptr + bData1.Stride;

        for (int y = 0; y < bData1.Height; ++y)
        {
            for (int x = 0; x < bData1.Width; ++x)
            {
                var red = bData1Scan0Ptr[2];
                var green = bData1Scan0Ptr[1];
                var blue = bData1Scan0Ptr[0];

                bData1Scan0Ptr += xIncr;      //xIncr = 4
            }

            bData1Scan0Ptr = nextBase;
            nextBase += bData1.Stride;
        }
    }
}

//http://stackoverflow.com/questions/7276212/reading-preserving-a-pixelformat-format48bpprgb-png-bitmap-in-net
private static Bitmap _bitmapFromSource(BitmapSource bitmapsource)
{
    Bitmap bitmap;
    using (MemoryStream outStream = new MemoryStream())     //Should I use "using" here?
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapsource));
        enc.Save(outStream);
        bitmap = new System.Drawing.Bitmap(outStream);
    }
    return bitmap;
}
static void Main(字符串[]args)
{
位图bmp;
使用(Stream imageStreamSource=new FileStream(位图路径,FileMode.Open,FileAccess.Read,FileShare.Read))//我应该在这里使用“using”吗?还是需要在使用位图时保持流的打开状态?
{
PngBitmapDecoder decoder=新的PngBitmapDecoder(imageStreamSource,BitmapCreateOptions.PreservePixelFormat,BitmapCacheOption.Default);
BitmapSource BitmapSource=解码器。帧[0];
bmp=_bitmapFromSource(bitmapSource);
}
不安全的
{
int xIncr=Image.GetPixelFormatSize(bmp.PixelFormat)/8;//8
xIncr/=2;
BitmapData bData1=bmp.LockBits(新矩形(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,bmp.PixelFormat);
ushort*bData1Scan0Ptr=(ushort*)bData1.Scan0.ToPointer();
ushort*nextBase=bData1Scan0Ptr+bData1.步幅;
对于(int y=0;y
对于白色图像(255、255、255),我得到0和32作为2个值。。。?我不知道这是否正确,我只是把它们理解错了,或者它们完全错了。(更新后的代码不再适用)

编辑: 经过更新的代码,我得到的255值为8192,128值为1768。这对我来说还是没什么意义


我还想知道在加载图像时是否应该使用“using”语句,因为它说我需要在位图的生命周期内保持流的打开状态。它现在的工作方式似乎很好,或者是因为我将位图复制到在“using”语句之外声明的另一个位图?

通常,64bpp格式的每个RGBA都有16位,因此您应该真正使用
ushort
而不是
byte
。例如:

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

byte* bData1Scan0Ptr = (byte*)bData1.Scan0.ToPointer();
byte* nextBase = bData1Scan0Ptr + bData1.Stride;

for (int y = 0; y < bData1.Height; ++y)
{
    ushort* pRow = (ushort*)bData1Scan0Ptr;

    for (int x = 0; x < bData1.Width; ++x)
    {
        var red = pRow[2];
        var green = pRow[1];
        var blue = pRow[0];

        pRow += 4;
    }

    bData1Scan0Ptr = nextBase;
    nextBase += bData1.Stride;
}
BitmapData bData1=bmp.LockBits(新矩形(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,bmp.PixelFormat);
字节*bData1Scan0Ptr=(字节*)bData1.Scan0.ToPointer();
字节*nextBase=bData1Scan0Ptr+bData1.Stride;
对于(int y=0;y

如果不能解决您的问题,请尝试此修改并对结果进行注释,然后在必要时更新我的答案。

这是一个老问题,但我在开发库的过程中遇到了相同的问题。见TL;DR答案位于“备注”部分底部的“帮助”页的“备注”部分

下面是更详细的答案:从8位到16位颜色通道的转换(实际上是13位,正如您已经注意到的)不是线性的:

  • 24bpp格式以及其他高达32位的格式(以及
    颜色
    结构)表示伽马校正γ=2.2的颜色
  • 而48/64bpp格式表示具有线性伽马的颜色,即无伽马校正(伽马=1.0)
因此,如果要将颜色从8位转换为16位,则需要使用γ~0.45调整8位RGB值。只需使用以下公式:

outputLevel = 8192 * Math.Pow(inputLevel / 255d, 1d / gamma)
这为
inputLevel=128
gamma=0.45提供了1770

这就是24bpp格式的橙色(255128,0)和48bpp格式的橙色(81921768,0)

注:该公式仅适用于RGB通道,不适用于A。Alpha没有“gamma校正”

事实上,Windows有点作弊,正如您可以注意到的,对于低
inputLevel
值:上面的公式给出了输入级别<5的0,尽管尝试使用48/64 bpp格式和低RGB颜色值的
SetPixel
,您可以看到原始RGB组件从不为零,它们总是不同的。如果将图像从32位转换为64位会丢失一些信息,这可能会非常可笑


不过,这可能因实现而异。在Linux上,
libgdiplus
根本不支持48/64位格式。另一方面,ReactOS(一个免费的Windows克隆)在8->16位转换之间使用一个简单的线性转换,使用完整的16位范围,正如您可以从其上看到的那样。

谢谢!我现在得到了不同的结果,但我仍然不确定它们是否正确。。我的图像是橙色(255128,0),我得到的值是:红色:8192,绿色:1768和蓝色:0我知道这是一个老问题,但对于像我这样偶然发现这个问题的人来说,8192是最大值(对于24bpp图像的白色,等于255)。