java - 多页 Tiff 压缩

标签 java tiff

我看到了一些关于多页 tiff 的问题和一些关于压缩的问题,但没有(我看到的)将两者联系起来。 This question和我见过的一样近,让我非常接近,所以我希望如此。我进入了提到的 Oracle 论坛线程(它正在谈论通过压缩将多页 PDF 转换为 TIFF)并且我认为我已经接近完成代码来完成此操作。有人可以帮忙吗?我将删除 try/catches 以尝试将其缩短(基本上他们所做的只是在控制台中输出一条消息并返回 false)。

 public static boolean CompressedTiff(List<BufferedImage> images, File path)
 {
    if (!path.getParentFile().exists())
         path.getParentFile().mkdirs();
    path.createNewFile();
    ImageOutputStream ios;
         ios = ImageIO.createImageOutputStream(path);

    Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("TIFF");
    ImageWriter writer = (ImageWriter)imageWriters.next();
    writer.setOutput(ios);
    TIFFImageWriteParam writeParam = (TIFFImageWriteParam)writer.getDefaultWriteParam();
    writeParam.setCompressionMode(2);
    writeParam.setCompressionType("LZW"); 
    writer.prepareWriteSequence(null);

    for(int i = 0; i < images.size(); i++)
    {
        ImageTypeSpecifier spec = ImageTypeSpecifier.createFromRenderedImage(images.get(i));
        javax.imageio.metadata.IIOMetadata metadata = writer.getDefaultImageMetadata(spec, writeParam);
        IIOImage iioImage = new IIOImage(images.get(i), null, metadata);
        writer.writeToSequence(iioImage, writeParam);
        images.get(i).flush();//modified after release.

        images.get(i).flush();
        writer.endWriteSequence();
        ios.flush();
        writer.dispose();
        ios.close();
    }
    return true;

}

它在 writer.writeToSequence 的下一次传递失败,说我需要调用 prepareWriteSequence。我改成了

 writer.prepareWriteSequence(metadata);
 writer.writeToSequence(iioImage, writeParam);

还删除了较早的 writer.prepareWriteSequence(null);

它似乎在正确地导航文件,但是,输出不是任何类型的可渲染 tif。多页或其他方式。

我安装了 JAI,所以如果可以以某种方式使用它来实现压缩图像,那就太棒了。我正在使用的生成 TIFF 的代码正在使用它,但我还没有看到任何在向页面添加压缩方面有效的方法。

编辑:我添加了一堆 ios.flush(); ios.close();在 catch block 中调用它可以防止不可渲染的 TIFF 问题。但是,除了第一个页面之外,它没有添加任何页面。

最佳答案

如果有帮助,这是我用来修改 TiffImageWriteParam 以设置压缩的代码:

try {
    jWriteParam.setCompressionMode(_compression != TiffCompression.NO_COMPRESSION 
                  ? ImageWriteParam.MODE_EXPLICIT : ImageWriteParam.MODE_DISABLED);

    if (_compression != TiffCompression.NO_COMPRESSION) {
        // this code corrects the compression if, say, the client code asked for
        // CCITT but the actual image pixel format was CMYK or some other non-1 bit
        // image type.
        TiffCompression mode = recastToValidCompression(_compression, pf);
        jWriteParam.setCompressionType(getCompressionType(mode));
        TIFFCompressor compressor = getTiffCompressor(mode, jWriteParam, shouldUsePredictor(pf));
        jWriteParam.setTIFFCompressor(compressor);
        if (_compression == TiffCompression.JPEG_COMPRESSION) {
            // Java supports setting to 1.0 (ie 100), but it will not actually do lossless (maybe)
            if (_jpegQuality == 100 && !jWriteParam.isCompressionLossless())
                continue;
            jWriteParam.setCompressionQuality(toJavaJpegQuality());
        }
    }
}
catch (UnsupportedOperationException e)
{
    // this shouldn't get here, but you should consider what to do if it does.
    // set a default? throw?
}

这里是 getTiffCompressor():

private TIFFCompressor getTiffCompressor (TiffCompression compression, TIFFImageWriteParam writeParam, boolean usePredictor)
{
    int predictor = usePredictor 
            ? BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING 
            : BaselineTIFFTagSet.PREDICTOR_NONE;

    switch (compression) 
    {
    case GROUP_3_FAX_ENCODING:
        return new TIFFT4Compressor();
    case GROUP_4_FAX_ENCODING:
        return new TIFFT6Compressor();
    case JPEG_COMPRESSION:
        return new TIFFJPEGCompressor(writeParam);
    case MACINTOSH_PACKBITS:
        return new TIFFPackBitsCompressor();
    case DEFLATE:
        return new TIFFDeflateCompressor(writeParam, predictor);
    case LZW:
        return new TIFFLZWCompressor(predictor);
    case MODIFIED_HUFFMAN:
        return new TIFFRLECompressor();
    case NO_COMPRESSION:
    case DEFAULT:
    default:
        return null;
    }
}

TiffCompression 是我自己的枚举,它模拟我为 TIFF 文件提供的压缩。最后,这里是 getCompressionType():

private String getCompressionType (TiffCompression compression)
{
    switch (compression)
    {
    case GROUP_3_FAX_ENCODING:
        return "CCITT T.4";
    case GROUP_4_FAX_ENCODING:
        return "CCITT T.6";
    case JPEG_COMPRESSION:
        return "JPEG";
    case MACINTOSH_PACKBITS:
        return "PackBits";
    case DEFLATE:
        return "Deflate";
    case LZW:
        return "LZW";
    case MODIFIED_HUFFMAN:
        return "CCITT RLE";
    case NO_COMPRESSION:
    case DEFAULT:
    default:
        return null;
    }
}

现在,我无法向您展示所有内容,因为我的代码是为编码任意数量的图像而构建的,而您的不是,因此我们的代码结构大不相同。就我而言,我将编码器设置为使用具有更开放架构的序列编写器。我拉入一张图片,触发一个事件以选择性地更改默认压缩,创建编写器和写入参数,设置元数据/图像标签,触发进度事件,然后写入序列。然后我必须进去修补最后写入的 ifd,因为 tiff 编码器将它们写入损坏,因此需要修补它们。

关于java - 多页 Tiff 压缩,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10317353/

相关文章:

python - 将 GeoTIFF 转换为 JPEG 并在 Python 中提取 GeoTIFF header

jpeg - vips 在大图像上失败

java - ImageJ 库无法打开 tiff 图像

java - Android 从 BaseAdapter 刷新 ListActivity

java - 如何在android中为除屏幕上的元素之外的任何地方设置onClickListener

java - Swing - Qt 信号/槽的替代品

Java - 如何编写非常大(20,000x20,000 像素或更大)的 tif 图像

python - 在 Python 中读取 tiff 标签

java - Hibernate序列化/反序列化对象并保存

java - 从事件调度线程 (EventQueue) 停止另一个线程