net.sourceforge.tess4j.util.ImageIOHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tess4j Show documentation
Show all versions of tess4j Show documentation
# Tess4J
## Description:
A Java JNA wrapper for Tesseract OCR API.
Tess4J is released and distributed under the Apache License, v2.0.
## Features:
The library provides optical character recognition (OCR) support for:
TIFF, JPEG, GIF, PNG, and BMP image formats
Multi-page TIFF images
PDF document format
/**
* Copyright @ 2008 Quan Nguyen
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.sourceforge.tess4j.util;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import org.w3c.dom.NodeList;
import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet;
import com.github.jaiimageio.plugins.tiff.TIFFDirectory;
import com.github.jaiimageio.plugins.tiff.TIFFField;
import com.github.jaiimageio.plugins.tiff.TIFFImageWriteParam;
import com.github.jaiimageio.plugins.tiff.TIFFTag;
import com.recognition.software.jdeskew.ImageDeskew;
import com.recognition.software.jdeskew.ImageUtil;
import org.apache.commons.io.FilenameUtils;
public class ImageIOHelper {
static final String OUTPUT_FILE_NAME = "Tesstmp";
public static final String TIFF_EXT = ".tif";
static final String TIFF_FORMAT = "tiff";
public static final String JAI_IMAGE_WRITER_MESSAGE = "Need to install JAI Image I/O package.\nhttps://github.com/jai-imageio/jai-imageio-core";
public static final String JAI_IMAGE_READER_MESSAGE = "Unsupported image format. May need to install JAI Image I/O package.\nhttps://github.com/jai-imageio/jai-imageio-core";
/**
* Creates a list of TIFF image files from an image file. It basically
* converts images of other formats to TIFF format, or a multi-page TIFF
* image to multiple TIFF image files.
*
* @param imageFile input image file
* @param index an index of the page; -1 means all pages, as in a multi-page
* TIFF image
* @return a list of TIFF image files
* @throws IOException
*/
public static List createTiffFiles(File imageFile, int index) throws IOException {
return createTiffFiles(imageFile, index, false);
}
/**
* Creates a list of TIFF image files from an image file. It basically
* converts images of other formats to TIFF format, or a multi-page TIFF
* image to multiple TIFF image files.
*
* @param imageFile input image file
* @param index an index of the page; -1 means all pages, as in a multi-page
* TIFF image
* @param preserve preserve compression mode
* @return a list of TIFF image files
* @throws IOException
*/
public static List createTiffFiles(File imageFile, int index, boolean preserve) throws IOException {
List tiffFiles = new ArrayList();
String imageFormat = getImageFileFormat(imageFile);
Iterator readers = ImageIO.getImageReadersByFormatName(imageFormat);
if (!readers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_READER_MESSAGE);
}
ImageReader reader = readers.next();
// Get tiff writer and set output to file
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_FORMAT);
if (!writers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_WRITER_MESSAGE);
}
ImageWriter writer = writers.next();
try (ImageInputStream iis = ImageIO.createImageInputStream(imageFile)) {
reader.setInput(iis);
// Read the stream metadata
// IIOMetadata streamMetadata = reader.getStreamMetadata();
// Set up the writeParam
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
if (!preserve) {
// not preserve original sizes; decompress
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
}
// Read the stream metadata
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
int imageTotal = reader.getNumImages(true);
for (int i = 0; i < imageTotal; i++) {
// all if index == -1; otherwise, only index-th
if (index == -1 || i == index) {
IIOImage oimage = reader.readAll(i, reader.getDefaultReadParam());
File tiffFile = File.createTempFile(OUTPUT_FILE_NAME, TIFF_EXT);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(tiffFile)) {
writer.setOutput(ios);
writer.write(streamMetadata, oimage, tiffWriteParam);
tiffFiles.add(tiffFile);
}
}
}
return tiffFiles;
} finally {
if (reader != null) {
reader.dispose();
}
if (writer != null) {
writer.dispose();
}
}
}
/**
* Creates a list of TIFF image files from a list of IIOImage
* objects.
*
* @param imageList a list of IIOImage
objects
* @param index an index of the page; -1 means all pages
* @return a list of TIFF image files
* @throws IOException
*/
public static List createTiffFiles(List imageList, int index) throws IOException {
return createTiffFiles(imageList, index, 0, 0);
}
public static List createTiffFiles(List imageList, int index, int dpiX, int dpiY) throws IOException {
List tiffFiles = new ArrayList();
//Set up the writeParam
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
//Get tiff writer and set output to file
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_FORMAT);
if (!writers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_WRITER_MESSAGE);
}
ImageWriter writer = writers.next();
//Get the stream metadata
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
// all if index == -1; otherwise, only index-th
for (IIOImage oimage : (index == -1 ? imageList : imageList.subList(index, index + 1))) {
if (dpiX != 0 && dpiY != 0) {
// Get the default image metadata.
ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(oimage.getRenderedImage());
ImageWriteParam param = writer.getDefaultWriteParam();
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageType, param);
imageMetadata = setDPIViaAPI(imageMetadata, dpiX, dpiY);
oimage.setMetadata(imageMetadata);
}
File tiffFile = File.createTempFile(OUTPUT_FILE_NAME, TIFF_EXT);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(tiffFile)) {
writer.setOutput(ios);
writer.write(streamMetadata, oimage, tiffWriteParam);
tiffFiles.add(tiffFile);
}
}
writer.dispose();
return tiffFiles;
}
/**
* Set DPI using API.
*
* @param imageMetadata original IIOMetadata
* @param dpiX horizontal resolution
* @param dpiY vertical resolution
* @return modified IIOMetadata
* @throws IIOInvalidTreeException
*/
private static IIOMetadata setDPIViaAPI(IIOMetadata imageMetadata, int dpiX, int dpiY)
throws IIOInvalidTreeException {
// Derive the TIFFDirectory from the metadata.
TIFFDirectory dir = TIFFDirectory.createFromMetadata(imageMetadata);
// Get {X,Y}Resolution tags.
BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
// Create {X,Y}Resolution fields.
TIFFField fieldXRes = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL,
1, new long[][]{{dpiX, 1}});
TIFFField fieldYRes = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL,
1, new long[][]{{dpiY, 1}});
// Append {X,Y}Resolution fields to directory.
dir.addTIFFField(fieldXRes);
dir.addTIFFField(fieldYRes);
// Convert to metadata object.
IIOMetadata metadata = dir.getAsMetadata();
// Add other metadata.
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(25.4f / dpiX));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(25.4f / dpiY));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
return metadata;
}
/**
* Gets pixel data of an IIOImage
object.
*
* @param image an IIOImage
object
* @return a byte buffer of pixel data
*/
public static ByteBuffer getImageByteBuffer(IIOImage image) {
return getImageByteBuffer(image.getRenderedImage());
}
/**
* Gets pixel data of an RenderedImage
object.
*
* @param image an RenderedImage
object
* @return a byte buffer of pixel data
*/
public static ByteBuffer getImageByteBuffer(RenderedImage image) {
ColorModel cm = image.getColorModel();
WritableRaster wr = image.getData().createCompatibleWritableRaster(image.getWidth(), image.getHeight());
image.copyData(wr);
BufferedImage bi = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), null);
return convertImageData(bi);
}
/**
* Converts BufferedImage
to ByteBuffer
.
*
* @param bi Input image
* @return pixel data
*/
public static ByteBuffer convertImageData(BufferedImage bi) {
DataBuffer buff = bi.getRaster().getDataBuffer();
// ClassCastException thrown if buff not instanceof DataBufferByte because raster data is not necessarily bytes.
// Convert the original buffered image to grayscale.
if (!(buff instanceof DataBufferByte)) {
BufferedImage grayscaleImage = ImageHelper.convertImageToGrayscale(bi);
buff = grayscaleImage.getRaster().getDataBuffer();
}
byte[] pixelData = ((DataBufferByte) buff).getData();
// return ByteBuffer.wrap(pixelData);
ByteBuffer buf = ByteBuffer.allocateDirect(pixelData.length);
buf.order(ByteOrder.nativeOrder());
buf.put(pixelData);
((Buffer) buf).flip();
return buf;
}
/**
* Gets image file format.
*
* @param imageFile input image file
* @return image file format
*/
public static String getImageFileFormat(File imageFile) {
String imageFileName = imageFile.getName();
String imageFormat = imageFileName.substring(imageFileName.lastIndexOf('.') + 1);
if (imageFormat.matches("(pbm|pgm|ppm)")) {
imageFormat = "pnm";
} else if (imageFormat.matches("(jp2|j2k|jpf|jpx|jpm)")) {
imageFormat = "jpeg2000";
}
return imageFormat;
}
/**
* Gets image file. Convert to multi-page TIFF if given PDF.
*
* @param inputFile input file (common image or PDF)
* @return image file
* @throws IOException
*/
public static File getImageFile(File inputFile) throws IOException {
File imageFile = inputFile;
if (inputFile.getName().toLowerCase().endsWith(".pdf")) {
imageFile = PdfUtilities.convertPdf2Tiff(inputFile);
}
return imageFile;
}
/**
* Gets a list of BufferedImage
objects for an image file.
*
* @param inputFile input image file. It can be any of the supported
* formats, including TIFF, JPEG, GIF, PNG, BMP, JPEG, and PDF if GPL
* Ghostscript or PDFBox is installed
* @return a list of BufferedImage
objects
* @throws IOException
*/
public static List getImageList(File inputFile) throws IOException {
// convert to TIFF if PDF
File imageFile = getImageFile(inputFile);
List biList = new ArrayList();
String imageFormat = getImageFileFormat(imageFile);
Iterator readers = ImageIO.getImageReadersByFormatName(imageFormat);
if (!readers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_READER_MESSAGE);
}
ImageReader reader = readers.next();
try (ImageInputStream iis = ImageIO.createImageInputStream(imageFile)) {
reader.setInput(iis);
int imageTotal = reader.getNumImages(true);
for (int i = 0; i < imageTotal; i++) {
BufferedImage bi = reader.read(i);
biList.add(bi);
}
return biList;
} finally {
if (reader != null) {
reader.dispose();
}
// delete temporary TIFF image for PDF
if (imageFile != null && imageFile.exists() && imageFile != inputFile && imageFile.getName().startsWith("multipage") && imageFile.getName().endsWith(TIFF_EXT)) {
imageFile.delete();
}
}
}
/**
* Gets a list of IIOImage
objects for an image file.
*
* @param inputFile input image file. It can be any of the supported
* formats, including TIFF, JPEG, GIF, PNG, BMP, JPEG, and PDF if GPL
* Ghostscript or PDFBox is installed
* @return a list of IIOImage
objects
* @throws IOException
*/
public static List getIIOImageList(File inputFile) throws IOException {
// convert to TIFF if PDF
File imageFile = getImageFile(inputFile);
List iioImageList = new ArrayList();
String imageFormat = getImageFileFormat(imageFile);
Iterator readers = ImageIO.getImageReadersByFormatName(imageFormat);
if (!readers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_READER_MESSAGE);
}
ImageReader reader = readers.next();
try (ImageInputStream iis = ImageIO.createImageInputStream(imageFile)) {
reader.setInput(iis);
int imageTotal = reader.getNumImages(true);
for (int i = 0; i < imageTotal; i++) {
// IIOImage oimage = new IIOImage(reader.read(i), null, reader.getImageMetadata(i));
IIOImage oimage = reader.readAll(i, reader.getDefaultReadParam());
iioImageList.add(oimage);
}
return iioImageList;
} finally {
if (reader != null) {
reader.dispose();
}
// delete temporary TIFF image for PDF
if (imageFile != null && imageFile.exists() && imageFile != inputFile && imageFile.getName().startsWith("multipage") && imageFile.getName().endsWith(TIFF_EXT)) {
imageFile.delete();
}
}
}
/**
* Gets a list of IIOImage
objects for a
* BufferedImage
.
*
* @param bi input image
* @return a list of IIOImage
objects
* @throws IOException
*/
public static List getIIOImageList(BufferedImage bi) throws IOException {
List iioImageList = new ArrayList();
IIOImage oimage = new IIOImage(bi, null, null);
iioImageList.add(oimage);
return iioImageList;
}
/**
* Merges multiple images into one multi-page TIFF image.
*
* @param inputImages an array of image files
* @param outputTiff the output multi-page TIFF file
* @throws IOException
*/
public static void mergeTiff(File[] inputImages, File outputTiff) throws IOException {
if (inputImages.length == 0) {
// if no image
return;
}
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_FORMAT);
if (!writers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_WRITER_MESSAGE);
}
ImageWriter writer = writers.next();
//Set up the writeParam
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
// tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED); // commented out to preserve original sizes
//Get the stream metadata
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputTiff)) {
writer.setOutput(ios);
boolean firstPage = true;
int index = 1;
for (File inputImage : inputImages) {
String imageFileFormat = getImageFileFormat(inputImage);
Iterator readers = ImageIO.getImageReadersByFormatName(imageFileFormat);
if (!readers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_READER_MESSAGE);
}
ImageReader reader = readers.next();
try (ImageInputStream iis = ImageIO.createImageInputStream(inputImage)) {
reader.setInput(iis);
int imageTotal = reader.getNumImages(true);
for (int i = 0; i < imageTotal; i++) {
IIOImage oimage = reader.readAll(i, reader.getDefaultReadParam());
if (firstPage) {
writer.write(streamMetadata, oimage, tiffWriteParam);
firstPage = false;
} else {
writer.writeInsert(index++, oimage, tiffWriteParam);
}
}
} finally {
if (reader != null) {
reader.dispose();
}
}
}
} finally {
writer.dispose();
}
}
/**
* Merges multiple images into one multi-page TIFF image.
*
* @param inputImages an array of BufferedImage
* @param outputTiff the output TIFF file
* @throws IOException
*/
public static void mergeTiff(BufferedImage[] inputImages, File outputTiff) throws IOException {
mergeTiff(inputImages, outputTiff, null);
}
/**
* Merges multiple images into one multi-page TIFF image.
*
* @param inputImages an array of BufferedImage
* @param outputTiff the output TIFF file
* @param compressionType valid values: LZW, CCITT T.6, PackBits
* @throws IOException
*/
public static void mergeTiff(BufferedImage[] inputImages, File outputTiff, String compressionType) throws IOException {
List imageList = new ArrayList();
for (BufferedImage inputImage : inputImages) {
imageList.add(new IIOImage(inputImage, null, null));
}
mergeTiff(imageList, outputTiff, compressionType);
}
/**
* Merges multiple images into one multi-page TIFF image.
*
* @param imageList a list of IIOImage
objects
* @param outputTiff the output TIFF file
* @throws IOException
*/
public static void mergeTiff(List imageList, File outputTiff) throws IOException {
mergeTiff(imageList, outputTiff, null);
}
/**
* Merges multiple images into one multi-page TIFF image.
*
* @param imageList a list of IIOImage
objects
* @param outputTiff the output TIFF file
* @param compressionType valid values: LZW, CCITT T.6, PackBits
* @throws IOException
*/
public static void mergeTiff(List imageList, File outputTiff, String compressionType) throws IOException {
if (imageList == null || imageList.isEmpty()) {
// if no image
return;
}
Iterator writers = ImageIO.getImageWritersByFormatName(TIFF_FORMAT);
if (!writers.hasNext()) {
throw new RuntimeException(JAI_IMAGE_WRITER_MESSAGE);
}
ImageWriter writer = writers.next();
//Set up the writeParam
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
// tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED); // comment out to preserve original sizes
if (compressionType != null) {
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
tiffWriteParam.setCompressionType(compressionType);
}
//Get the stream metadata
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputTiff)) {
writer.setOutput(ios);
int dpiX = 300;
int dpiY = 300;
for (IIOImage iioImage : imageList) {
// Get the default image metadata.
ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(iioImage.getRenderedImage());
ImageWriteParam param = writer.getDefaultWriteParam();
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageType, param);
imageMetadata = setDPIViaAPI(imageMetadata, dpiX, dpiY);
iioImage.setMetadata(imageMetadata);
}
IIOImage firstIioImage = imageList.remove(0);
writer.write(streamMetadata, firstIioImage, tiffWriteParam);
int i = 1;
for (IIOImage iioImage : imageList) {
writer.writeInsert(i++, iioImage, tiffWriteParam);
}
} finally {
writer.dispose();
}
}
/**
* Deskews image.
*
* @param imageFile input image
* @param minimumDeskewThreshold minimum deskew threshold (typically, 0.05d)
* @return temporary multi-page TIFF image file
* @throws IOException
*/
public static File deskewImage(File imageFile, double minimumDeskewThreshold) throws IOException {
List imageList = getImageList(imageFile);
for (int i = 0; i < imageList.size(); i++) {
BufferedImage bi = imageList.get(i);
ImageDeskew deskew = new ImageDeskew(bi);
double imageSkewAngle = deskew.getSkewAngle();
if ((imageSkewAngle > minimumDeskewThreshold || imageSkewAngle < -(minimumDeskewThreshold))) {
bi = ImageUtil.rotate(bi, -imageSkewAngle, bi.getWidth() / 2, bi.getHeight() / 2);
imageList.set(i, bi); // replace original with deskewed image
}
}
File tempImageFile = File.createTempFile(FilenameUtils.getBaseName(imageFile.getName()), ".tif");
mergeTiff(imageList.toArray(new BufferedImage[0]), tempImageFile);
return tempImageFile;
}
/**
* Reads image meta data.
*
* @param oimage
* @return a map of meta data
*/
public static Map readImageData(IIOImage oimage) {
Map dict = new HashMap();
IIOMetadata imageMetadata = oimage.getMetadata();
if (imageMetadata != null) {
IIOMetadataNode dimNode = (IIOMetadataNode) imageMetadata.getAsTree("javax_imageio_1.0");
NodeList nodes = dimNode.getElementsByTagName("HorizontalPixelSize");
int dpiX;
if (nodes.getLength() > 0) {
float dpcWidth = Float.parseFloat(nodes.item(0).getAttributes().item(0).getNodeValue());
dpiX = (int) Math.round(25.4f / dpcWidth);
} else {
dpiX = Toolkit.getDefaultToolkit().getScreenResolution();
}
dict.put("dpiX", String.valueOf(dpiX));
nodes = dimNode.getElementsByTagName("VerticalPixelSize");
int dpiY;
if (nodes.getLength() > 0) {
float dpcHeight = Float.parseFloat(nodes.item(0).getAttributes().item(0).getNodeValue());
dpiY = (int) Math.round(25.4f / dpcHeight);
} else {
dpiY = Toolkit.getDefaultToolkit().getScreenResolution();
}
dict.put("dpiY", String.valueOf(dpiY));
}
return dict;
}
}