15

一直在扯我的头发。

如何将多页/多层 TIFF 图像拆分为多个单独的图像?

此处提供演示图像。

(更喜欢纯 Java(即非本机)解决方案。解决方案是否依赖商业库无关紧要。)

4

7 回答 7

29

您可以使用Java Advanced ImagingJAI通过 ImageReader 拆分多页 TIFF:

ImageInputStream is = ImageIO.createImageInputStream(new File(pathToImage));
if (is == null || is.length() == 0){
  // handle error
}
Iterator<ImageReader> iterator = ImageIO.getImageReaders(is);
if (iterator == null || !iterator.hasNext()) {
  throw new IOException("Image file format not supported by ImageIO: " + pathToImage);
}
// We are just looking for the first reader compatible:
ImageReader reader = (ImageReader) iterator.next();
iterator = null;
reader.setInput(is);

然后可以得到页数:

nbPages = reader.getNumImages(true);

并分别阅读页面:

reader.read(numPage)
于 2013-09-11T10:33:57.870 回答
3

我将上面的这个示例与我发现的一个名为 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);

但后来我发现了另一个名为 apache commonsimaging Maven dependency 的项目:

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-imaging</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

在一行中,您可以获得缓冲的图像:

 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;
}
于 2013-11-26T23:33:35.923 回答
3

一个快速但非 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/
于 2014-03-20T13:41:36.703 回答
3

这就是我使用 ImageIO 的方式:

public List<BufferedImage> extractImages(InputStream fileInput) throws Exception {
    List<BufferedImage> extractedImages = new ArrayList<BufferedImage>();

    try (ImageInputStream iis = ImageIO.createImageInputStream(fileInput)) {

        ImageReader reader = getTiffImageReader();
        reader.setInput(iis);

        int pages = reader.getNumImages(true);
        for (int imageIndex = 0; imageIndex < pages; imageIndex++) {
            BufferedImage bufferedImage = reader.read(imageIndex);
            extractedImages.add(bufferedImage);
        }
    }

    return extractedImages;
}

private ImageReader getTiffImageReader() {
    Iterator<ImageReader> imageReaders = ImageIO.getImageReadersByFormatName("TIFF");
    if (!imageReaders.hasNext()) {
        throw new UnsupportedOperationException("No TIFF Reader found!");
    }
    return imageReaders.next();
}

我从这个博客中获取了部分代码。

于 2017-05-22T12:56:02.323 回答
2

所有提出的解决方案都需要逐页读取多页图像并将页面写回新的 TIFF 图像。除非您想将各个页面保存为不同的图像格式,否则对图像进行解码是没有意义的。鉴于 TIFF 图像的特殊结构,您可以将多页 TIFF 拆分为单个 TIFF 图像而无需解码。

TIFF 调整工具(一个更大的图像相关库的一部分 -我正在使用的“icafe”)是用纯 Java 从头开始​​编写的。它可以删除页面、插入页面、保留某些页面、从多页 TIFF 中拆分页面以及合并将多页 TIFF 图像转换为一张 TIFF 图像,无需对其进行解压缩。

尝试使用 TIFF 调整工具后,我可以将图像分成 3 页:page#0page#1page#2

注意 1:由于某种原因,原始演示图像包含“不正确”的 StripByteCounts 值 1,这不是图像条所需的实际字节。事实证明,图像数据没有被压缩,因此每个图像条的实际字节可以通过其他 TIFF 字段值来计算,例如 RowsPerStrip、SamplesPerPixel、ImageWidth 等。

注意 2:由于在拆分 TIFF 时,上述库不需要对图像进行解码和重新编码。所以速度很快,而且还保留了每个页面的原始编码和附加元数据!

于 2014-04-11T14:36:38.163 回答
0

下面的代码会将多个 tiff 转换为个人的,并生成一个带有 tiff 图像列表的 Excel 工作表。

您需要在 C 盘中创建一个文件夹并将 TIFF 图像放入其中,然后运行此代码。

需要导入以下jar。

1.sun-as-jsr88-dm-4.0-sources

2./sun-jai_codec

3.sun-jai_core

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.IOException;

import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;

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.TIFFEncodeParam;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;

import javax.swing.JOptionPane;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;

public class TIFF_Sepreator {

