确定Java中图像的DPI

3

我有一个TIFF图像已经在我的应用程序中读取并存储在BufferedImage对象中。如何使用Java高级图像(JAI)API确定图像的水平和垂直DPI?我一直在寻找,但没有找到一个简单直接的方法来完成这个任务。


如果你只有一个 BufferedImage,那么你就没办法了。如果你有文件、流或类似的引用,我建议你研究一下 ImageIO API,特别是 ImageReader.getImageMetadata(),从那里获取 DPI 应该相当简单。 - Harald K
数据以字节数组的形式通过 Web 服务调用传递给我。 我认为我可以回到最初从字节数组中读取它的点,尝试使用 ImageReader。 然后将 DPI 信息传递到我需要它的位置。 我接下来遇到的障碍似乎是当我尝试执行 ImageIO.getImageReadersByFormatName("tiff") 加载读取器时,它会说找不到该类型的读取器。 仍在努力解决。 - Michael
我发现Java图像API非常复杂,而且我对它们的了解很少... - Michael
很难不同意这个复杂性.. :-/ 关于TIFF问题:ImageIO周围有几个TIFF插件。您可以使用jai-imageio,它支持标准和本地格式的元数据。我也想推荐自己的插件,但它还不支持元数据。 :-) - Harald K
你的插件是什么?我不确定我是否会在这个项目中使用它,但将来了解它可能会很有用。 - Michael
你可以在GitHub上找到我的ImageIO插件。目前TIFF的读写还在进行中,但已经接近完成了。 - Harald K
2个回答

5

这里有一个完整的示例,使用标准的ImageIO API和标准的元数据格式提取DPI(实际上是每毫米的像素数)。复杂度,我们来了…… :-P

public class DPITest {
    public static void main(String[] args) throws IOException {
        File input = new File(args[0]);

        ImageInputStream stream = ImageIO.createImageInputStream(input);
        Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);

        if (readers.hasNext()) {
            ImageReader reader = readers.next();
            reader.setInput(stream);

            IIOMetadata metadata = reader.getImageMetadata(0);
            IIOMetadataNode standardTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
            IIOMetadataNode dimension = (IIOMetadataNode) standardTree.getElementsByTagName("Dimension").item(0);
            float horizontalPixelSizeMM = getPixelSizeMM(dimension, "HorizontalPixelSize");
            float verticalPixelSizeMM = getPixelSizeMM(dimension, "VerticalPixelSize");

            // TODO: Convert pixelsPerMM to DPI left as an exercise to the reader.. ;-)  

            System.err.println("horizontalPixelSizeMM: " + horizontalPixelSizeMM);
            System.err.println("verticalPixelSizeMM: " + verticalPixelSizeMM);
        }
        else {
            System.err.printf("Could not read %s\n", input);
        }
    }

    private static float getPixelSizeMM(final IIOMetadataNode dimension, final String elementName) {
        // NOTE: The standard metadata format has defined dimension to pixels per millimeters, not DPI...
        NodeList pixelSizes = dimension.getElementsByTagName(elementName);
        IIOMetadataNode pixelSize = pixelSizes.getLength() > 0 ? (IIOMetadataNode) pixelSizes.item(0) : null;
        return pixelSize != null ? Float.parseFloat(pixelSize.getAttribute("value")) : -1;
    }
}

以下是一些阅读资料:

注意:请勿修改HTML标签。

1
哇,他们绝对不会让JAI易于理解...有时我真的觉得自己有点力不从心(而我并不是Java的新手)。所以,看起来我可以将InputStream传递给ImageIO.createImageInputStream而不是File,这应该适用于我正在做的事情。星期一回到办公室时会尝试这个方法。感谢您提供的信息! - Michael
1
我很好奇是否有一些资源可以帮助我找出如何完成任务,或者真的只能通过“试错”和查阅API文档来找到答案? - Michael
@Michael 这只是ImageIO API,不是JAI特定的(但jai-imageio提供了实现此API的TIFF ImageReader插件),因此尝试在Google上搜索。但不确定是否有“权威指南”存在... InputStream应该可以胜任。我会更新答案并提供一些指针。 - Harald K
1
那不会是一个整数,例如:对于300DPI的0.0846,使用Double.valueOf而不是 - Daniel Hári
1
@DanielHári 很好的发现!在格式中,值被定义为“Float”,所以我更新了代码来使用它(float)。double同样可以工作。 - Harald K
显示剩余5条评论

4

使用Apache Commons Sanselan工具库获取图像信息: http://commons.apache.org/imaging/index.html

final ImageInfo imageInfo = Sanselan.getImageInfo(file);

final int physicalWidthDpi = imageInfo.getPhysicalWidthDpi();
final int physicalHeightDpi = imageInfo.getPhysicalHeightDpi();

1
有没有一种不使用Commons Imaging的方法来完成这个任务?我宁愿不为这一个任务额外引入库。此外,要使用这种方法,我必须将其重新编码为字节数组。如果我在TIFFEncodeParam中未指定DPI设置就这样做,上面的代码会返回两个值的-1。如果我向编码器指定DPI设置,我确实会得到一个值,但显然它将是我指定的DPI,而不是图像的原始值。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接