Buffer Image width and height inverted values randomly - java

I need to know if an image is landscape oriented or not, for that i´m getting the width and height of a BufferImage but sometimes the values appear as inverted.
This is the code I use to detect if an image is landscape:
public class ProcessImage{
private static final String IMAGE_ORIENTATION_ERROR ="001";
public static void main(String[] args) {
try {
File file = new File("C:\\myFolder\\nothing.jpg");
byte[] fileContent = Files.readAllBytes(file.toPath());
BufferedImage image = createImageFromBytes(fileContent);
validateImage(image);
System.out.println(image.getWidth());
} catch (IOException | ImageOrientationException e) {
e.printStackTrace();
}
}
private static BufferedImage createImageFromBytes(byte[] imageData) {
ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
try {
return ImageIO.read(bais);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/* Here is where i validate if the image is landscape
But Values of image.getWidth() and image.getHeight() appear as inverted.*/
private static void validateImage (BufferedImage image)
throws ImageOrientationException{
//if this happens the picture is NOT landscape
if (image.getWidth() < image.getHeight()) {
throw new ImageOrientationException(IMAGE_ORIENTATION_ERROR,
"error oriented picture");
}
}
}
The problem i'm having is that for some images the values of heigth and width are inverted .
For example, for an image which has a width of 3024 and a height of 4032 , the values of width and height the BufferImage returns are width: 4032 and height:3024. But if i edit the same image in Paint (respecting the original sizes) the BufferImage returns width:3024 and height: 4032 (As it should be).
I have tested with other images with the same width and height and the BufferImage gets the rigth values
Do you have any idea of why is this happening? Is there any way to know if the image is really landscape oriented?
Here is a link with the image which has the problem: http://www.mediafire.com/file/n1a8uhy195zgesd/nothing.jpg/file
Thanks in advance!

Related

Convert an ImageIcon to a Base64 String and back to an ImageIcon without saving to disk?

I'm trying to store an imageIcon bas a Base64 String.
This is what I have so far:
public ImageIcon getImageIcon() {
if(imageIcon == null || imageIcon.isEmpty()){
return null;
} else {
try {
byte[] btDataFile = Base64.decodeBase64(imageIcon);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(btDataFile));
return new ImageIcon(image);
} catch (IOException ex) {
System.out.println(ex.getLocalizedMessage());
return null;
}
}
}
public void setImageIcon(ImageIcon imageIconIn) {
imageIcon = Base64.encodeBase64String(imageToByteArray(imageIconIn));
}
public static byte[] imageToByteArray(ImageIcon imageIn) {
try {
BufferedImage image = new BufferedImage(imageIn.getIconWidth(), imageIn.getIconHeight(),BufferedImage.TYPE_INT_RGB);
ByteArrayOutputStream b = new ByteArrayOutputStream();
// FIX
Graphics g;
g = image.createGraphics();
imageIn.paintIcon(null, g, 0,0);
// END FIX
ImageIO.write(image, "jpg", b );
g.dispose();
return b.toByteArray();
} catch (IOException ex) {
System.out.println(ex.getLocalizedMessage());
return null;
}
}
I get a black rectangle instead of the image.
I'm using Java 1.8 on Ubuntu 16.04.
What am I doing wrong?
Thanks for your help.
******************************** . FIXED . ******************************
I found a working solution and updated the above code.
******************************** EDIT *********************************
Added g.dispose() after painting icon.
This code creates a brand new BufferedImage, with width & height same as the given image.
BufferedImage image = new BufferedImage(
imageIn.getIconWidth(),
imageIn.getIconHeight(),
BufferedImage.TYPE_INT_RGB);
Note that the image is empty. No content has been written to it. The bytes will all be zero, and RGB 0x000000 is black.
Then, you are writing the bytes of this black image to your ByteArrayOutputStream.
ByteArrayOutputStream b = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", b );
return b.toByteArray();
Of course, when you convert that byte buffer back to an image, it will be black.
You will want to draw/copy imageIn into your new image before you write out the bytes.
But if you don't mind using whatever the current image's format is, you could just write out that image instead of converting it to TYPE_INT_RGB...
Image image = imageIn.getImage();
// write image to ByteArrayOutputStream

Image getWidth and getHeight returning -1 inappropriately

Why does this:
URL url = MinecraftPlatformGame.class.getResource("images/diamondPick.png");
image = Toolkit.getDefaultToolkit().getImage(url);
int width = image.getWidth(null);
int height = image.getHeight(null);
System.out.println(width);
System.out.println(height);
Return -1 for both the width and the height
Edit: My question before I figured out the answer was actually how I was supposed to fix it.
I fixed it by doing the following:
URL url = MinecraftPlatformGame.class.getResource("images/diamondPick.png");
image = Toolkit.getDefaultToolkit().getImage(url);
MediaTracker mTracker = new MediaTracker(this);
mTracker.addImage(image,1);
try {
mTracker.waitForID(1);
} catch (InterruptedException ex) {
Logger.getLogger(MinecraftPlatformGame.class.getName()).log(Level.SEVERE, null, ex);
}
int width = image.getWidth(null);
int height = image.getHeight(null);
System.out.println(width);
System.out.println(height);
url = MinecraftPlatformGame.class.getResource("images/gui.png");
image1 = Toolkit.getDefaultToolkit().getImage(url);
mTracker.addImage(image1,2);
try {
mTracker.waitForID(2);
} catch (InterruptedException ex) {
Logger.getLogger(MinecraftPlatformGame.class.getName()).log(Level.SEVERE, null, ex);
}
width = image1.getWidth(null);
height = image1.getHeight(null);
System.out.println(width);
System.out.println(height);
Problem I have now is this doesn't seem very efficient nor does it seem like I should need so much code just for two images to be imported and given sizes. Is there a better more efficient and easier way to do this?
Per the Javadoc:
If the width is not yet known, this method returns -1 and the
specified ImageObserver object is notified later.
In response to your edit, and following Russell's comment, use ImageIO.read() instead to get a fully-loaded image.

My batch jpg resizer works with color images, but grayscale ones become washed out

I've been having a problem with my Java program. It's for resizing images. You drop it into a folder and run it, and it creates a new folder with the resized images. It works great on color, but it has a problem with grayscale. The images are converted, but they become lighter and more washed out, as if someone has messed with the curves or levels. All the input files and output files are sRGB color space jpegs, saved in RGB color mode. I have thousands of 50 megapixel film scans I'm trying to convert down to 15 megapixels or less. Any help or ideas anyone could offer would be most appreciated. The programs full code is below, it's about 130 lines. I have a feeling the problem may be in the toBufferedImage function but I'm lost as to what it could be.
package jpegresize;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
System.out.println("JPEGResize running . . .");
int max_side = 4096;
float quality = 0.9f;
if(args.length == 0) System.out.println("No maximum side resolution or compression quality arguments given, using default values.\nUsage: java -jar JPEGResize.jar <maximum side resolution in pixels> <quality 0 to 100 percent>");
if(args.length >= 1) max_side = Integer.parseInt(args[0]);
if(args.length >= 2) quality = Float.parseFloat(args[1]) / 100.0f;
System.out.println("Maximum side resolution: " + max_side);
System.out.println("Compression quality: " + (quality * 100) + "%");
File folder = new File(".");
File[] listOfFiles = folder.listFiles(new JPEGFilter());
for(int i = 0; i < listOfFiles.length; i++) {
System.out.println("Processing " + listOfFiles[i].getName() + " . . .");
resizeFile(listOfFiles[i].getName(), max_side, quality);
System.out.println("Saved /resized/" + listOfFiles[i].getName());
}
System.out.println("Operations complete.");
}
public static void resizeFile(String filename, int max_side, float quality) {
try
{
BufferedImage input_img = ImageIO.read(new File(filename));
double aspect_ratio = ((double)input_img.getWidth()) / ((double)input_img.getHeight());
int width, height;
if(input_img.getWidth() >= input_img.getHeight()) {
width = max_side;
height = (int)(((double)max_side) / aspect_ratio);
}
else {
width = (int)(((double)max_side) * aspect_ratio);
height = max_side;
}
Image scaled_img = input_img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage output_img = toBufferedImage(scaled_img);
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(quality);
File doesDirExist = new File("resized/");
if(!doesDirExist.exists())
new File("resized").mkdir();
File file = new File("resized/" + filename);
FileImageOutputStream output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage image = new IIOImage(output_img, null, null);
writer.write(null, image, iwp);
writer.dispose();
}
catch (IOException e)
{
e.printStackTrace();
}
}
// This method returns a buffered image with the contents of an image
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}
// This code ensures that all the pixels in the image are loaded
image = new ImageIcon(image).getImage();
// Create a buffered image with a format that's compatible with the screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
// Determine the type of transparency of the new buffered image
int transparency = Transparency.OPAQUE;
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
// The system does not have a screen
}
if (bimage == null) {
// Create a buffered image using the default color model
int type = BufferedImage.TYPE_INT_RGB;
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
// Copy image to buffered image
Graphics g = bimage.createGraphics();
// Paint the image onto the buffered image
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
}
class JPEGFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".jpg")) || (name.toLowerCase().endsWith(".jpeg"));
}
}
If jdk's classes and methods are buggy, report the bug to oracle (oh! I wish I could go on saying to SUN..).
And, while the next release will correct the bug ;), try some work arounds, scaling image by yourself like proposed here.
Regards,
Stéphane
In your code, you assume jpeg are encoded in RGB, but that's not always the case. It's also possible to encode 8 bit gray scaled jpeg. So I suggest that you try this when building your BufferedImage, replace :
BufferedImage.TYPE_INT_RGB;
by
BufferedImage.TYPE_BYTE_GRAY;
and see if it works for those images.
If so, then you still have to find out a way to determine the encoding type to automatically change the type of BufferedImage color encoding to use, but you will be one stop closer.
Regards,
Stéphane

