如何在图像中设置 DPI 信息?

How to set DPI information in an image?

提问人:David Koelle 提问时间:11/27/2008 最后编辑:Mark BiekDavid Koelle 更新时间:10/13/2018 访问量:53198

问:

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

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

知道如何设置 DPI 吗?

Java 映像

评论


答:

33赞 Peter Kofler 1/29/2011 #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);
 }

评论

0赞 Zeveso 5/19/2012
为什么叫网格图像?与普通图像有什么不同吗?
0赞 Peter Kofler 5/23/2012
@Zeveso我只是碰巧从一个保存网格图像的工作应用程序中复制/粘贴此代码,因此 BufferedImage 的名称和方法。该代码适用于每个 BufferedImage。
1赞 Mihir 3/18/2013
@PeterKofler我使用上述代码而没有任何更改。上面的代码按预期创建PNG文件(使用给定的dpi),但是当我将上述代码用于JPEG文件时,它失败了,我需要为JPEG设置任何参数还是此代码仅适用于PNG?
0赞 Peter Kofler 3/18/2013
@Mihir我不知道JPG会遇到任何问题。你能更具体地说一下它是如何失败的吗?
1赞 Mihir 3/18/2013
@PeterKofler当我使用上述代码通过seeting formatName = “jpg”保存400 dpi的jpeg时,它会保存文件,但是当我在irfanview,photoshop,inkspace中打开保存的文件时,所有文件的dpi 72 DPI.如果你需要,我可以在SO上提出一个单独的问题,并带有相关的屏幕截图。
3赞 rj27 3/8/2016 #2

我在我的项目中将此代码用于 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;
}
}

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

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 } });

评论

0赞 Dan Ortega 7/18/2018
伙计,你不知道这提供了多少帮助......非常感谢你分享这个!!
0赞 user2179092 12/3/2018
这非常适合设置 DPI,但如何设置分辨率?
0赞 xyzzz 9/9/2021
@rj27 我尝试了 tiff_maker 中的上述代码为我拥有的 bufferedImage 设置 dpi,然后我尝试将其保存到本地 fs 。之后,我尝试获取图像,并得到一个尺寸非常大的灰色屏幕。我错过了什么吗?writer.write(null, new IIOImage(image, null, null), null);
4赞 Sergei Bubenshchikov 3/27/2017 #3

设置 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 标签的类似方式。

源头上阅读更多内容

评论

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