获得64bpp图像颜色

时间:2013-12-26 01:36:55

标签: 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 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;
}

对于白色图像(255,255,255),我得到0和32作为2个值...?我不知道这是否正确,我只是将它们解释错误或者它们是完全错误的。 (不再是真实的更新代码)

修改 使用更新的代码,我得到8192表示255值,1768表示128值。这对我来说仍然没有多大意义......

我还想知道在加载图片时是否应该使用“using”语句,因为http://msdn.microsoft.com/en-us/library/z7ha67kw说我需要在位图的生命周期内保持流打开。它似乎以现在的方式工作正常,或者是因为我将位图复制到在“using”语句之外声明的另一个位图?

2 个答案:

答案 0 :(得分:2)

通常,每个RGBA的64bpp格式将有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;
}

如果不能解决您的问题,也许可以尝试修改并对结果进行评论,然后我会在必要时更新我的​​答案。

答案 1 :(得分:0)

这是一个古老的问题,但是在开发库时遇到了同样的问题。请参阅备注部分底部this帮助页面的注释中的TL; DR答案。

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

  • 24bpp格式以及其他高达32位的格式(以及Color结构)表示伽玛校正值为γ= 2.2的颜色。
  • 而48 / 64bpp格式表示具有线性伽玛的颜色,即。没有伽玛校正(γ= 1.0)

因此,如果要将颜色从8位转换为16位,则需要将γ〜0.45调整为8位RGB值。只需使用公式:

outputLevel = 8192 * Math.Pow(inputLevel / 255d, 1d / gamma)

inputLevel = 128gamma = 0.45提供1770

橙色就是24bpp中的(255,128,0)和48bpp像素格式中的(8192,1768,0)。

注意:仅对RGB通道使用该公式,而对A则不使用。Alpha没有“伽马校正”。

实际上,Windows有点作弊,您可能会注意到inputLevel值较低:上面的公式为输入水平<5提供了0,尽管尝试使用48/64 bpp格式和低RGB的SetPixel颜色值,您会看到原始RGB分量永远不会为零,并且总是不同。如果将图像从32位转换为64位会丢失一些信息,可能会很荒谬...

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