In one of my current projects I need some basic image operations and want to use as few third part libraries as possible. Especially I do not want to be dependent from server side libraries like ImageMagick.
In this entry, I want to explain an java implementation which can be used to automatically checks the orientation of an image and correct, if necessary.
Every image stores several metadata like the date, time, current GPS position and the orientation. This information can be used to check if the image has the right orientation. Especially for images from smartphones this is very important.
Before continuing it should be noted, that this project stores all files in the database, for this, I use a FileRecord class which consists of the following attributes:
private long pid; private Date creationDate; private String filename; private String filetyp; private int size; private byte[] file; private String hash; private boolean deleted;
First I want to reference to the following stackoverflow discussion, where I got the basic knowledge and several parts of my code. To get more information about EXIF orientation have a look at this entry.
public static void correctOrientation(FileRecord image) throws ImageProcessingException, IOException, MetadataException { String typ = image.getFiletyp().split("/")[0]; // The file typ if(typ.equals("image")) { Metadata metadata = getImageMetadata(image); if(metadata != null) { if(metadata.containsDirectoryOfType(ExifIFD0Directory.class)) { // Get the current orientation of the image Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); int orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); // Get the current width and height of the image int[] imageSize = getImageSize(image); int width = imageSize[0]; int height = imageSize[1]; // Determine which correction is needed AffineTransform t = new AffineTransform(); switch(orientation) { case 1: // no correction necessary skip and return the image break; case 2: // Flip X t.scale(-1.0, 1.0); t.translate(-width, 0); transform(image, t); break; case 3: // PI rotation t.translate(width, height); t.rotate(Math.PI); transform(image, t); break; case 4: // Flip Y t.scale(1.0, -1.0); t.translate(0, -height); transform(image, t); break; case 5: // - PI/2 and Flip X t.rotate(-Math.PI / 2); t.scale(-1.0, 1.0); transform(image, t); break; case 6: // -PI/2 and -width t.translate(height, 0); t.rotate(Math.PI / 2); transform(image, t); break; case 7: // PI/2 and Flip t.scale(-1.0, 1.0); t.translate(height, 0); t.translate(0, width); t.rotate( 3 * Math.PI / 2); transform(image, t); break; case 8: // PI / 2 t.translate(0, width); t.rotate( 3 * Math.PI / 2); transform(image, t); break; } } } } } private static void transform(FileRecord image, AffineTransform transform) throws IOException { String typ = image.getFiletyp().split("/")[0]; // The file typ if(typ.equals("image")) { // Create a buffered image of the current file InputStream in = new ByteArrayInputStream(image.getFile()); BufferedImage bimage = ImageIO.read(in); // Create an transformation operation AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC); // Create an instance of the resulting image, with the same width, height and image type than the referenced one BufferedImage destinationImage = new BufferedImage( bimage.getWidth(), bimage.getHeight(), bimage.getType() ); op.filter(bimage, destinationImage); // Set the created image as new buffered image image.setFile(getByteArray(destinationImage, image.getFilename())); } } public static int[] getImageSize(FileRecord image) throws IOException { String typ = image.getFiletyp().split("/")[0]; // The file typ if(typ.equals("image")) { InputStream in = new ByteArrayInputStream(image.getFile()); BufferedImage bimg = ImageIO.read(in); int imageSize[] = {bimg.getWidth(), bimg.getHeight()}; log.debug("Image size: "+bimg.getWidth()+"x"+bimg.getHeight()); return imageSize; } return null; } private static byte[] getByteArray(BufferedImage image, String filename) throws IOException { String formatName = "JPEG"; if(filename.contains(".")) { String[] split = filename.split("\\."); formatName = split[split.length-1]; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write( image, formatName, baos ); baos.flush(); byte[] imageInByte = baos.toByteArray(); baos.close(); return imageInByte; } public static int[] getImageSize(FileRecord image) throws IOException { String typ = image.getFiletyp().split("/")[0]; // The file typ if(typ.equals("image")) { InputStream in = new ByteArrayInputStream(image.getFile()); BufferedImage bimg = ImageIO.read(in); int imageSize[] = {bimg.getWidth(), bimg.getHeight()}; log.debug("Image size: "+bimg.getWidth()+"x"+bimg.getHeight()); return imageSize; } return null; }
Okay, let’s have a deeper look on the code above. As you can see we perform the image orientation correction in several steps:
- We extract the information about the current orientation of the image using the metadata-exctractor library. The current orientation of the image is stored as an integer value. See here, for further information.
- Next we need the dimension of the image using the getImageSize(FileRecord image) method.
- Now we use the AffineTransform class from the java.awt.geom package to set the required rotation. If the current orientation value is 1 we do not to need to perform an operation. For each other value we do need and for this we use the transform(FileRecord image, AffineTransform transform) method. This method creates a new BufferedImage and performs the right AffineTransformOp operation on it, which corrects the orientation. In the last line of the transform(FileRecord image, AffineTransform transform) method we store the resulted image as byte array in the FileRecord object.
That´s it, now our image has the right orientation.
I appreciate, cause I found exactly what I was looking for. You have ended my 4 day long hunt! God Bless you man. Have a nice day. Bye