【问题标题】:Splitting a multipage TIFF image into individual images (Java)将多页 TIFF 图像拆分为单个图像 (Java)
【发布时间】:2013-07-21 07:31:37
【问题描述】:

在这个问题上我一直在撕扯我的头发。

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

提供演示图片here

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

【问题讨论】:

  • 如果您的 TIFF 文件将是一致的并且像示例一样,您可以执行一个不难编写的快速-n-dirty 方法。处理所有 TIFF 的通用例程会更复杂。

标签: java tiff


【解决方案1】:

您可以使用 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)

【讨论】:

  • 实际上,您发布的代码的一个优点是它在编译时甚至不需要 JAI,并且 JAI 可以在运行时替换另一个 TIFF 插件。 :-)
【解决方案2】:

一个快速但非 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/

【讨论】:

    【解决方案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>
                <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;
    }
    

    【讨论】:

      【解决方案4】:

      这就是我使用 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();
      }
      

      我从this blog获取了部分代码。

      【讨论】:

        【解决方案5】:

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

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

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

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

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

        【讨论】:

          【解决方案6】:

          可以将压缩设置为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();
            }
          }

          【讨论】:

            【解决方案7】:

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

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

            需要导入下面的jars。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-12-10
              • 2019-06-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-03-21
              • 1970-01-01
              相关资源
              最近更新 更多