Checking the current orientation of the image and rotate if necessary

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:

  1. 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.
  2. Next we need the dimension of the image using the getImageSize(FileRecord image) method.
  3. 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.

Print Friendly, PDF & Email

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *