I need to open an .hdr file and work on it, but imageIO doesn't supports that format.
The problem is that I need to keep the information loss as little as possible: 32bpc is perfect, 16 is fine and less the 16 won't work.
There are 3 possible solutions I came up to:
Find a plugin that allow me to open .HDR file. I've been searching for it a lot but without luck;
Find a way to convert the .HDR file to a format I can find a plugin for. Tiff maybe? Tried this too but still no luck;
Reduce the dynamic range from 32bpc to 16bpc and then convert it to png. This is tricky because once I have a png file I win, but it's not that easy to cut the range without killing the image..
What would you recommend me to do? Do you know a way to make one of those 3 options works? Or do you have a better idea?
You can now read .HDR using ImageIO. :-)
This is a first version, so it might be a little rough around the edges, but should work for standard (default settings) Radiance RGBE .HDR files.
The returned image will be a custom BufferedImage with a DataBufferFloat backing (ie., samples will be in 3 samples, 32-bit float interleaved RGB format).
By default, a simple global tone-mapping is applied, and all RGB values will be normalized to range [0...1] (this allows anyone to just use ImageIO.read(hdrFile) and the image will look somewhat reasonable, in a very reasonable time).
It is also possible to pass an HDRImageReadParam to the ImageReader instance with a NullToneMapper. This is even faster, but the float values will be unnormalized, and might exceed the max value. This allows you to do custom, more sophisticated tone-mapping on the image data, before converting to something more displayable.
Something like:
// Create input stream
ImageInputStream input = ImageIO.createImageInputStream(hdrFile);
try {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + hdrFile);
}
ImageReader reader = readers.next();
try {
reader.setInput(input);
// Disable default tone mapping
HDRImageReadParam param = (HDRImageReadParam) reader.getDefaultReadParam();
param.setToneMapper(new NullToneMapper());
// Read the image, using settings from param
BufferedImage image = reader.read(0, param);
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
}
finally {
// Close stream in finally block to avoid resource leaks
input.close();
}
// Get float data
float[] rgb = ((DataBufferFloat) image.getRaster().getDataBuffer()).getData();
// TODO: Custom tone mapping on float RGB data
// Convert the image to something easily displayable
BufferedImage converted = new ColorConvertOp(null).filter(image, new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB));
// Optionally write as JPEG or other format
ImageIO.write(converted, "JPEG", new File(...));
Related
I am using the Tesseract Java API (tess4J) to convert Tiff images to PDFs.
This works nicely, but I am forced to write both the source Tiff image and the output PDF to local filestore as actual physical files in order to use the TessAPI1.TessPDFRendererCreate API.
Please note the following in the code snippet below: -
The input Tiff is originally a java.awt.image.BufferedImage, but I have to write it to a physical file (sourceTiffFile is a File object).
I must specify a file path for the output (pdfFullFilepath is a String representing an absolute path for the new PDF file).
try {
ImageIO.write(bufferedImage, "tiff", sourceTiffFile);
} catch (Exception ioe) {
//handling code...
}
TessResultRenderer renderer = TessAPI1.TessPDFRendererCreate(pdfFullFilepath, dataPath, 0);
TessAPI1.TessResultRendererInsert(renderer, TessAPI1.TessPDFRendererCreate(pdfFullFilepath, dataPath, 0));
int result = TessAPI1.TessBaseAPIProcessPages(handle, sourceTiffFile.getAbsolutePath(), null, 0, renderer);
I would really like to avoid creating physical files, but am not sure if it is possible with this API. Ideally, I would like to pass the Tiff as a java.awt.image.BufferedImage or a byte array and receive the output PDF as a byte array.
Any suggestions would be most welcome as always. Thank you :)
You can pass in ProcessPage API method a Pix, which can be converted from a BufferedImage, but the output will still be a physical file. Tesseract API dictates that.
https://tesseract-ocr.github.io/tessapi/4.0.0/a01625.html
http://tess4j.sourceforge.net/docs/docs-4.4/net/sourceforge/tess4j/TessAPI1.html
For ex:
int result = TessAPI1.TessBaseAPIProcessPage(handle, LeptUtils.convertImageToPix(bufferedImage), page_index, "input file name", null, 0, renderer);
I am using javax.imageio.ImageIO.read() which almost takes 9 seconds to read an image having size 5 mb and located at windows temp location, PFB the screenshot of Jprofiler. i want a more efficient way which can decrease the time to at least 2-3 seconds.
The file is coming as a org.springframework.web.multipart.MultipartFile request through rest endpoint and then getting copied to the windows temp location for further proccessing. The complete code block:
String fileName = StringUtils.cleanPath(multipartFile.getOriginalFilename());
Path destinationPath = Paths.get(System.getProperty("java.io.tmpdir"));
String tempPath = destinationPath.resolve(fileName).toString();
File uploadedImageFile = new File(tempPath);
File originalFileInTempLocation = file.transferTo(uploadedImageFile);
BufferedImage originalImage = ImageIO.read(originalFileInTempLocation);
While googling i found a 3rd party library which claims to be more efficient than javax.imageio.ImageIO but its a paid one: https://files.idrsolutions.com/maven/site/jdeli/apidocs/com/idrsolutions/image/JDeli.html#read(java.io.File).
Can anyone suggest a better and efficient way to read the image file.
Your time of 9 seconds looks bad - I wonder if you've included all code and timing of just ImageIO.read(File)?
However I found that ImageIO.read(File) quite slow on non-SSD drives (ie 500ms to 1 second). When testing NAS and HDD drives on my PC 4-6MB some image reads used InputStream.read() up to 1000 times. I found that that elapsed time for loads are reduced if entire image was read into memory before calling ImageIO.read:
static BufferedImage load(File f) throws IOException
{
byte[] bytes = Files.readAllBytes(f.toPath());
try (InputStream is = new ByteArrayInputStream(bytes))
{
return ImageIO.read(is);
}
}
This solution can slow down access time on SSD drives a little (which was not an issue for me at ~ 20ms), but the margin of gain on NAS/network drive was 50-250ms on my PC.
Obviously the timings you observe would depend on your hardware, image file size and types but it may be worth trying in your particular case.
If you need to load several images you can split the file I/O and ImageIO calls to separate threads for a small extra gain, but at cost of extra complexity and memory footprint.
First of all, I have to say that 9 seconds for a 5Mb image seem too much for modern hardware. Sure, complex images or sophisticated formats (i.e. multi-page tiff) can take longer but my low-end PC needs 1.3 seconds for a 5mb JPG. I would definitely try to identify hardware and/or OS level issues.
Having that said, here is my proposed solution for faster image loading since the use case is to generate thumbnails. The key point here is that we can instruct the image reader to only read part of the image from disk, since we need less image data to generate a thumbnail. This technique (sub-sampling) can be realized for JPG as:
Iterator<ImageReader> imageReadersByFormatName = ImageIO.getImageReadersByFormatName("jpg");
ImageReader ir = imageReadersByFormatName.next(); //There should be at least the default com.sun.imageio.plugins.jpeg.JPEGImageReader installed
ImageReadParam defaultReadParam = ir.getDefaultReadParam();
defaultReadParam.setSourceSubsampling(8, 8, 0, 0); //Configure the reader to read every 8th row / 8th column of the image
ir.setInput(ImageIO.createImageInputStream(new FileInputStream(IMAGE_PATH)), true);
BufferedImage image = ir.read(0, defaultReadParam);
You will have to determine the optimal values for X-axis and Y-axis sub-sampling (first two parameters to setSourceSubsampling()). This will be a speed/image quality trade-off.
There are more configuration options for ImageReadParam.
Bonus material: Since you are using AWS, you might consider this approach in order to delegate this task to a lambda function (i.e. if this task can be done asynchronously).
I would recommend trying to use classes from the javax.imageio.stream package:
if you want to keep your solution with the temp file:
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
FileCacheImageInputStream fileCache = new FileCacheImageInputStream​(fileInputStream, new File(tempPath));
BufferedImage bufferedImage = ImageIO.read(fileCache);
if you have enough memory you can use solution with the memory cache:
FileInputStream fileInputStream = new FileInputStream(uploadedImageFile);
MemoryCacheImageInputStream memoryCache = new MemoryCacheImageInputStream(fileInputStream);
BufferedImage bufferedImage = ImageIO.read(memoryCache);
In JPEG images, EXIF metadata is sometimes included and tells in what orientation the image should be shown.
The question is, whether Java's ImageIO.read() takes EXIF into account while reading a JPEG image, and automatically applies the transformation.
More concretely, if I use Java's ImageIO for converting a JPEG image with EXIF into a PNG image, is the orientation of the PNG image going to be correct? Or is the below code going to produce a PNG image without taking EXIF orientation instructions into account?
private byte[] convertToPng(byte[] imageFileAsByteArray) {
ByteArrayInputStream bis = new ByteArrayInputStream(imageFileAsByteArray);
BufferedImage bi = ImageIO.read(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(bi, "png", bos);
return bos.toByteArray();
}
The short answer is, unfortunately, no; ImageIO.read(..) (by default) does not take the Exif Orientation tag into account. Your PNG will be not be rotated accordingly.
However, all ImageIO.read(..) does internally, is to look up an appropriate ImageReader plug-in for the input, and delegate reading to it. And while the JRE-bundled plug-in does not take Exif into account, it is possible to add support for it, in a third-party ImageReader. Unfortunately, I don't know of any that do, but I am considering adding Exif orientation support to the TwelveMonkeys ImageIO JPEGImageReader in the future.
In the mean time, you have to apply the rotation yourself, by reading the metadata and rotating. The value of the orientation tag can be acquired either using the ImageIO JPEG metadata or some third-party library. I think both the mentioned metadata-extractor or TwelveMonkeys ImageIO can be used for this purpose. JAI ImageIO (using the TIFF metadata) can probably also do this.
If using the ImageIO JPEG metadata, be aware:
Note that an application wishing to interpret Exif metadata given a metadata tree structure in the javax_imageio_jpeg_image_1.0 format must check for an unknown marker segment with a tag indicating an APP1 marker and containing data identifying it as an Exif marker segment.
I have struggled with jpeg exif orientation for awhile, and now that I set out to fix my problems, I find that they are gone. To make sure for myself, I simplified OP.s example so as to read
public static void main (String [] args) throws Exception {
java.io.File in = new java.io.File ("in.jpg");
java.io.File out = new java.io.File ("out.png");
BufferedImage image = ImageIO.read (new FileInputStream(in));
ImageIO.write (image, "png", out);
}
in.jpg is a picture with exif orientation tag 8, tilted to the left. When I run the example, out.png is created as a properly oriented png picture. So it seems that ImageIo has learned something recently.
I need to display the 3rd page of scanned tiff files. i used the code
TIFFReader reader = new TIFFReader(new File(pathOfFile));
RenderedImage image = reader.getPage(2);
its sometimes work. and show error : Decoding of old style JPEG-in-TIFF data is not supported.
I used aspriseTIFF.jar
then how i solve this problem.
please reply.
thanks in advance
The problem you have run into is that "old style" JPEG compression in the TIFF format (compression == 6), is not supported in the library you use.
This is quite common I guess, as "old-style" JPEG compression is deprecated in TIFF, because it was never fully specified. And because of this under-specification, various vendors implemented it in different, incompatible ways. Support was dropped in favor for TIFF compression 7, JPEG.
Unfortunately, old TIFF files using this compression still exists, so you need to find another library. The good news is that you can use ImageIO and a proper plug-in.
Using a TIFF ImageReader plug-in, like the one from my TwelveMonkeys ImageIO open source project, you should be able to do this:
// Create input stream
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
ImageReader reader = ImageIO.getImageReaders(input).next();
try {
reader.setInput(input);
// Read page 2 of the TIFF file
BufferedImage image = reader.read(2, null);
}
finally {
reader.dispose();
}
}
(sorry about the try/finally boiler-plate, but it is important to avoid resource/memory leaks).
I am currently working with Image processing in Java. Initially I used ImageIO class to write images
ImageIO.write(image,"jpg",os);
the problem with this method is am lossing the actual image size and quality. Then I preferred ByteStream
Files.readAllBytes(fi.toPath());
to read and
fos.write(fileContent);
to write Images. This works perfectly. The issue I am facing here is I can read only files but not Images(ie, BuffreredImage image). Is it possible to read a Image rather than files here or should I move to someother IO?
Code Snippet is here,
try {
File fnew=new File("d:\\3\\IMG1.jpg");
java.io.FileOutputStream fos = new java.io.FileOutputStream(new File("d:\\3\\Test1\\4.jpg"));
File fi = new File("d:\\3\\7.jpg");
byte[] fileContent = Files.readAllBytes(fi.toPath());
fos.write(fileContent);
} catch (Exception e) {
System.out.println("Exception");
}
Any Kind of suggestions or help will be appreciated. Thanks in Advance.
the problem with this method is am lossing the actual image size and quality. Then I preferred ByteStream
When you read a JPEG with with ImageIO, it is converting the JPEG to a Bitmap automatically. Then when you write it, it is encoding to a JPEG again (which loses quality).
Just replace ImageIO.write(image,"jpg",os) with ImageIO.write(image,"png",os) and you are done. A lossless format such as PNG will not lose any data when you write the image.
BufferedImage getRGB() will get you all the actual pixel data for the image. There will be no compression or anything like JPEG. It will be the raw image.
Edited to add an example based on my comments...
BufferedImage image = ImageIO.read(new File("google.jpg"));
ImageWriter w = ImageIO.getImageWritersBySuffix("jpg").next();
ImageOutputStream out = ImageIO.createImageOutputStream(new File("output.jpg"));
w.setOutput(out);
ImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault());
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(1);
w.write(null, new IIOImage(image, null, null), param);
out.close();