多页Tiff压缩

4
我看到了一些关于多页Tiff和压缩的问题,但没有一个(据我所见)将两者联系起来的问题。这个问题是我看到的最接近的问题,让我非常接近。我进入了提到的Oracle论坛线程(它正在讨论使用压缩的多页PDF转换为TIFF),我认为我已经接近完成此操作的代码了。有人能帮忙吗?我要删除try/catch语句以尝试将其缩短一点(基本上它们只在控制台输出消息并返回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的代码我正在使用这个,但我还没有看到任何有效的添加页面压缩的方法。

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

1个回答

3
如果有帮助的话,这是我用来修改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编码器会将它们写成损坏的形式,所以需要进行修补。


我想说压缩方面是有效的(大约剩余50%的文件大小),但我从未尝试过这种多页的方式(我通常使用JAI)。但每当我用上述方法压缩单个页面并尝试与JAI协调时,文件大小压缩就消失了。阅读相关资料后,似乎JAI由于开发时的专利问题不支持LZW。 - Robert

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