I am working on a Spring-MVC application in which I am using Tesseract for OCR. I am getting an Index out of bounds exception for the file I am passing. Any ideas?
Error log :
et.sourceforge.tess4j.TesseractException: java.lang.IndexOutOfBoundsException
at net.sourceforge.tess4j.Tesseract.doOCR(Tesseract.java:215)
at net.sourceforge.tess4j.Tesseract.doOCR(Tesseract.java:196)
at com.tooltank.spring.service.GroupAttachmentsServiceImpl.testOcr(GroupAttachmentsServiceImpl.java:839)
at com.tooltank.spring.service.GroupAttachmentsServiceImpl.lambda$addAttachment$0(GroupAttachmentsServiceImpl.java:447)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IndexOutOfBoundsException
at javax.imageio.stream.FileCacheImageOutputStream.seek(FileCacheImageOutputStream.java:170)
at net.sourceforge.tess4j.util.ImageIOHelper.getImageByteBuffer(ImageIOHelper.java:297)
at net.sourceforge.tess4j.Tesseract.setImage(Tesseract.java:397)
at net.sourceforge.tess4j.Tesseract.doOCR(Tesseract.java:290)
at net.sourceforge.tess4j.Tesseract.doOCR(Tesseract.java:212)
... 4 more
Code :
private String testOcr(String fileLocation, int attachId) {
try {
File imageFile = new File(fileLocation);
BufferedImage img = ImageIO.read(imageFile);
BufferedImage blackNWhite = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics2D graphics = blackNWhite.createGraphics();
graphics.drawImage(img, 0, 0, null);
String identifier = String.valueOf(new BigInteger(130, random).toString(32));
String blackAndWhiteImage = previewPath + identifier + ".png";
File outputfile = new File(blackAndWhiteImage);
ImageIO.write(blackNWhite, "png", outputfile);
ITesseract instance = new Tesseract();
// Point to one folder above tessdata directory, must contain training data
instance.setDatapath("/usr/share/tesseract-ocr/");
// ISO 693-3 standard
instance.setLanguage("deu");
String result = instance.doOCR(outputfile);
result = result.replaceAll("[^a-zA-Z0-9öÖäÄüÜß#\\s]", "");
Files.delete(new File(blackAndWhiteImage).toPath());
GroupAttachments groupAttachments = this.groupAttachmentsDAO.getAttachmenById(attachId);
System.out.println("OCR Result is "+result);
if (groupAttachments != null) {
saveIndexes(result, groupAttachments.getFileName(), null, groupAttachments.getGroupId(), false, attachId);
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Thank you.
Due to a bug in Java Image IO (which was fixed with Java 9), the current version of Java Tesseract Wrapper (3.4.0 as this answer was written) does not work with < Java 9. To work with lower Java versions, you can try the following fix to Tesseract ImageIOHelper class. Simply make a copy of the class in your project and apply the necessary changes and it will work with both files and BufferedImages smoothly.
Note: This version does not use the Tiff optimization used in the original class, you can add it if it is necessary for your project.
public static ByteBuffer getImageByteBuffer(RenderedImage image) throws IOException {
//Set up the writeParam
if (image instanceof BufferedImage) {
return convertImageData((BufferedImage) image);
}
ColorModel cm = image.getColorModel();
int width = image.getWidth();
int height = image.getHeight();
WritableRaster raster = cm
.createCompatibleWritableRaster(width, height);
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
Hashtable properties = new Hashtable();
String[] keys = image.getPropertyNames();
if (keys != null) {
for (int i = 0; i < keys.length; i++) {
properties.put(keys[i], image.getProperty(keys[i]));
}
}
BufferedImage result = new BufferedImage(cm, raster,
isAlphaPremultiplied, properties);
image.copyData(raster);
return convertImageData(result);
}
Try upgrading to tess4j version 3.4.1.
That solved the issue for me.
Related
I´m trying to compare two fingerprint in java using DigitalPersona SDK v 3.0.0, but I geeting FMD is Invalid Error or sometimes I get One or more parameters passed to the API call are invalid.
I have a png image obtained from Javascript API and I tried to create a FID then a FDM like in this post
example
But I don´t know how to get the method params: inDpi, fingerPosition, cbeffId, outDpi and rotate180 to get de Fid
Fid fid = UareUGlobal.getImporter().ImportRaw(rawPixels,
width, height, inDpi, fingerPosition, cbeffId,
Fid.Format.ANSI_381_2004, outDpi, rotate180);
also I had tried ImportFmd using intermediate format without success
Any help will be apreciated
Well, I currently working on that one too. UareU SDK V3.0.0,
But I have problem with Verify and Compare feature.
However, to answer your question, In case you have a fingerprint image,
You can import image into Fmd directly. I meant this (I use ISO standard):
Fmd fmd = UareUGlobal.GetEngine().CreateFmd(bInput, 500, 550, 700, 0, 0, Fmd.Format.ISO_19794_2_2005)
Then, you can do compare or verify directly with this fmd.
Note: with, height, resolution must be changed according to your image and reader.
cbeffid parameter, I have tried 0 and 1. If any one found the right answer for this parameter, please suggest me as well.
CBEFF
error
API call are invalid
Mostly, I found the resolution, fingerprint format and finger position param is in correct. fingerprint param, I usually set to 0 (first finger or first view)
Update
If you are using javascript. I would recommend you to convert base64 string from javascript to bufferedImage first.
public static Fmd importImageString(String image) throws UnsupportedEncodingException, IOException, UareUException {
byte[] bytes = Base64.getDecoder().decode(image.getBytes("UTF-8"));
if (null != bytes && bytes.length > 0) {
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
BufferedImage buf = ImageIO.read(stream);
if (null != buf) {
return UareUGlobal.GetEngine().CreateFmd(
TestDPuareU.toBytes(buf),
buf.getWidth(),
buf.getHeight(),
500, 0, 1, Fmd.Format.ISO_19794_2_2005
);
}
}
return null;
}
public static byte[] toBytes(BufferedImage image) throws IOException {
WritableRaster raster = image.getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
return (data.getData());
}
The image I got it from javascript (var finger) sdk.onSamplesAcquired = function (s) like below
var samples = JSON.parse(s.samples);
var finger = Fingerprint.b64UrlTo64(samples[0])
This code worked for me as on digital persona 4500 model.
private boolean ProcessCaptureResult(CaptureThread.CaptureEvent evt) throws IOException{
boolean bCanceled = false;
// CaptureThread.CaptureEvent evt = evt;
if(null != evt.capture_result){
System.out.println("This is Processing Capture");
if(null != evt.capture_result.image && Reader.CaptureQuality.GOOD == evt.capture_result.quality){
//extract features
Engine engine = UareUGlobal.GetEngine();
try{
Fmd fmd = engine.CreateFmd(evt.capture_result.image,
Fmd.Format.ISO_19794_2_2005;
BufferedImage img=ImageIO.read(new File("C:\\Users\\User\\Documents\\NetBeansProjects\\FingerPrintApp\\MembersImages","imag6.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( img, "jpg", baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
// Fmd fmd1 = engine.CreateFmd(img, 0xa, 0xa, 0x14, Fmd.Format.ANSI_378_2004);
Fmd fmd2=UareUGlobal.GetEngine().CreateFmd(
toBytes(img),
img.getWidth(),
img.getHeight(),
500, 0, 3407615, Fmd.Format.ISO_19794_2_2005
);
System.out.println("Resulotion: "+evt.capture_result.image.getImageResolution()); ;
System.out.println("ID : "+evt.capture_result.image.getCbeffId()); ;
System.out.println(evt.capture_result.image.getData());
// if(null == m_fmds[0])
m_fmds[0] = fmd;
// else if(null == m_fmds[1])
m_fmds[1] = fmd2;
}
catch(UareUException e){ System.out.println("Engine.CreateFmd() "+e.getMessage()); }
// if(null != m_fmds[0] && null != m_fmds[1]){
//perform comparison
try{
System.out.println(m_fmds[0].getFormat().name());
int falsematch_rate = engine.Compare(m_fmds[0], 0, m_fmds[1], 0);
int target_falsematch_rate = Engine.PROBABILITY_ONE / 100000; //target rate is 0.00001
if(falsematch_rate < target_falsematch_rate){
System.out.println("Fingerprints matched.\n");
String str = String.format("dissimilarity score: 0x%x.\n", falsematch_rate);
System.out.println(str);
str = String.format("false match rate: %e.\n\n\n", (double)(falsematch_rate / Engine.PROBABILITY_ONE));
System.out.println(str);
}
else{
System.out.println("Fingerprints did not match.\n\n\n");
}
}
catch(UareUException e){ System.out.println("Engine.CreateFmd()"+ e.getMessage()); }
//discard FMDs
m_fmds[0] = null;
m_fmds[1] = null;
//the new loop starts
System.out.println(m_strPrompt1);
// }
// else{
// //the loop continues
// System.out.println(m_strPrompt2);
// }
}
else if(Reader.CaptureQuality.CANCELED == evt.capture_result.quality){
//capture or streaming was canceled, just quit
bCanceled = true;
}
else{
//bad quality
MessageBox.BadQuality(evt.capture_result.quality);
}
}
else if(null != evt.exception){
//exception during capture
MessageBox.DpError("Capture", evt.exception);
bCanceled = true;
}
else if(null != evt.reader_status){
//reader failure
MessageBox.BadStatus(evt.reader_status);
bCanceled = true;
}
return !bCanceled;
}
public static byte[] toBytes(BufferedImage image) {
WritableRaster raster = image.getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
return (data.getData());
}
-
Code working in digital persona 4500
#
Something like this is how you obtain those values, BUT Im getting the same error PID IS INVALID,so good luck and WHETHER you have the answer I hope you share it, regards.
m_fmd = m_engine.CreateFmd(cap_result.image, Fmd.Format.ANSI_378_2004);
data = m_fmd.getData();
width = m_fmd.getWidth();
height = m_fmd.getHeight();
resolution = m_fmd.getResolution();
finger_position = m_fmd.getCaptureEquipmentCompliance();
cbeff_id = m_fmd.getCbeffId();
format = m_fmd.getFormat();
I have a pdf containing 2 blank images. I need to replace both the images with 2 separate images using PDFBox. The problem is, both the blank images appear to have the same resource. So, if I replace one, the other one is replaced with the same image as well.
I followed this example and tried overriding the processOperator() method and replaced the images based on the imageHeight. However, it still ends up replacing both the images with the same image. This is my code thus far:
protected void processOperator( PDFOperator operator, List arguments ) throws IOException
{
String operation = operator.getOperation();
if( INVOKE_OPERATOR.equals(operation) )
{
COSName objectName = (COSName)arguments.get( 0 );
Map<String, PDXObject> xobjects = getResources().getXObjects();
PDXObject xobject = (PDXObject)xobjects.get( objectName.getName() );
if( xobject instanceof PDXObjectImage )
{
PDXObjectImage blankImage = (PDXObjectImage)xobject;
int imageWidth = blankImage.getWidth();
int imageHeight = blankImage.getHeight();
System.out.println("Image width >>> "+imageWidth+" height >>>> "+imageHeight);
// Check if it is blank image 1 based on height
if(imageHeight < 480){
File logo = new File("abc.jpg");
BufferedImage bufferedImage = ImageIO.read(logo);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "jpg", baos );
baos.flush();
byte[] logoImageInBytes = baos.toByteArray();
baos.close();
// label will be used to replace the blank image
label = logoImageInBytes;
}
BufferedImage img = ImageIO.read(new ByteArrayInputStream(label));
BufferedImage resizedImage = Scalr.resize(img, Scalr.Method.BALANCED, Scalr.Mode.FIT_EXACT, img.getWidth(), img.getHeight());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "jpg", baos);
// Replace empty image in template with the image generated from shipping label byte array
PDXObjectImage validImage = new PDJpeg(doc, new ByteArrayInputStream(baos.toByteArray()));
blankImage.getCOSStream().replaceWithStream(validImage.getCOSStream());
}
Now, when I remove the if block which checks if (imageHeight < 480), it prints the imageHeight as 30 and 470 for the blank images. However, when I add the if block, it prints the imageHeight as 480 and 1500 and never goes inside the if block because of which both the blank images end up getting replaced by the same image.
What's going on here? I'm new to PDFBox, so I am unsure if my code is correct.
While first thinking about a generic way to actually replace the existing Image by the new Images, I agree with #TilmanHausherr that a more simple solution would be to simply add an extra content stream with two images in the size / position you need covering the existing Image.
This approach is easier to implement (even generically) and less error-prone than actual replacement.
In a generic solution we do not have the Image positions beforehand. To determine them, we can use this helper class (which essentially is a rip-off of the PDFBox example PrintImageLocations):
public class ImageLocator extends PDFStreamEngine
{
private static final String INVOKE_OPERATOR = "Do";
public ImageLocator() throws IOException
{
super(ResourceLoader.loadProperties("org/apache/pdfbox/resources/PDFTextStripper.properties", true));
}
public List<ImageLocation> getLocations()
{
return new ArrayList<ImageLocation>(locations);
}
protected void processOperator(PDFOperator operator, List<COSBase> arguments) throws IOException
{
String operation = operator.getOperation();
if (INVOKE_OPERATOR.equals(operation))
{
COSName objectName = (COSName) arguments.get(0);
Map<String, PDXObject> xobjects = getResources().getXObjects();
PDXObject xobject = (PDXObject) xobjects.get(objectName.getName());
if (xobject instanceof PDXObjectImage)
{
PDXObjectImage image = (PDXObjectImage) xobject;
PDPage page = getCurrentPage();
Matrix matrix = getGraphicsState().getCurrentTransformationMatrix();
locations.add(new ImageLocation(page, matrix, image));
}
else if (xobject instanceof PDXObjectForm)
{
// save the graphics state
getGraphicsStack().push((PDGraphicsState) getGraphicsState().clone());
PDPage page = getCurrentPage();
PDXObjectForm form = (PDXObjectForm) xobject;
COSStream invoke = (COSStream) form.getCOSObject();
PDResources pdResources = form.getResources();
if (pdResources == null)
{
pdResources = page.findResources();
}
// if there is an optional form matrix, we have to
// map the form space to the user space
Matrix matrix = form.getMatrix();
if (matrix != null)
{
Matrix xobjectCTM = matrix.multiply(getGraphicsState().getCurrentTransformationMatrix());
getGraphicsState().setCurrentTransformationMatrix(xobjectCTM);
}
processSubStream(page, pdResources, invoke);
// restore the graphics state
setGraphicsState((PDGraphicsState) getGraphicsStack().pop());
}
}
else
{
super.processOperator(operator, arguments);
}
}
public class ImageLocation
{
public ImageLocation(PDPage page, Matrix matrix, PDXObjectImage image)
{
this.page = page;
this.matrix = matrix;
this.image = image;
}
public PDPage getPage()
{
return page;
}
public Matrix getMatrix()
{
return matrix;
}
public PDXObjectImage getImage()
{
return image;
}
final PDPage page;
final Matrix matrix;
final PDXObjectImage image;
}
final List<ImageLocation> locations = new ArrayList<ImageLocation>();
}
(ImageLocator.java)
In contrast to the example class this helper stores the locations in a list instead of printing them.
We now can cover existing images using code like this:
try ( InputStream resource = getClass().getResourceAsStream("sample.pdf");
InputStream left = getClass().getResourceAsStream("left.png");
InputStream right = getClass().getResourceAsStream("right.png");
PDDocument document = PDDocument.load(resource) )
{
if (document.isEncrypted())
{
document.decrypt("");
}
PDJpeg leftImage = new PDJpeg(document, ImageIO.read(left));
PDJpeg rightImage = new PDJpeg(document, ImageIO.read(right));
// Locate images
ImageLocator locator = new ImageLocator();
List<?> allPages = document.getDocumentCatalog().getAllPages();
for (int i = 0; i < allPages.size(); i++)
{
PDPage page = (PDPage) allPages.get(i);
locator.processStream(page, page.findResources(), page.getContents().getStream());
}
// cover images
for (ImageLocation location : locator.getLocations())
{
// Decide on a replacement
PDRectangle cropBox = location.getPage().findCropBox();
float center = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2.0f;
PDJpeg image = location.getMatrix().getXPosition() < center ? leftImage : rightImage;
AffineTransform transform = location.getMatrix().createAffineTransform();
PDPageContentStream content = new PDPageContentStream(document, location.getPage(), true, false, true);
content.drawXObject(image, transform);
content.close();
}
document.save(new File(RESULT_FOLDER, "sample-changed.pdf"));
}
(OverwriteImage)
This sample covers all images on the left half of their respective page with left.png and all others with right.png.
I have no implementation or example, but I want to illustrate you a possible way to do what you want by the following steps:
Since you need 2 Images (lets tell them imageA and imageB) in the pdf instead of 1 (which is the blank one). You have to add both of them to the pdf.
save the file temporary - optional, it could work without rewriting the pdf
reopen the file - optional, if you don't need step 2, you also don't need this step
Then replace the blank image with imageA or imageB
Remove the blank image from the pdf
Save the pdf
It seems to me there are two ways to store an attachment in a NotesDocument.
Either as a RichTextField or as a "MIME Part".
If they are stored as RichText you can do stuff like:
document.getAttachment(fileName)
That does not seem to work for an attachment stored as a MIME Part. See screenshot
I have thousands of documents like this in the backend. This is NOT a UI issue where I need to use the file Download control of XPages.
Each document as only 1 attachment. An Image. A JPG file. I have 3 databases for different sizes. Original, Large, and Small. Originally I created everything from documents that had the attachment stored as RichText. But my code saved them as MIME Part. that's just what it did. Not really my intent.
What happened is I lost some of my "Small" pictures so I need to rebuild them from the Original pictures that are now stored as MIME Part. So my ultimate goal is to get it from the NotesDocument into a Java Buffered Image.
I think I have the code to do what I want but I just "simply" can't figure out how to get the attachment off the document and then into a Java Buffered Image.
Below is some rough code I'm working with. My goal is to pass in the document with the original picture. I already have the fileName because I stored that out in metaData. But I don't know how to get that from the document itself. And I'm passing in "Small" to create the Small image.
I think I just don't know how to work with attachments stored in this manner.
Any ideas/advice would be appreciated! Thanks!!!
public Document processImage(Document inputDoc, String fileName, String size) throws IOException {
// fileName is the name of the attachment on the document
// The goal is to return a NEW BLANK document with the image on it
// The Calling code can then deal with keys and meta data.
// size is "Original", "Large" or "Small"
System.out.println("Processing Image, Size = " + size);
//System.out.println("Filename = " + fileName);
boolean result = false;
Session session = Factory.getSession();
Database db = session.getCurrentDatabase();
session.setConvertMime(true);
BufferedImage img;
BufferedImage convertedImage = null; // the output image
EmbeddedObject image = null;
InputStream imageStream = null;
int currentSize = 0;
int newWidth = 0;
String currentName = "";
try {
// Get the Embedded Object
image = inputDoc.getAttachment(fileName);
System.out.println("Input Form : " + inputDoc.getItemValueString("form"));
if (null == image) {
System.out.println("ALERT - IMAGE IS NULL");
}
currentSize = image.getFileSize();
currentName = image.getName();
// Get a Stream of the Imahe
imageStream = image.getInputStream();
img = ImageIO.read(imageStream); // this is the buffered image we'll work with
imageStream.close();
Document newDoc = db.createDocument();
// Remember this is a BLANK document. The calling code needs to set the form
if ("original".equalsIgnoreCase(size)) {
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Large".equalsIgnoreCase(size)) {
// Now we need to convert the LARGE image
// We're assuming FIXED HEIGHT of 600px
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 600);
convertedImage = this.getScaledInstance(img, newWidth, 600, false);
this.attachImage(newDoc, img, fileName, "JPG");
return newDoc;
}
if ("Small".equalsIgnoreCase(size)) {
System.out.println("converting Small");
newWidth = this.getNewWidth(img.getHeight(), img.getWidth(), 240);
convertedImage = this.getScaledInstance(img, newWidth, 240, false);
this.attachImage(newDoc, img, fileName, "JPG");
System.out.println("End Converting Small");
return newDoc;
}
return newDoc;
} catch (Exception e) {
// HANDLE EXCEPTION HERE
// SAMLPLE WRITE TO LOG.NSF
System.out.println("****************");
System.out.println("EXCEPTION IN processImage()");
System.out.println("****************");
System.out.println("picName: " + fileName);
e.printStackTrace();
return null;
} finally {
if (null != imageStream) {
imageStream.close();
}
if (null != image) {
LibraryUtils.incinerate(image);
}
}
}
I believe it will be some variation of the following code snippet. You might have to change which mimeentity has the content so it might be in the parent or another child depending.
Stream stream = session.createStream();
doc.getMIMEEntity().getFirstChildEntity().getContentAsBytes(stream);
ByteArrayInputStream bais = new ByteArrayInputStream(stream.read());
return ImageIO.read(bais);
EDIT:
session.setConvertMime(false);
Stream stream = session.createStream();
Item itm = doc.getFirstItem("ParentEntity");
MIMEEntity me = itm.getMIMEEntity();
MIMEEntity childEntity = me.getFirstChildEntity();
childEntity.getContentAsBytes(stream);
ByteArrayOutputStream bo = new ByteArrayOutputStream();
stream.getContents(bo);
byte[] mybytearray = bo.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(mybytearray);
return ImageIO.read(bais);
David have a look at DominoDocument,http://public.dhe.ibm.com/software/dw/lotus/Domino-Designer/JavaDocs/XPagesExtAPI/8.5.2/com/ibm/xsp/model/domino/wrapped/DominoDocument.html
There you can wrap every Notes document
In the DominoDocument, there such as DominoDocument.AttachmentValueHolder where you can access the attachments.
I have explained it at Engage. It very powerful
http://www.slideshare.net/flinden68/engage-use-notes-objects-in-memory-and-other-useful-java-tips-for-x-pages-development
I am using JAI to load in multipage TIFF images
File file = workArea[0];
SeekableStream s = new FileSeekableStream(file);
TIFFDecodeParam param = null;
ImageDecoder dec = ImageCodec.createImageDecoder("tiff", s, param);
//first page
RenderedImage op1 =
new NullOpImage(dec.decodeAsRenderedImage(0),
null,
OpImage.OP_IO_BOUND,
null);
BufferedImage pg1 = new BufferedImage(op1.getWidth(), op1.getHeight(),
BufferedImage.TYPE_INT_RGB);
pg1.getGraphics().drawImage((Image) op1, 0, 0, null);
However, in the last line I get a runtime error of:
Exception in thread "main" java.lang.ClassCastException:
javax.media.jai.MullOpImage cannot be cast to java.awt.Image
I clear the RenderedImage after attempting to set the BufferedImage so I don't exactly "need" the RenderedImage if there is another method of doing this.
I attempted:
pg1.setData(op1.getData());
and that gives an ArrayIndexOutOfBoundsException. I'm not sure why exactly as pg1's width and height are set by op1's, but there is probably a very valid reason.
I found a solution at http://www.jguru.com/faq/view.jsp?EID=114602
The first one didn't work, however, the convertRenderedImage function did work.
public BufferedImage convertRenderedImage(RenderedImage img) {
if (img instanceof BufferedImage) {
return (BufferedImage)img;
}
ColorModel cm = img.getColorModel();
int width = img.getWidth();
int height = img.getHeight();
WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
Hashtable properties = new Hashtable();
String[] keys = img.getPropertyNames();
if (keys!=null) {
for (int i = 0; i < keys.length; i++) {
properties.put(keys[i], img.getProperty(keys[i]));
}
}
BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
img.copyData(raster);
return result;
}
Use op1.getAsBufferedImage() to create pg1.
If you are stuck with a RenderedImage, you can use
PlanarImage.wrapRenderedImage(renderedImage).getAsBufferedImage()
see here for documentation
JAI apparently has a "converter" class in there:
ImageDecoder dec = ImageCodec.createImageDecoder("PNM", new File(input), null);
return new RenderedImageAdapter(dec.decodeAsRenderedImage()).getAsBufferedImage()
ref: http://www.programcreek.com/java-api-examples/index.php?api=com.sun.media.jai.codec.ImageDecoder
Try this :
RenderedImage im = dec.decodeAsRenderedImage();
BufferedImage bi = PlanarImage.wrapRenderedImage(im).getAsBufferedImage();
The easiest way to load TIFF is using Twelve Monkey with provide plugin to support TIFF format into Standard Java ImageIO.
Just add below Maven dependency,
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.5</version>
</dependency>
Now, you will be able to load TIFF file directly Using ImageIO.
ImageReader imageReader1 = SPI.createReaderInstance();
ImageInputStream iis1 = ImageIO.createImageInputStream(new File("1.tif"));
imageReader1.setInput(iis1);
BufferedImage = imageReader1.read(0);
It is very easy and reliable because it uses standard Java ImageIO API for all processing.
Only the Plugin from Twelve Monkey provide the SPI plugin to provide support of TIFF.
Adding here one example program which is running is Java 8 and it reads TIFF files and create a single multi-page TIFF file:
BufferedImage b1 = null;
BufferedImage b2 = null;
TIFFImageReaderSpi SPI = new TIFFImageReaderSpi();
ImageReader imageReader1 = SPI.createReaderInstance();
ImageInputStream iis1 = ImageIO.createImageInputStream(new File("1.tif"));
imageReader1.setInput(iis1);
b1 = imageReader1.read(0);
ImageReader imageReader2 = SPI.createReaderInstance();
ImageInputStream iis2 = ImageIO.createImageInputStream(new File("2.tif"));
imageReader2.setInput(iis2);
b2 = imageReader2.read(0);
ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();
writer.setOutput(ImageIO.createImageOutputStream(new File("3.tif")));
ImageWriteParam writeParam = writer.getDefaultWriteParam();
//writeParam.setTilingMode(ImageWriteParam.MODE_EXPLICIT);
//writeParam.setCompressionType("Deflate");
writer.prepareWriteSequence(null);
IIOImage i1 = new IIOImage(b1, null, null);
IIOImage i2 = new IIOImage(b2, null, null);
writer.writeToSequence(i1, writeParam);
writer.writeToSequence(i2, writeParam);
writer.endWriteSequence();
writer.dispose();
It is working with Java 8, if someone wants to add compression just comment the line and add appropriate compression name.
I have a BufferedImage:
BufferedImage bi = new BufferedImage(14400, 14400, BufferedImage.TYPE_INT_ARGB);
I have saved this image to a PNG file using the following code:
public static void saveGridImage(BufferedImage sourceImage, int DPI,
File output) throws IOException {
output.delete();
final String formatName = "png";
for (Iterator<ImageWriter> iw = ImageIO
.getImageWritersByFormatName(formatName); iw.hasNext();) {
ImageWriter writer = iw.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier
.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(
typeSpecifier, writeParam);
if (metadata.isReadOnly()
|| !metadata.isStandardMetadataFormatSupported()) {
continue;
}
setDPI(metadata, DPI);
final ImageOutputStream stream = ImageIO
.createImageOutputStream(output);
try {
writer.setOutput(stream);
writer.write(metadata,
new IIOImage(sourceImage, null, metadata), writeParam);
} finally {
stream.close();
}
break;
}
}
public static void setDPI(IIOMetadata metadata, int DPI)
throws IIOInvalidTreeException {
double INCH_2_CM = 2.54;
// for PNG, it's dots per millimeter
double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM;
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
}
When the code executes it creates an PNG file with 400 DPI and Disk Size of 168 MB; this is too much.
Is there any way or parameters I can use to save a smaller PNG?
Before, I had a 1.20 GB TIFF file, and when I converted it to PNG using imagemagick at 400 DPI, the resulting file size was only 700 KB.
So, I think I might be able to save the above file smaller.
Can pngj help me? Because I now have a png file which I can read in pngj library.
A 14400x14400 ARGB8 image has a raw (uncompressed) size of 791MB. It will compress more or less according to its nature (has uniform or smooth zones) and according (less important) to the PNG compression parameters.
when i convert it using imagemagic to PNG using 400 DPI , the
resulting file size is only 700 KB.
(I don't understand why you speak of DPI, that has nothing to do, what matters is the size in pixels) Are you saying that you are getting a 14400x14400 ARGB of 700KB? That would represent a compression of 1/1000, hard to believe unless the image is practically flat.
You should first understand what is going on here.
Anyway, here's a sample code with PNGJ
/** writes a BufferedImage of type TYPE_INT_ARGB to PNG using PNGJ */
public static void writeARGB(BufferedImage bi, OutputStream os) {
if(bi.getType() != BufferedImage.TYPE_INT_ARGB)
throw new PngjException("This method expects BufferedImage.TYPE_INT_ARGB" );
ImageInfo imi = new ImageInfo(bi.getWidth(), bi.getHeight(), 8, true);
PngWriter pngw = new PngWriter(os, imi);
pngw.setCompLevel(9);// maximum compression, not critical usually
pngw.setFilterType(FilterType.FILTER_AGGRESSIVE); // see what you prefer here
DataBufferInt db =((DataBufferInt) bi.getRaster().getDataBuffer());
SinglePixelPackedSampleModel samplemodel = (SinglePixelPackedSampleModel) bi.getSampleModel();
if(db.getNumBanks()!=1)
throw new PngjException("This method expects one bank");
ImageLine line = new ImageLine(imi);
for (int row = 0; row < imi.rows; row++) {
int elem=samplemodel.getOffset(0,row);
for (int col = 0,j=0; col < imi.cols; col++) {
int sample = db.getElem(elem++);
line.scanline[j++] = (sample & 0xFF0000)>>16; // R
line.scanline[j++] = (sample & 0xFF00)>>8; // G
line.scanline[j++] = (sample & 0xFF); // B
line.scanline[j++] = (((sample & 0xFF000000)>>24)&0xFF); // A
}
pngw.writeRow(line, row);
}
pngw.end();
}
I would attempt to fiddle with the settings on the writeParam object you're creating. Currently you're calling getDefaultWriteParam(); which gives you a basic writeParam object. My guess is the default would be NO compression.
After doing that, you can probably set some of the compression modes to reduce the file size.
writeParam.setCompressionMode(int mode);
writeParam.setCompressionQuality(float quality);
writeParam.setCompressionType(String compressionType);
See http://docs.oracle.com/javase/6/docs/api/javax/imageio/ImageWriteParam.html
And specifically http://docs.oracle.com/javase/6/docs/api/javax/imageio/ImageWriteParam.html#setCompressionMode(int)
sample code for pngj that works for 2.x versions of the leonbloy's pngj library
/** writes a BufferedImage of type TYPE_INT_ARGB to PNG using PNGJ */
public static void writePNGJARGB(BufferedImage bi, /*OutputStream os, */File file) {
System.out.println(".....entering PNGj alternative image file save mode....." );
if(bi.getType() != BufferedImage.TYPE_INT_ARGB) throw new PngjException("This method expects BufferedImage.TYPE_INT_ARGB" );
ImageInfo imi = new ImageInfo(bi.getWidth(), bi.getHeight(), 8, true);
PngWriter pngw = new PngWriter(file, imi, false);
// PngWriter pngw = new PngWriter(file,imginfo,overwrite); //params
pngw.setCompLevel(7); // tuning compression, not critical usually
pngw.setFilterType(FilterType.FILTER_PAETH); // tuning, see what you prefer here
System.out.println("..... PNGj metadata = "+pngw.getMetadata() );
DataBufferInt db =((DataBufferInt) bi.getRaster().getDataBuffer());
if(db.getNumBanks()!=1) {
throw new PngjException("This method expects one bank");
}
SinglePixelPackedSampleModel samplemodel = (SinglePixelPackedSampleModel) bi.getSampleModel();
ImageLineInt line = new ImageLineInt(imi);
int[] dbbuf = db.getData();
for (int row = 0; row < imi.rows; row++) {
int elem=samplemodel.getOffset(0,row);
for (int col = 0,j=0; col < imi.cols; col++) {
int sample = dbbuf[elem++];
line.getScanline()[j++] = (sample & 0xFF0000)>>16; // R
line.getScanline()[j++] = (sample & 0xFF00)>>8; // G
line.getScanline()[j++] = (sample & 0xFF); // B
line.getScanline()[j++] = (((sample & 0xFF000000)>>24)&0xFF); // A
}
//pngw.writeRow(line, /*imi.rows*/);
pngw.writeRow(line);
}
pngw.end();
}