    File folder = new File("C:/FAX/"); 
    
    
public static void infoBox(String infoMessage, String titleBar)
    {
        JOptionPane.showMessageDialog(null, infoMessage, "InfoBox: " + titleBar, JOptionPane.INFORMATION_MESSAGE);
    }
    
public void  splitting() throws IOException, AWTException 
    {       
        boolean FinalFAXFolder = (new File("C:/Final_FAX")).mkdirs();
        
        File[] listOfFiles = folder.listFiles();
        String dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
                
   try{
        if (listOfFiles.length > 0)
        {
            for(int file=0; file<listOfFiles.length; file++)
            {
            System.out.println(listOfFiles[file]);
            FileSeekableStream ss = new FileSeekableStream(listOfFiles[file]);
            ImageDecoder dec = ImageCodec.createImageDecoder("tiff", ss, null);
            int count = dec.getNumPages();
            TIFFEncodeParam param = new TIFFEncodeParam();
            param.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
            param.setLittleEndian(false); // Intel
            System.out.println("This TIF has " + count + " image(s)");
        
                for (int i = 0; i < count; i++) 
                {
                    RenderedImage page = dec.decodeAsRenderedImage(i);
                    File f = new File("C:\\Final_FAX\\"+dateFormat+ file +i + ".tif");
                    System.out.println("Saving " + f.getCanonicalPath());
                    ParameterBlock pb = new ParameterBlock();
                    pb.addSource(page);
                    pb.add(f.toString());
                    pb.add("tiff");
                    pb.add(param);
                    RenderedOp r = JAI.create("filestore",pb);
                    r.dispose();
                }
            }
            TIFF_Sepreator.infoBox("Find your splitted TIFF images in location 'C:/Final_FAX/' " , "Done :)");
            WriteListOFFilesIntoExcel();
        }  
  
        else
        {
            TIFF_Sepreator.infoBox("No files was found in location 'C:/FAX/' " , "Empty folder");
            System.out.println("No files found");
        }
    } 
   
   catch(Exception e)
   
        {
            TIFF_Sepreator.infoBox("Unabe to run due to this  error: " +e , "Error");
            System.out.println("Error: "+e);
        }
    }

public void WriteListOFFilesIntoExcel(){
    
    File[] listOfFiles = folder.listFiles();
    ArrayList<File> files = new ArrayList<File>(Arrays.asList(folder.listFiles()));
    
    
    try {
    String filename = "C:/Final_FAX/List_Of_Fax_Files.xls" ;
    HSSFWorkbook workbook = new HSSFWorkbook();
    HSSFSheet sheet = workbook.createSheet("FirstSheet"); 
    
    for (int file=0; file<listOfFiles.length; file++) {
        System.out.println(listOfFiles[file]);
           Row r = sheet.createRow(file);
           r.createCell(0).setCellValue(files.get(file).toString());
        }

    FileOutputStream fileOut = new FileOutputStream(filename);
    workbook.write(fileOut);
    fileOut.close();
    System.out.println("Your excel file has been generated!");
    
    }
 catch(Exception ex){
        TIFF_Sepreator.infoBox("Unabe to run due to this  error: " +ex , "Error");
        System.out.println("Error: "+ex);   
 }
}
    public static void main(String[] args) throws IOException, AWTException {
         new TIFF_Sepreator().splitting();

    }
}
 
于 2017-08-09T06:54:24.357 回答
0

它可以将压缩设置为default param.setCompression(32946);.

public static void  doitJAI(String mutitiff) throws IOException {
  FileSeekableStream ss = new FileSeekableStream(mutitiff);
  ImageDecoder dec = ImageCodec.createImageDecoder("tiff", ss, null);
  int count = dec.getNumPages();
  TIFFEncodeParam param = new TIFFEncodeParam();
  param.setCompression(32946);
  param.setLittleEndian(false); // Intel
  System.out.println("This TIF has " + count + " image(s)");
  for (int i = 0; i < count; i++) {
      RenderedImage page = dec.decodeAsRenderedImage(i);
      File f = new File("D:/PSN/SCB/SCAN/bin/Debug/Temps/test/single_" + i + ".tif");
      System.out.println("Saving " + f.getCanonicalPath());
      ParameterBlock pb = new ParameterBlock();
      pb.addSource(page);
      pb.add(f.toString());
      pb.add("tiff");
      pb.add(param);
      RenderedOp r = JAI.create("filestore",pb);
      r.dispose();
  }
}

于 2018-06-15T09:40:23.010 回答