PDF to Image conversion [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Convert pdf file to jpg asp.net
public class Pdf2Image {
private Image image;
int length;
public int convertPdf2Image(String pdfname) {
File file = new File(pdfname);
RandomAccessFile raf;
try {
raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
PDFFile pdffile = new PDFFile(buf);
// draw the first page to an image
int num = pdffile.getNumPages();
length=num;
for (int i = 0; i <= num; i++) {
PDFPage page = pdffile.getPage(i);
//get the width and height for the doc at the default zoom
int width = (int) page.getBBox().getWidth();
int height = (int) page.getBBox().getHeight();
Rectangle rect = new Rectangle(0, 0, width, height);
int rotation = page.getRotation();
Rectangle rect1 = rect;
if (rotation == 90 || rotation == 270) {
rect1 = new Rectangle(0, 0, rect.height, rect.width);
}
//generate the image
BufferedImage img = (BufferedImage) page.getImage(
rect.width, rect.height, //width & height
rect1, // clip rect
null, // null for the ImageObserver
true, // fill background with white
true // block until drawing is done
);
ImageIO.write(img, "png", new File("src\\downloadedFiles\\aa" + i + ".png"));
}
} catch (FileNotFoundException e1) {
System.err.println(e1.getLocalizedMessage());
} catch (IOException e) {
System.err.println(e.getLocalizedMessage());
}
return length;
}
public static void main(String[] args) {
Pdf2Image p = new Pdf2Image();
p.convertPdf2Image("src\\downloadedFiles\\todaypdf.pdf");
}
}
I am using this code to convert PDF file to image. It is working fine for most of the PDF's but showing exception for a PDF file. Exception is:
Expected 'xref' at start of table.
Could any one tell me why it is giving such an exception?
There are many malformed PDF files out in the wild and this is most likely one of them.
It is not possible to give a definite answer until seeing the problem PDF file. What I am guessing is that the 'startxref' specifies an absolute position into the PDF where the xref table should be located. The java library is jumping to this position on the file expecting to find the word 'xref' but cannot find it.
http://blog.amyuni.com/?p=1627
One way to fix this would be to load the file into the full version of Acrobat and then save the file. Acrobat will fix the xref offset as mentioned in the link.
There are quite large companies out there generating malformed PDF's that should know better. Adobe lets these files exist because it makes it hard for their PDF competitors to keep up and compete.

Merge small images into one without allocating full image in memory

I've to make a parallel image processing script in java, the idea is to divide the images into tiles of any size, process them, and reassemble the final image.
For now i've created a function:
public static BufferedImage readImg (String path, int startx, int starty, int w, int h)
that returns the region of an image as BufferedImage, then i'll process it and i want to place that region in the correct position of the final image.
So i've tried to make a function writeImg that uses replacePixels method to write just in the correct position without loading the whole image into memory:
public static void writeImg (String path, int startx, int starty, BufferedImage image){
File output = new File(path);
ImageOutputStream ios = null;
try {
ios = ImageIO.createImageOutputStream(output);
} catch (IOException e){
e.printStackTrace();
}
Iterator iter = ImageIO.getImageWritersByFormatName("JPEG");
ImageWriter writer = (ImageWriter)iter.next();
writer.setOutput(ios);
try{
if(writer.canReplacePixels(0)){
System.out.println("True");
}else{
System.out.println("False");
}
}catch (IOException e) {
e.printStackTrace();
}
ImageWriteParam param = writer.getDefaultWriteParam();
Point destinationOffset = new Point(startx,starty);
param.setDestinationOffset(destinationOffset);
try {
writer.replacePixels(image, param);
} catch (IOException e) {
e.printStackTrace();
}
}
The problem is that canReplacePixels is always set as false, and i've no idea what should i use to do that.
The images can be very big so it's impossible to load the whole image in memory as it will cause a OutOfMemory exception.
As long as you are fine with an 24 bit PNG file as output I have a working solution for you (under GPL license):
The class PngXxlWriter allows to write PNG files "line by line". That means that you can write an image of 10000x10000 (width * height) pixels in lines of e.g. 256 pixels (10000 * 256).
Usually this reduces the memory usage down to a level which is practically.
All required classes can be found here:
PngXxlWriter is the main class. By calling its method writeTileLine you can add a new line to the output image.
https://sourceforge.net/p/mobac/code/HEAD/tree/trunk/MOBAC/src/main/java/mobac/utilities/imageio/

Categories

Resources