How can I rotate TIFF CMYK image in ImageIO or TwelveMonkeys?

122 views Asked by At

I need to rotate my TIFF image with color space CMYK. Standard Java ImageIO doesn't support CMYK TIFF images so I used TwelveMonkeys plugin. But it didn't help. When I tried to rotate my image I faced with an exception.

I do the following:

try (InputStream is = new ByteArrayInputStream(bytes);
        ImageInputStream iis = ImageIO.createImageInputStream(is)) {
      Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);

      if (!iter.hasNext()) {
        throw new RuntimeException("Image type is not supported");
      }

      ImageReader reader = iter.next();
      BufferedImage bi;
      try {
        reader.setInput(iis);
        bi = reader.read(0);
      } finally {
        reader.dispose();
      }
      int type = bi.getType();
      BufferedImage newBi;
      if (angle == 90 || angle == 270) {
        newBi= new BufferedImage(height, width, type);
      } else {
        newBi= new BufferedImage(width, height, type);
      }
      //writing the image content to new buffered image
}

But it throws java.lang.IllegalArgumentException: Unknown image type 0. How can I create a BufferedImage for TIFF CMYK image? Or at least how can I rotate TIFF CMYK image?

2

There are 2 answers

2
Harald K On BEST ANSWER

To rotate a BufferedImage in Java, the normal way would be to use AffineTransformOp. This will allow you to rotate any image without having to create a new image of the same type up front.

For an example see the TIFFUtilities.applyOrientation method in TwelveMonkeys "contrib" module. You can also just use this method directly, if you like. Just beware the input is one of the TIFF orientation tag constant values, not an angle:

Ie. to rotate 90 degrees (assuming static imports for readability):

BufferedImage original;

BufferedImage rotated = applyOrientation(original, ORIENTATION_RIGHTTOP); // 6

For the more general topic "How can I create a BufferedImage for a TIFF CMYK image", the quick answer is use the constructor that takes a WritableRaster and a ColorModel as argument like this:

BufferedImage original;

ColorModel cm = original.getColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(newWidth, newHeight);
BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);

You have to use this constructor, as any CMYK BufferedImage has to be TYPE_CUSTOM (the constant value 0). And the BufferedImage constructor taking a type argument does not allow TYPE_CUSTOM (after all, which of the countless possible custom types would that be?).

1
vatbub On

I don't know how you would read a CMYK Tiff image with ImageIO, but here's how you could read and rotate the image using OpenCV:

Note that you would need this dependency in your project.

// Since OpenCV is a native (C++) library, it needs to be loaded before it can be used. Only do this once in your application.
OpenCV.loadLocally();

MatOfByte inputBuffer = new MatOfByte(bytes);
Mat inputImage;

try {
    inputImage = Imgcodecs.imdecode(inputBuffer, Imgcodecs.IMREAD_UNCHANGED);
} finally {
    inputBuffer.release();
}

Mat rotatedImage;

try {
    if (angle == 0) {
        // Copies the image by reference as we will release the input image in the finally-block
        rotatedImage = new Mat(inputImage, new Rect(0, 0, inputImage.cols(), inputImage.rows()));
    } else {
        int rotateCode = Core.ROTATE_180;

        if (angle == 90) rotateCode = Core.ROTATE_90_CLOCKWISE;
        else if (angle == 270) rotateCode = Core.ROTATE_90_COUNTERCLOCKWISE;

        rotatedImage = new Mat();
        Core.rotate(inputImage, rotatedImage, rotateCode);
    }
} finally {
    inputImage.release();
}

// Convert the image to a BufferedImage
MatOfByte outputBuffer = new MatOfByte();
BufferedImage bufferedImage;
try {
    Imgcodecs.imencode(".png", rotatedImage, outputBuffer);
    bufferedImage = ImageIO.read(new ByteArrayInputStream(outputBuffer.toArray()));
} catch (IOException e) {
    throw new RuntimeException(e);
} finally {
    outputBuffer.release();
}

Also, note that the conversion in the last step to a BufferedImage uses the png file-format in this example. You can probably safely change it to Tiff, if you have the TwelveMonkeys plugin installed.

Furthermore, Imgcodecs.imdecode may change the color space from CMYK to BGR (relevant documentation). Since I don't have a test picture available, you would need to test that.