これで私の髪を引き裂いています。
マルチページ/マルチレイヤの TIFF 画像を複数の個別の画像に分割するにはどうすればよいですか?
デモ画像はこちらから入手できます。
(純粋な Java (つまり、非ネイティブ) ソリューションを好むでしょう。ソリューションが商用ライブラリに依存しているかどうかは問題ではありません。)
高速だが Java 以外のソリューションはtiffsplit
. libtiff ライブラリの一部です。
tiff ファイルをすべてのレイヤーに分割するコマンドの例は次のとおりです。
tiffsplit image.tif
マンページはそれをすべて言います:
NAME
tiffsplit - split a multi-image TIFF into single-image TIFF files
SYNOPSIS
tiffsplit src.tif [ prefix ]
DESCRIPTION
tiffsplit takes a multi-directory (page) TIFF file and creates one or more single-directory (page) TIFF files
from it. The output files are given names created by concatenating a prefix, a lexically ordered suffix in the
range [aaa-zzz], the suffix .tif (e.g. xaaa.tif, xaab.tif, xzzz.tif). If a prefix is not specified on the
command line, the default prefix of x is used.
OPTIONS
None.
BUGS
Only a select set of ‘‘known tags’’ is copied when splitting.
SEE ALSO
tiffcp(1), tiffinfo(1), libtiff(3TIFF)
Libtiff library home page: http://www.remotesensing.org/libtiff/
上記のサンプルを、見つけた imageio-tiff という tiff プラグインと共に使用しました。
Maven の依存関係:
<dependency>
<groupId>com.tomgibara.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>1.0</version>
</dependency>
バッファリングされた画像を tiff リソースから取得できました。
Resource img3 = new ClassPathResource(TIFF4);
ImageInputStream is = ImageIO.createImageInputStream(img3.getInputStream());
Iterator<ImageReader> iterator = ImageIO.getImageReaders(is);
if (iterator == null || !iterator.hasNext()) {
throw new IOException("Image file format not supported by ImageIO: ");
}
// We are just looking for the first reader compatible:
ImageReader reader = (ImageReader) iterator.next();
iterator = null;
reader.setInput(is);
int nbPages = reader.getNumImages(true);
LOGGER.info("No. of pages for tiff file is {}", nbPages);
BufferedImage image1 = reader.read(0);
BufferedImage image2 = reader.read(1);
BufferedImage image3 = reader.read(2);
しかし、その後、Maven の依存関係をイメージする apache commons という別のプロジェクトを見つけました。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-imaging</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
バッファリングされた画像を 1 行で取得できます。
List<BufferedImage> bufferedImages = Imaging.getAllBufferedImages(img3.getInputStream(), TIFF4);
LOGGER.info("No. of pages for tiff file is {} using apache commons imaging", bufferedImages.size());
次に、サンプル ファイルに書き込みます。
final Map<String, Object> params = new HashMap<String, Object>();
// set optional parameters if you like
params.put(ImagingConstants.PARAM_KEY_COMPRESSION, new Integer(TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_4));
int i = 0;
for (Iterator<BufferedImage> iterator1 = bufferedImages.iterator(); iterator1.hasNext(); i++) {
BufferedImage bufferedImage = iterator1.next();
LOGGER.info("Image type {}", bufferedImage.getType());
File outFile = new File("C:\\tmp" + File.separator + "shane" + i + ".tiff");
Imaging.writeImage(bufferedImage, outFile, ImageFormats.TIFF, params);
}
実際にパフォーマンスをテストすると、Apacheはかなり遅いです...
または、はるかに高速な古いバージョンの iText を使用します。
private ByteArrayOutputStream convertTiffToPdf(InputStream imageStream) throws IOException, DocumentException {
Image image;
ByteArrayOutputStream out = new ByteArrayOutputStream();
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, out);
writer.setStrictImageSequence(true);
document.open();
RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageStream);
int pages = TiffImage.getNumberOfPages(ra);
for (int i = 1; i <= pages; i++) {
image = TiffImage.getTiffImage(ra, i);
image.setAbsolutePosition(0, 0);
image.scaleToFit(PageSize.A4.getWidth(), PageSize.A4.getHeight());
document.setPageSize(PageSize.A4);
document.newPage();
document.add(image);
}
document.close();
out.flush();
return out;
}
提案されたすべてのソリューションでは、複数ページの画像をページごとに読み取り、そのページを新しい TIFF 画像に書き戻す必要があります。個々のページを別の画像形式で保存したくない場合を除き、画像をデコードしても意味がありません。TIFF 画像の特殊な構造により、複数ページの TIFF をデコードせずに単一の TIFF 画像に分割できます。
TIFF 微調整ツール (より大きな画像関連ライブラリの一部 -私が使用している「icafe」は、純粋な Java でゼロから作成されています。ページの削除、ページの挿入、特定のページの保持、複数ページの TIFF からのページの分割、マージが可能です。複数ページの TIFF 画像を解凍せずに 1 つの TIFF 画像に変換します。
TIFF 微調整ツールを試してみたところ、画像をpage#0、page#1、およびpage#2の 3 つのページに分割できました。
注 1:元のデモ イメージには、何らかの理由で「正しくない」StripByteCounts 値 1 が含まれています。これは、イメージ ストリップに必要な実際のバイトではありません。画像データは圧縮されていないことが判明したため、各画像ストリップの実際のバイト数は、RowsPerStrip、SamplesPerPixel、ImageWidth などの他の TIFF フィールド値から計算できます。
注 2: TIFF を分割するため、上記のライブラリは画像をデコードして再エンコードする必要はありません。そのため、高速であり、各ページの元のエンコーディングと追加のメタデータも保持されます!