ImageIO.read 正在偶尔读取图像文件
ImageIO.read is sporadically reading an image file
我正在使用以下 Java 代码读取 Dicom 图像,稍后尝试将其转换为 JPEG 文件。当读取发生在行
tempImage = ImageIO.read(dicomFile);
,返回图像的图像类型为 10 或其他类型,例如 0 或 11。这里的问题是读取偶尔发生。有时返回的图像类型是 10,有时不是。
当返回的图像类型为 10 时,转换后的 JPEG 文件的写入成功并且 returns 为真,我得到了我的 JPEG 文件。但是,当返回的图片类型不是10时,写入失败,returns false,不产生任何文件。这是我用来写作的声明:
writerReturn = ImageIO.write(image, "jpeg", new File(tempLocation + studyId + File.separator + seriesUID + File.separator + objectId + thumbnail+ ".jpeg"));
我花了很长时间试图弄清楚为什么会发生这种零星的行为,但无法解决任何问题。你能帮忙吗?
我猜问题是您的输入图像是 16 位,而我确定您的代码只接受 8 位输入。你不能使用所谓的通常的 JPEG 8 位有损格式写出,除非你转换你的 16 位输入。
在我的盒子上,这是我看到的:
$ gdcminfo 1.2.840.113619.2.67.2200970061.29232060605151433.387
MediaStorage is 1.2.840.10008.5.1.4.1.1.1.1 [Digital X-Ray Image Storage - For Presentation]
TransferSyntax is 1.2.840.10008.1.2.4.90 [JPEG 2000 Image Compression (Lossless Only)]
NumberOfDimensions: 2
Dimensions: (1887,1859,1)
SamplesPerPixel :1
BitsAllocated :16
BitsStored :14
HighBit :13
PixelRepresentation:0
ScalarType found :UINT16
PhotometricInterpretation: MONOCHROME2
PlanarConfiguration: 0
TransferSyntax: 1.2.840.10008.1.2.4.90
Group 0x6000
Rows 1859
Columns 1887
NumberOfFrames 0
Description
Type G
Origin[2] 1,1
FrameOrigin 0
BitsAllocated 1
BitPosition 0
Origin: (0,0,0)
Spacing: (0.187429,0.187429,1)
DirectionCosines: (1,0,0,0,1,0)
Rescale Intercept/Slope: (0,1)
Orientation Label: AXIAL
因此,如果您想说服自己,您可以提取封装的 JPEG 2000 字节流:
$ gdcmraw 1.2.840.113619.2.67.2200970061.29232060605151433.387 bug.j2k
$ file bug.j2k
bug.j2k: JPEG 2000 codestream
我能够使用 IrfanView 和 kdu_show 打开生成的 bug.j2k
,但是正如您所见,图像非常暗(只读取了低位)。
根据评论中的额外信息,我们发现该应用程序 运行正在 Glassfish 服务器中运行,并且 安装了两个 ImageIO 插件,两者能够读取 DICOM 图像。问题与读取无关,但有时将解码图像写入JPEG会失败。
上述插件的服务提供商是 org.dcm4cheri.imageio.plugins.DcmImageReaderSpi
和
org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi
,但只有后者 (DicomImageReaderSpi
) 似乎有效。这是因为每个样本 BufferedImage
产生 8 位,这是 JPEGImageWriter
能够写入的(DcmImageReaderSpi
每个样本图像创建 16 位,这是无法写入的作为 JFIF JPEG,因此不受 JPEGImageWriter
) 支持。
由于 ImageIO 插件的(默认)未指定(读取:不可预测)顺序,结果是有时您获得 8 bps 版本,有时获得 16 位版本的图像,最终结果是有时 转换不起作用。
现在,好消息是我们可以 设置 ImageIO 插件的明确顺序,或者我们可以 在 运行 时间取消注册 插件,以获得稳定的可预测结果。这些选项中哪个更好,取决于您的服务器上是否有其他代码依赖于不需要的插件。如果不需要,请注销。
下面的代码显示了以上两个选项:
// Get the global registry
IIORegistry registry = IIORegistry.getDefaultInstance();
// Lookup the known providers
ImageReaderSpi goodProvider = lookupProviderByName(registry, "org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi");
ImageReaderSpi badProvider = lookupProviderByName(registry, "org.dcm4cheri.imageio.plugins.DcmImageReaderSpi");
if (goodProvider != null && badProvider != null) {
// If both are found, EITHER
// order the good provider BEFORE the bad one
registry.setOrdering(ImageReaderSpi.class, goodProvider, badProvider);
// OR
// un-register the bad provider
registry.deregisterServiceProvider(badProvider);
}
// New and improved (shorter) version. :-)
private static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName) {
try {
return (T) registry.getServiceProviderByClass(Class.forName(providerClassName));
}
catch (ClassNotFoundException ignore) {
return null;
}
}
您还应该确保只运行此代码一次,对于基于容器的应用程序,应用程序上下文启动是一个很好的时机。
通过上述解决方案,ImageIO.read(...)
将始终使用好的插件,并且 ImageIO.write(...)
将按预期工作。
我正在使用以下 Java 代码读取 Dicom 图像,稍后尝试将其转换为 JPEG 文件。当读取发生在行
tempImage = ImageIO.read(dicomFile);
,返回图像的图像类型为 10 或其他类型,例如 0 或 11。这里的问题是读取偶尔发生。有时返回的图像类型是 10,有时不是。 当返回的图像类型为 10 时,转换后的 JPEG 文件的写入成功并且 returns 为真,我得到了我的 JPEG 文件。但是,当返回的图片类型不是10时,写入失败,returns false,不产生任何文件。这是我用来写作的声明:
writerReturn = ImageIO.write(image, "jpeg", new File(tempLocation + studyId + File.separator + seriesUID + File.separator + objectId + thumbnail+ ".jpeg"));
我花了很长时间试图弄清楚为什么会发生这种零星的行为,但无法解决任何问题。你能帮忙吗?
我猜问题是您的输入图像是 16 位,而我确定您的代码只接受 8 位输入。你不能使用所谓的通常的 JPEG 8 位有损格式写出,除非你转换你的 16 位输入。
在我的盒子上,这是我看到的:
$ gdcminfo 1.2.840.113619.2.67.2200970061.29232060605151433.387
MediaStorage is 1.2.840.10008.5.1.4.1.1.1.1 [Digital X-Ray Image Storage - For Presentation]
TransferSyntax is 1.2.840.10008.1.2.4.90 [JPEG 2000 Image Compression (Lossless Only)]
NumberOfDimensions: 2
Dimensions: (1887,1859,1)
SamplesPerPixel :1
BitsAllocated :16
BitsStored :14
HighBit :13
PixelRepresentation:0
ScalarType found :UINT16
PhotometricInterpretation: MONOCHROME2
PlanarConfiguration: 0
TransferSyntax: 1.2.840.10008.1.2.4.90
Group 0x6000
Rows 1859
Columns 1887
NumberOfFrames 0
Description
Type G
Origin[2] 1,1
FrameOrigin 0
BitsAllocated 1
BitPosition 0
Origin: (0,0,0)
Spacing: (0.187429,0.187429,1)
DirectionCosines: (1,0,0,0,1,0)
Rescale Intercept/Slope: (0,1)
Orientation Label: AXIAL
因此,如果您想说服自己,您可以提取封装的 JPEG 2000 字节流:
$ gdcmraw 1.2.840.113619.2.67.2200970061.29232060605151433.387 bug.j2k
$ file bug.j2k
bug.j2k: JPEG 2000 codestream
我能够使用 IrfanView 和 kdu_show 打开生成的 bug.j2k
,但是正如您所见,图像非常暗(只读取了低位)。
根据评论中的额外信息,我们发现该应用程序 运行正在 Glassfish 服务器中运行,并且 安装了两个 ImageIO 插件,两者能够读取 DICOM 图像。问题与读取无关,但有时将解码图像写入JPEG会失败。
上述插件的服务提供商是 org.dcm4cheri.imageio.plugins.DcmImageReaderSpi
和
org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi
,但只有后者 (DicomImageReaderSpi
) 似乎有效。这是因为每个样本 BufferedImage
产生 8 位,这是 JPEGImageWriter
能够写入的(DcmImageReaderSpi
每个样本图像创建 16 位,这是无法写入的作为 JFIF JPEG,因此不受 JPEGImageWriter
) 支持。
由于 ImageIO 插件的(默认)未指定(读取:不可预测)顺序,结果是有时您获得 8 bps 版本,有时获得 16 位版本的图像,最终结果是有时 转换不起作用。
现在,好消息是我们可以 设置 ImageIO 插件的明确顺序,或者我们可以 在 运行 时间取消注册 插件,以获得稳定的可预测结果。这些选项中哪个更好,取决于您的服务器上是否有其他代码依赖于不需要的插件。如果不需要,请注销。
下面的代码显示了以上两个选项:
// Get the global registry
IIORegistry registry = IIORegistry.getDefaultInstance();
// Lookup the known providers
ImageReaderSpi goodProvider = lookupProviderByName(registry, "org.dcm4che2.imageioimpl.plugins.dcm.DicomImageReaderSpi");
ImageReaderSpi badProvider = lookupProviderByName(registry, "org.dcm4cheri.imageio.plugins.DcmImageReaderSpi");
if (goodProvider != null && badProvider != null) {
// If both are found, EITHER
// order the good provider BEFORE the bad one
registry.setOrdering(ImageReaderSpi.class, goodProvider, badProvider);
// OR
// un-register the bad provider
registry.deregisterServiceProvider(badProvider);
}
// New and improved (shorter) version. :-)
private static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName) {
try {
return (T) registry.getServiceProviderByClass(Class.forName(providerClassName));
}
catch (ClassNotFoundException ignore) {
return null;
}
}
您还应该确保只运行此代码一次,对于基于容器的应用程序,应用程序上下文启动是一个很好的时机。
通过上述解决方案,ImageIO.read(...)
将始终使用好的插件,并且 ImageIO.write(...)
将按预期工作。