Tag: image operations

  • 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.

  • Extract the GPS information of an image

    In one of my current project 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 the following blog entries I want to show a java implementation for some frequently uses image operations/tranfomrations. This time I want to go into detail how to extract the GPS information from an image, if exists.

    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;

    As already mentioned I wanted to use as few third part libraries as possible, but the Exif information of the image is a basic requirment which is needed to continue. For this task I decided to use the metadata-extractor java library, which I already used in another project, and I really like because of its simplicity.

    But now let us start with the implemenation of the operations:


    public static Metadata getImageMetadata(FileRecord image) throws ImageProcessingException, IOException {
    String typ = image.getFiletyp().split("/")[0]; // The file typ
    if(typ.equals("image")) {
    InputStream in = new ByteArrayInputStream(image.getFile());
    Metadata metadata = ImageMetadataReader.readMetadata(in);

    return metadata;
    } else
    return null;
    }

    public static String[] getGeoLocation(FileRecord image) throws ImageProcessingException, IOException {
    String coordinates[] = new String[3];
    Metadata metadata = getImageMetadata(image);
    if(metadata != null) {
    if (metadata.containsDirectoryOfType(GpsDirectory.class)) {
    GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    GpsDescriptor gpsDesc = new GpsDescriptor(gpsDir);

    // Set Latitude
    coordinates[0] = gpsDesc.getGpsLatitudeDescription();
    // Set Longitude
    coordinates[1] = gpsDesc.getGpsLongitudeDescription();
    // Set Altitude
    coordinates[2] = gpsDesc.getGpsAltitudeDescription();
    }
    }
    return coordinates;
    }

    These two methods uses the already mentioned metadata-extractor java library to extract the image metadata, from a given FileRecord if of type “image”. For more information about metada and the library please visit the git-hub page of the developer, the readme is quiet well written.