如何根据方向元数据旋转JPEG图像?

hvg*_*des 61 java groovy image-processing

我有一些服务器代码在上传图像时生成缩略图.问题在于,当拍摄图像并旋转相机/设备时,即使在任何图像查看软件中以正确的方向显示全尺寸图像本身,也会旋转缩略图.这只发生在jpgs上.

在OSX上使用Preview,我可以看到jpgs中嵌入了方向元数据.当我使用ImageTools(Grails插件)生成缩略图时,EXIF元数据不在缩略图中,这就是缩略图显示为旋转的原因.

通过离线对话,我了解到虽然读取EXIF元数据相对容易,但没有简单的方法来编写它,这就是生成jpg缩略图时数据丢失的原因.

所以看来我有两个选择:

  1. 使用ImageMagick生成缩略图.缺点是它需要在我们的服务器上安装更多软件.
  2. 阅读EXIF Orientation数据是代码并适当旋转缩略图.

有没有人知道其他任何选择?

Ant*_*tin 59

如果您想旋转图片,我建议您使用元数据提取器库http://code.google.com/p/metadata-extractor/.您可以使用以下代码获取图像信息:

// Inner class containing image information
public static class ImageInformation {
    public final int orientation;
    public final int width;
    public final int height;

    public ImageInformation(int orientation, int width, int height) {
        this.orientation = orientation;
        this.width = width;
        this.height = height;
    }

    public String toString() {
        return String.format("%dx%d,%d", this.width, this.height, this.orientation);
    }
}


public static ImageInformation readImageInformation(File imageFile)  throws IOException, MetadataException, ImageProcessingException {
    Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
    Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    JpegDirectory jpegDirectory = metadata.getFirstDirectoryOfType(JpegDirectory.class);

    int orientation = 1;
    try {
        orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    } catch (MetadataException me) {
        logger.warn("Could not get orientation");
    }
    int width = jpegDirectory.getImageWidth();
    int height = jpegDirectory.getImageHeight();

    return new ImageInformation(orientation, width, height);
}
Run Code Online (Sandbox Code Playgroud)

然后,根据您检索的方向,您可以将图像旋转和/或翻转到正确的方向.EXIF方向的仿射变换由以下方法给出:

// Look at http://chunter.tistory.com/143 for information
public static AffineTransform getExifTransformation(ImageInformation info) {

    AffineTransform t = new AffineTransform();

    switch (info.orientation) {
    case 1:
        break;
    case 2: // Flip X
        t.scale(-1.0, 1.0);
        t.translate(-info.width, 0);
        break;
    case 3: // PI rotation 
        t.translate(info.width, info.height);
        t.rotate(Math.PI);
        break;
    case 4: // Flip Y
        t.scale(1.0, -1.0);
        t.translate(0, -info.height);
        break;
    case 5: // - PI/2 and Flip X
        t.rotate(-Math.PI / 2);
        t.scale(-1.0, 1.0);
        break;
    case 6: // -PI/2 and -width
        t.translate(info.height, 0);
        t.rotate(Math.PI / 2);
        break;
    case 7: // PI/2 and Flip
        t.scale(-1.0, 1.0);
        t.translate(-info.height, 0);
        t.translate(0, info.width);
        t.rotate(  3 * Math.PI / 2);
        break;
    case 8: // PI / 2
        t.translate(0, info.width);
        t.rotate(  3 * Math.PI / 2);
        break;
    }

    return t;
}
Run Code Online (Sandbox Code Playgroud)

图像的旋转将通过以下方法完成:

public static BufferedImage transformImage(BufferedImage image, AffineTransform transform) throws Exception {

    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);

    BufferedImage destinationImage = op.createCompatibleDestImage(image, (image.getType() == BufferedImage.TYPE_BYTE_GRAY) ? image.getColorModel() : null );
    Graphics2D g = destinationImage.createGraphics();
    g.setBackground(Color.WHITE);
    g.clearRect(0, 0, destinationImage.getWidth(), destinationImage.getHeight());
    destinationImage = op.filter(image, destinationImage);
    return destinationImage;
}
Run Code Online (Sandbox Code Playgroud)

在服务器环境中,不要忘记运行 -Djava.awt.headless=true

  • 执行变换方法后,图像的颜色已更改,为什么? (2认同)

dna*_*ult 15

Thumbnailator图书馆荣誉EXIF方向标志.要以正确的方向读取完整尺寸的图像:

BufferedImage image = Thumbnails.of(inputStream).scale(1).asBufferedImage();
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,缩略图旋转后的图像质量很差。https://github.com/coobird/thumbnailator/issues/101 (2认同)

Per*_*erg 7

通过使用JavaXT核心库图像部分,可以非常轻松地完成此操作:

// Browsers today can't handle images with Exif Orientation tag
Image image = new Image(uploadedFilename);
// Auto-rotate based on Exif Orientation tag, and remove all Exif tags
image.rotate(); 
image.saveAs(permanentFilename);
Run Code Online (Sandbox Code Playgroud)

而已!

我已经尝试过Apache Commons Imaging,但那是一团糟.JavaXT更优雅.

  • 另外,image.saveAs()使用内存映射文件,因此结果文件可以为空或在Windows中锁定。通过字节数组进行保存似乎更好。但是我还是要扔掉JavaXT。 (2认同)