如何在 .net x64 中保存/加载 16 位图像?
之前,当我使用 win32 时,我使用 FreeImage 来加载和保存位深度大于的位图8 位。这就是我处理的每张图像,因为我正在做医学成像,在任何人说什么之前,是的,我和我的客户已经花了很多钱购买具有 11 或 12 位动态范围的高亮度、高对比度显示器。事实上,如果您好奇,ACR 对运行乳房 X 光检查的要求包括具有至少 10 位动态范围的监视器。
我只是为了内存开销而切换到 x64,并将所有开发工作转移到一个平台和编译模式上。我不想回到 win32,而且我的客户就在我身边(并且确实迫使我做出改变)。 FreeImage 无法在 64 位 Windows 上编译;它的代码中有一个编译器无法处理的 _asm 指令。
我想尝试一下 Microsoft 类中的本机 .NET 支持。长话短说:它们不起作用,并且失败并显示非常有限的错误消息。我怀疑这是因为 Microsoft 仍然不支持 Format16bppGrayScale 类。
也许我的代码有问题。这是我的编写代码:
Bitmap theBitmap = new Bitmap(inImage.XSize, inImage.YSize, PixelFormat.Format16bppGrayScale);
//have to go with lockbits
Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
PixelFormat.Format16bppGrayScale);
IntPtr ptr = bmpData.Scan0;
int theByteSize = theBitmap.Width * theBitmap.Height *2;
byte[] theByteBuffer = new byte[theByteSize];
System.Buffer.BlockCopy(inImage.Data, 0, theByteBuffer, 0, theByteSize);
System.Runtime.InteropServices.Marshal.Copy(theByteBuffer, 0, ptr, theByteSize);
theBitmap.UnlockBits(bmpData);
theBitmap.Save(inDirectory + "\\" + inName);
theBitmap.Dispose();
此代码使程序崩溃,
An unhandled exception of type
'System.Runtime.InteropServices.ExternalException' occurred in
System.Drawing.dll
Additional information: A generic error occurred in GDI+.
有趣,特别是因为我从来不想像这样将此图像绘制到屏幕上(尽管这会很好!),而只是想使用保存/加载功能。图像确实被写入磁盘(即使程序崩溃),并且以下读取代码也会使程序崩溃:
Bitmap theBitmap = new Bitmap(theCompleteName, false);
ushort[] theData = new ushort[theBitmap.Width * theBitmap.Height];
int x, y;
switch (theBitmap.PixelFormat)
{
case PixelFormat.Format16bppGrayScale:
//have to go with lockbits
{
Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
PixelFormat.Format16bppGrayScale);
IntPtr ptr = bmpData.Scan0;//scanline approach left over from FreeImage
for (y = 0; y < theBitmap.Height; ++y){
byte[] scanline = new byte[theBitmap.Width*2];
System.Runtime.InteropServices.Marshal.Copy(ptr, scanline, y * theBitmap.Width * 2, theBitmap.Width * 2);
System.Buffer.BlockCopy(scanline, 0, theData, y * theBitmap.Width * 2, theBitmap.Width * 2);
}
theBitmap.UnlockBits(bmpData);
}
break;
//for colors, just take the red and call it a day
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppArgb://really stupid reading code, always works
for (y = 0; y < theBitmap.Height; ++y) {
for (x = 0; x < theBitmap.Width; ++x) {
theData[y * theBitmap.Width + x] = (byte)(theBitmap.GetPixel(x, y).R);
}
}
break;
}
theNewImage = new ImageContainer(theData, theBitmap.Width, theBitmap.Height, inName, inAssessmentID);
theBitmap.Dispose();//not needed, anymore
此代码使程序崩溃并出现错误:
An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
Additional information: Parameter is not valid.
这些结果告诉我 Microsoft 仍然没有修复该格式的 Format16bppGrayScale 部分PixelFormat 枚举。太可惜了。
那么我可以使用什么来通过 .NET 在 x64 上加载和保存 16 位灰度图像呢?
(编辑:我应该补充一点,虽然我可以保存 DICOM 图像,但我需要对非患者数据进行实验以验证算法是否正确,等等。DICOM 需要一组 UID 和其他必填字段,这些字段是多余的满足我的需要;目前我只需要图像,而不是患者数据)。
Before, when I was using win32, I used FreeImage in order to load and save bitmaps of bit depth greater than 8 bits. That's every image I work with, since I'm doing medical imaging, and before anyone says anything, yes, me and my customers have spent a lot of money on high-brightness, high-contrast monitors with 11 or 12 bits of dynamic range. In fact, if you're curious, requirements by the ACR for running mammography incldue a monitor with at least 10 bits of dynamic range.
I just switched to x64 for the memory overheads and to get all of my development onto one platform and compiling mode. I'd rather not to back to win32, and my customers are right there with me (and really forcing the change). FreeImage does not compile on 64 bit windows; it has a _asm directive in the code that the compiler just can't handle.
I thought I'd try the native .NET support in the Microsoft classes. Long story short: They don't work, and fail with very limited error messages. I suspect it's because Microsoft still doesn't support the Format16bppGrayScale class.
Maybe there's a problem in my code. Here's my code for writing:
Bitmap theBitmap = new Bitmap(inImage.XSize, inImage.YSize, PixelFormat.Format16bppGrayScale);
//have to go with lockbits
Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
PixelFormat.Format16bppGrayScale);
IntPtr ptr = bmpData.Scan0;
int theByteSize = theBitmap.Width * theBitmap.Height *2;
byte[] theByteBuffer = new byte[theByteSize];
System.Buffer.BlockCopy(inImage.Data, 0, theByteBuffer, 0, theByteSize);
System.Runtime.InteropServices.Marshal.Copy(theByteBuffer, 0, ptr, theByteSize);
theBitmap.UnlockBits(bmpData);
theBitmap.Save(inDirectory + "\\" + inName);
theBitmap.Dispose();
This code crashes the program with
An unhandled exception of type
'System.Runtime.InteropServices.ExternalException' occurred in
System.Drawing.dll
Additional information: A generic error occurred in GDI+.
Interesting, especially since I never want to draw this image to the screen like this (although it would be nice!), but just want to use the save/load functionality. The image does get written to disk (even though the program crashes), and the following reading code also crashes the program:
Bitmap theBitmap = new Bitmap(theCompleteName, false);
ushort[] theData = new ushort[theBitmap.Width * theBitmap.Height];
int x, y;
switch (theBitmap.PixelFormat)
{
case PixelFormat.Format16bppGrayScale:
//have to go with lockbits
{
Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
PixelFormat.Format16bppGrayScale);
IntPtr ptr = bmpData.Scan0;//scanline approach left over from FreeImage
for (y = 0; y < theBitmap.Height; ++y){
byte[] scanline = new byte[theBitmap.Width*2];
System.Runtime.InteropServices.Marshal.Copy(ptr, scanline, y * theBitmap.Width * 2, theBitmap.Width * 2);
System.Buffer.BlockCopy(scanline, 0, theData, y * theBitmap.Width * 2, theBitmap.Width * 2);
}
theBitmap.UnlockBits(bmpData);
}
break;
//for colors, just take the red and call it a day
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppArgb://really stupid reading code, always works
for (y = 0; y < theBitmap.Height; ++y) {
for (x = 0; x < theBitmap.Width; ++x) {
theData[y * theBitmap.Width + x] = (byte)(theBitmap.GetPixel(x, y).R);
}
}
break;
}
theNewImage = new ImageContainer(theData, theBitmap.Width, theBitmap.Height, inName, inAssessmentID);
theBitmap.Dispose();//not needed, anymore
This code crashes the program with the error:
An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
Additional information: Parameter is not valid.
These results tell me that Microsoft still hasn't fixed the Format16bppGrayScale portion of the PixelFormat enumeration. That's a shame.
So what can I use to load and save 16 bit grayscale images on x64 with .NET?
(EDIT: I should add that while I can save out DICOM images, I need to run experiments on non-patient data to verify that the algorithms are sound, and so forth. DICOM requires a set of UIDs and other required fields that are overkill for what I need; I just need images, and not patient data, at the moment).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我并没有真正回答你的问题,但之前使用过 Neodnyamic 出售的 ImageDraw 组件,并且肯定会推荐(并且再次使用)。它不是免费的,但非常值得进行小额投资。
I'm not really answering your question but have previously used the ImageDraw component sold by Neodnyamic and would definitely recommend (and use) it again. It is not free but well worth a small investment.
FreeImage可以编译为 x64。按照此处的说明进行操作可以绕过
_asm
指令。页面底部还有一个已编译的 64 位 dll。最新版本 (3.15.1) 已包含此修复程序。我已经选择了源代码发行版来尝试(我很好奇在我自己的项目中使用 FreeImage)并且 x64 平台立即可以正常编译。
FreeImage can be compiled to x64. Following the instructions here you can get around the
_asm
directive . There also is a compiled 64-bit dll at the bottom of the page.The latest version (3.15.1) already contains this fix. I've picked up the source distribution to try (I'm being curious to use FreeImage in my own project) and the x64 platform compiles fine right away.