【问题标题】:How to set DPI information in an image?如何在图像中设置 DPI 信息?
【发布时间】:2008-11-26 18:49:00
【问题描述】:

我有一个应用程序,我想导出高分辨率(或者更确切地说,高像素密度?)图像进行打印 - 例如,我想要以每英寸 250 点 (DPI) 而非默认值打印的图像,我理解为 72 DPI。

我使用带有 Graphics2D 对象的 BufferedImage 来绘制图像,然后使用 ImageIO.write() 来保存图像。

知道如何设置 DPI 吗?

【问题讨论】:

    标签: java image


    【解决方案1】:

    Kurt 的回答指明了方向,但我仍然花了很长时间才让它运行,所以这里是保存 PNG 时设置 DPI 的代码。要找到合适的作家,还有很多事情要做......

     private BufferedImage gridImage;
     ...
    
     private void saveGridImage(File output) throws IOException {
        output.delete();
    
        final String formatName = "png";
    
        for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
           ImageWriter writer = iw.next();
           ImageWriteParam writeParam = writer.getDefaultWriteParam();
           ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
           IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
           if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
              continue;
           }
    
           setDPI(metadata);
    
           final ImageOutputStream stream = ImageIO.createImageOutputStream(output);
           try {
              writer.setOutput(stream);
              writer.write(metadata, new IIOImage(gridImage, null, metadata), writeParam);
           } finally {
              stream.close();
           }
           break;
        }
     }
    
     private void setDPI(IIOMetadata metadata) throws IIOInvalidTreeException {
    
        // for PMG, it's dots per millimeter
        double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM;
    
        IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
        horiz.setAttribute("value", Double.toString(dotsPerMilli));
    
        IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
        vert.setAttribute("value", Double.toString(dotsPerMilli));
    
        IIOMetadataNode dim = new IIOMetadataNode("Dimension");
        dim.appendChild(horiz);
        dim.appendChild(vert);
    
        IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
        root.appendChild(dim);
    
        metadata.mergeTree("javax_imageio_1.0", root);
     }
    

    【讨论】:

    • 为什么叫网格图?与普通图片​​有什么不同?
    • @Zeveso 我只是碰巧从一个正在保存网格图像的工作应用程序中复制/粘贴了这段代码,因此是 BufferedImage 的名称和方法。该代码适用于每个 BufferedImage。
    • @PeterKofler 我正在使用上面的代码,没有任何更改。上面的代码按预期创建了PNG文件(使用给定的dpi)但是当我将上面的代码用于JPEG文件时它失败了,我需要为JPEG设置任何参数还是这个代码只适用于PNG?
    • @Mihir 我不知道 JPG 会有什么问题。您能否更具体地说明它是如何失败的?
    • @PeterKofler 当我使用上面的代码通过看到 formatName = "jpg" 保存 jpeg 时,它会保存文件,但是当我在 irfanview、photoshop、inkspace 中打开保存的文件时,所有文件的 dpi 72 DPI。如果你需要,我可以用 SO 上的相关屏幕截图提出一个单独的问题。
    【解决方案2】:

    设置 TIFF DPI

    如果您想为 TIFF 设置 dpi,请尝试通过以下步骤进行设置:

    private static IIOMetadata createMetadata(ImageWriter writer, ImageWriteParam writerParams, int resolution) throws
                                                                                                                IIOInvalidTreeException
    {
        // Get default metadata from writer
        ImageTypeSpecifier type = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
        IIOMetadata meta = writer.getDefaultImageMetadata(type, writerParams);
    
        // Convert default metadata to TIFF metadata
        TIFFDirectory dir = TIFFDirectory.createFromMetadata(meta);
    
        // Get {X,Y} resolution tags
        BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
        TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
        TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
    
        // Create {X,Y} resolution fields
        TIFFField fieldXRes = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
        TIFFField fieldYRes = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
    
        // Add {X,Y} resolution fields to TIFFDirectory
        dir.addTIFFField(fieldXRes);
        dir.addTIFFField(fieldYRes);
    
        // Add unit field to TIFFDirectory (change to RESOLUTION_UNIT_CENTIMETER if necessary)
        dir.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT), BaselineTIFFTagSet.RESOLUTION_UNIT_INCH));
    
        // Return TIFF metadata so it can be picked up by the IIOImage
        return dir.getAsMetadata();
    }
    

    此外,您可以通过类似的方式设置任何 TIFF 标签。

    source了解更多信息

    【讨论】:

    • 我认为代码不完整,因为它没有设置分辨率单位,默认为RESOLUTION_UNIT_NONE。在这种情况下,阅读器/客户端应将比率设置为 1/1,请参阅TIFFImageWriter:1182。修复:dir.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT), BaselineTIFFTagSet.RESOLUTION_UNIT_INCH))。免责声明:我不是 TIFF 专家。
    • @dma_k 如果您的建议有效,请随时编辑答案。我也不是 TIFF 专家)
    • 作为附加参考,我提供了一个指向 TiffImage:146 的链接,如果分辨率单位为“无”,则可以看到 X/Y DPI 设置为零,因此 iTextPDF 库将图像解释为具有没有设置 DPI。已知哪些客户端可以正常工作?
    【解决方案3】:

    我在我的项目中将此代码用于 tiff 文件,它运行良好..

    import java.awt.image.BufferedImage;
    import java.awt.image.RenderedImage;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    import javax.imageio.ImageIO;
    import javax.media.jai.NullOpImage;
    import javax.media.jai.OpImage;
    import javax.media.jai.PlanarImage;
    import com.sun.media.jai.codec.FileSeekableStream;
    import com.sun.media.jai.codec.ImageCodec;
    import com.sun.media.jai.codec.ImageDecoder;
    import com.sun.media.jai.codec.ImageEncoder;
    import com.sun.media.jai.codec.SeekableStream;
    import com.sun.media.jai.codec.TIFFEncodeParam;
    import com.sun.media.jai.codec.TIFFField;
    class SetDDPI
     {
    static void tiff_Maker(List<BufferedImage> output, String result) throws   IOException
    {
        TIFFEncodeParam params = new TIFFEncodeParam();
        OutputStream out = new FileOutputStream(result);
        List<BufferedImage> imageList = new ArrayList<BufferedImage>();
        for (int i = 1; i < output.size(); i++)
        {
            imageList.add(output.get(i));
        }
        params.setWriteTiled(true);
        params.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
        params.setExtraImages(imageList.iterator());
        TIFFField[] extras = new TIFFField[2];
        extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
                { (long) 0, (long) 0 } });
        extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
                { (long) 0, (long) 0 } });
        params.setExtraFields(extras);
        ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
        encoder.encode(output.get(0));
        out.close();
    }
    static List<BufferedImage> tiff_Extractor(File tiff) throws IOException
    {
        List<BufferedImage> images = new ArrayList<BufferedImage>();
        SeekableStream ss = new FileSeekableStream(tiff);
        ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);
        int numPages = decoder.getNumPages();
        for (int j = 0; j < numPages; j++)
        {
            PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(j), null, null, OpImage.OP_IO_BOUND);
            images.add(op.getAsBufferedImage());
    
        }
        return images;
    }
    }
    

    这是设置 300 DPI 的 Tiff 图像。您可以根据需要进行更改。

    extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new     
    long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });
    
    extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new     
    long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });
    

    【讨论】:

    • 老兄,你不知道这提供了多少帮助......非常感谢你分享这个!!
    • 设置 DPI 效果很好,但如何设置分辨率?
    • @rj27 我尝试使用 tiff_maker 中的上述代码为我拥有的缓冲图像设置 dpi,然后尝试将其保存到本地 fs writer.write(null, new IIOImage(image, null, null), null);。之后我尝试获取图像,我得到一个尺寸非常大的灰色屏幕。有什么我想念的吗?
    猜你喜欢
    • 2015-02-16
    • 1970-01-01
    • 2014-04-27
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多