Division d’une image TIFF multipage en images individuelles (Java)

Été en train de me déchirer les cheveux sur celui-ci.

Comment diviser une image TIFF multipage / multicouches en plusieurs images individuelles?

Image de démonstration disponible ici .

(Préférerait une solution purement Java (c’est-à-dire non native). Peu importe si la solution s’appuie sur des bibliothèques commerciales.)

Vous pouvez utiliser la bibliothèque Java Advanced Imaging , JAI , pour fractionner un fichier TIFF multipage en utilisant un ImageReader

ImageInputStream is = ImageIO.createImageInputStream(new File(pathToImage)); if (is == null || is.length() == 0){ // handle error } Iterator 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); 

Ensuite, vous pouvez obtenir le nombre de pages:

 nbPages = reader.getNumImages(true); 

et lisez les pages séparément:

 reader.read(numPage) 

tiffsplit est une solution rapide mais non JAVA. Cela fait partie de la bibliothèque libtiff.

Un exemple de commande permettant de fractionner un fichier tiff dans toutes ses couches serait:

tiffsplit image.tif

La page de manuel dit tout:

 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 (eg 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/ 

J’ai utilisé cet exemple ci-dessus avec un plugin tiff que j’ai trouvé appelé imageio-tiff.

Dépendance Maven:

  com.tomgibara.imageio imageio-tiff 1.0  

J’ai pu obtenir les images mises en mémoire tampon à partir d’une ressource tiff:

  Resource img3 = new ClassPathResource(TIFF4); ImageInputStream is = ImageIO.createImageInputStream(img3.getInputStream()); Iterator 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); 

Mais ensuite, j’ai trouvé un autre projet appelé apache commons, imagerie de la dépendance Maven:

  org.apache.commons commons-imaging 1.0-SNAPSHOT  

Dans une ligne, vous pouvez obtenir les images en mémoire tampon:

  List bufferedImages = Imaging.getAllBufferedImages(img3.getInputStream(), TIFF4); LOGGER.info("No. of pages for tiff file is {} using apache commons imaging", bufferedImages.size()); 

Puis écrivez dans un exemple de fichier:

  final Map params = new HashMap(); // 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 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); } 

En fait, en testant les performances, Apache est beaucoup plus lent …

Ou utilisez une ancienne version de iText, qui est beaucoup plus rapide:

 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.setSsortingctImageSequence(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; } 

Voici comment je l’ai fait avec ImageIO:

 public List extractImages(InputStream fileInput) throws Exception { List extractedImages = new ArrayList(); 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 imageReaders = ImageIO.getImageReadersByFormatName("TIFF"); if (!imageReaders.hasNext()) { throw new UnsupportedOperationException("No TIFF Reader found!"); } return imageReaders.next(); } 

J’ai pris une partie du code de ce blog .

Toutes les solutions proposées nécessitent de lire page par page l’image multipage et d’écrire les pages dans de nouvelles images TIFF. Si vous ne souhaitez pas enregistrer les pages individuelles dans un format d’image différent, il est inutile de décoder l’image. Compte tenu de la structure particulière de l’image TIFF, vous pouvez fractionner un fichier TIFF multipage en une seule image TIFF sans décodage.

L’outil de modification TIFF (partie d’une bibliothèque plus grande liée aux images – “icafe” que j’utilise est écrit à partir de zéro avec Java pur. Il peut supprimer des pages, insérer des pages, conserver certaines pages, scinder des pages à partir de plusieurs pages TIFF et les fusionner images TIFF multipages en une seule image TIFF sans les décompresser.

Après avoir essayé avec l’outil de peaufinage TIFF, je suis en mesure de scinder l’image en 3 pages: page n ° 0 , page n ° 1 et page n ° 2

REMARQUE 1: l’ image de démonstration d’origine, pour une raison quelconque, contient la valeur 1 incorrecte de SsortingpByteCounts, qui n’est pas le nombre d’octets requirejs pour la bande d’images. Il s’avère que les données d’image ne sont pas compressées. Les octets réels de chaque bande d’image peuvent donc être calculés via d’autres valeurs de champ TIFF telles que RowsPerSsortingp, SamplesPerPixel, ImageWidth, etc.

REMARQUE 2: étant donné que vous divisez le fichier TIFF, la bibliothèque mentionnée ci-dessus n’a pas besoin de décoder ni de recoder l’image. C’est donc rapide et conserve également l’encodage d’origine et les métadonnées supplémentaires de chaque page!

Le code ci-dessous convertit le fichier tiff multiple en fichier individuel et produit une feuille Excel avec une liste d’images tiff.

Vous devez créer un dossier nommé “FAX” dans le lecteur C et y placer vos images TIFF, puis exécuter ce code. Vous pouvez trouver les images converties dans “C: \ Final_FAX \”

Besoin d’importer les pots ci-dessous à partir de http://www.java2s.com/Code/JarDownload/sun/

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(Ssortingng infoMessage, Ssortingng titleBar) { JOptionPane.showMessageDialog(null, infoMessage, "InfoBox: " + titleBar, JOptionPane.INFORMATION_MESSAGE); } public void splitting() throws IOException, AWTException { boolean FinalFAXFolder = (new File("C:/Final_FAX")).mkdirs(); // int ListOfFiles = new File("C:/Final_FAX/").listFiles().length; // System.out.println(ListOfFiles); File[] listOfFiles = folder.listFiles(); Ssortingng dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()); try{ if (listOfFiles.length > 0) { for(int file=0; file files = new ArrayList(Arrays.asList(folder.listFiles())); try { Ssortingng filename = "C:/Final_FAX/List_Of_Fax_Files.xls" ; HSSFWorkbook workbook = new HSSFWorkbook(); HSSFSheet sheet = workbook.createSheet("FirstSheet"); for (int file=0; file 

Cela fonctionne pour définir la compression sur default param.setCompression(32946); .

 public static void doitJAI(Ssortingng 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(); } }