我想用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”语句之外声明的另一个位图?
答案 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位,您已经注意到了)的转换不是线性的:
Color
结构)表示伽玛校正值为γ= 2.2的颜色。因此,如果要将颜色从8位转换为16位,则需要将γ〜0.45调整为8位RGB值。只需使用公式:
outputLevel = 8192 * Math.Pow(inputLevel / 255d, 1d / gamma)
为inputLevel = 128
和gamma = 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所示。