获得64bpp图像颜色

问题描述 投票:1回答:2

我想使用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个值...?我不知道这是否正确,我只是在解释它们错误还是完全错误。 (在更新的代码中不再是真的)

编辑:使用更新的代码,我得到255值的8192和128值的1768。这对我来说仍然没有多大意义...

我还想知道加载图像时是否应该使用“ using”语句,因为http://msdn.microsoft.com/en-us/library/z7ha67kw说我需要在位图的生存期内保持流打开。似乎现在的状态很好,或者是因为我将位图复制到在“ using”语句之外声明的另一个位图?

c# .net image-processing bitmap
2个回答
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;
}

如果无法解决您的问题,也许尝试此修改并用结果发表评论,然后如有必要,我将更新我的答案。


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)

[C0和C0给您1770

这就是橘色在24bpp中为(255,128,0)和在48bpp像素格式中为(8192,1768,0)的方式。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.