英文:
How to deal with 48 or 64bit images in C#
问题
我一直在使用Bitmap类和它的png字节流来处理我的WPF应用程序中的图像,主要是读取/写入文件,从本机摄像头库获取图像并在UI中显示它们。
最近,我有一些新的需求,尝试将其中一些升级以处理64位深度图像(或48位,我不需要Alpha通道)。在几乎每个操作中,位图都会将我的图像转换回32bppArgb。我已经找到了深拷贝的方法,并且目前正在研究Image.FromStream()
,但需要处理这个问题让我产生了疑问:
在C#应用程序中,是否有一种正确处理非32bppArgb图像的方法(包括真正的灰度图像)?或者微软是否忽视了这个需求?我找到了一些相关问题,但大多数是10年前的技巧,所以我期望现在应该有一种合适的方法...
编辑: 如有需要,我将展示一些代码。它运行在.netStandard2.0上,主要用于.net6.0应用程序内;使用System.Drawing.Common版本5.0.2(我认为这并不重要,但如果有必要,我愿意升级)。想象一下nativeImage
是来自某个cameraLib的结构体,所以我们想要一个深拷贝以摆脱共享内存:
使用 Bitmap bitmap = new(nativeImage.Width, nativeImage.Height, nativeImage.stride, PixelFormat.Format64bppArgb, nativeImage.DataPtr);
使用 Bitmap copy = bitmap.DeepCopy(); //自定义函数
使用 MemoryStream stream = new();
copy.Save(stream, ImageFormat.Png);
byte[] byteStream = stream.ToArray(); //保存到文件后显示它是真正的64位
//这些位被拼接在一起以进行测试,通常这个字节数组可能会被传递多次
使用 MemoryStream ms = new(byteStream);
使用 Image image = Image.FromStream(ms, true); //这突然变成了32bppArgb
与:
public static Bitmap DeepCopy(this Image original)
{
// 这只是复制Bitmap(Image)构造函数的代码,但使用原始的像素格式而不是32bppArgb
var result = new Bitmap(original.Width, original.Height, original.PixelFormat);
using (Graphics g = Graphics.FromImage(result))
{
g.Clear(Color.Transparent);
g.DrawImage(original, 0, 0, original.Width, original.Height);
}
return result;
}
英文:
I've been using the Bitmap-class and it's png-ByteStream to deal with images in my WPF-application, mostly with reading/writing files, getting images from native cam libs and showing them in the UI.
Lately I've got some new requirements and tried to upgrade some of that to deal with 64 bit deep images (or 48 bits, I don't need an alpha channel). In pretty much every operation bitmap is converting my image back to 32bppArgb. I've figured out deepcopying and am currently looking into Image.FromStream()
, but needing to deal with this got me wondering:
Is there a way to properly deal with non 32bppArgb-images within a C#-application (including true greyscale)? Or does microsoft just neglect the need for it? I've found some related questions, but mostly with 10 year old hacks, so I'd expect there to be a proper way by now...
Edit: As requested, I'm gonna show some code. It's running on .netStandard2.0 to be used mostly inside .net6.0 apps; with System.Drawing.Common on Version 5.0.2 (I don't think it matters but would be willing to upgrade if that's the case). Imagine nativeImage
to be a struct from some cameraLib, so we want a deepcopy to get away from the shared memory:
using Bitmap bitmap = new(nativeImage.Width, nativeImage.Height, nativeImage.stride, PixelFormat.Format64bppArgb, nativeImage.DataPtr);
using Bitmap copy = bitmap.DeepCopy(); //custom function
using MemoryStream stream = new();
copy.Save(stream, ImageFormat.Png);
byte[] byteStream = stream.ToArray(); //Saving this to file shows it's really 64 bit
//These bits are stitched together for testing, normally this byte-Array might get passed around quite a bit
using MemoryStream ms = new(byteStream);
using Image image = Image.FromStream(ms, true); //this suddenly is 32bppArgb
with:
public static Bitmap DeepCopy(this Image original)
{
// this just copies the code of the Bitmap(Image)-constructor but uses the original pixelFromat instead of 32bppArgb
var result = new Bitmap(original.Width, original.Height, original.PixelFormat);
using (Graphics g = Graphics.FromImage(result))
{
g.Clear(Color.Transparent);
g.DrawImage(original, 0, 0, original.Width, original.Height);
}
return result;
}
答案1
得分: 1
我不认为你会在System.Drawing.Bitmap上取得太大的成功。根据我的经验,它们对高位深度的支持很差。我建议你看看对应的System.Windows.Media类。我知道PngBitmapEncoder/decoder至少支持16位灰度,但我怀疑48/64位也能正常工作。
对于显示,我期望你可能会希望自己进行色调映射,因为我从未见过任何内置方法能够做得特别好。
如果你想要一个用于交换的格式,我建议你创建自己的格式。任何图像基本上都可以用几个属性来表示:
- 宽度
- 高度
- Stride - 一行上的字节数。这必须至少足够大以容纳一行像素,但出于对齐的原因可能更大。
- 像素格式
- 像素数据 - 这可以是表示二进制数据的几乎任何东西。
Byte[]
,一个指针,一个流,Memory<T>
等。数据的总大小应为Height * Stride。
大多数图像处理库应该有接受原始图像数据的方法。你可能需要采取一些措施来使其正常工作,比如使用不安全的代码或进行块复制。你还可能需要一些映射代码来在各种像素格式枚举之间进行转换。
通常,库提供了Bitmap访问以方便使用,但尽快将这些数据转换为某种内部格式。你应该能够做到同样的事情。
英文:
I do not think you will have much success with System.Drawing.Bitmap. In my experience these just have poor support high bit depths. I would instead take a look at the corresponding System.Windows.Media classes. I know PngBitmapEncoder/decoder at least support 16 bit grayscale, but I suspect 48/64 bit works fine as well.
For display I would expect that you would want to do your own tone mapping, since I have never seen any builtin method do a particular good job.
If you want a format for interchange I would suggest creating your own. Any image is essentially represented using just a few properties:
- Width
- Height
- Stride - The number of bytes on a row. This must be at least large enough to fit a row of pixels, but may be larger for alignment reasons.
- PixelFormat
- Pixel Data - This can essentially anything representing binary data.
Byte[]
, a pointer, a stream,Memory<T>
etc. The total size of the data should be Height * Stride.
Most image processing libraries should have methods accepting raw image data. You might need to jump thru some hoops to get it to work, like using unsafe code or doing a blockCopy. You also likely need some mapping code to convert between all the various PixelFormat enums.
Libraries typically provide Bitmap access for convenience, but convert this data to some internal format as soon as possible. You should be able to do the same.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论