如何确定和自动旋转图像?

时间:2014-02-22 08:39:10

标签: java image image-processing image-rotation

我有一堆图像,其中一些图像必须旋转。

样品:

enter image description here

我想将此图像逆时针旋转90°。

我用Google搜索知道如何旋转图像并找到许多链接和SO线程。但是,如何确定图像是否需要旋转? Picasa具有自动旋转功能。我希望有类似的功能。

任何指针对我都非常有用。

我找到了link,但它与Android有关。

3 个答案:

答案 0 :(得分:17)

metadata-extractor提供的Roger Rowland指针解决了这个问题。我将其发布在此处以供将来参考:

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.jpeg.JpegDirectory;

public class Main {

    private static String inFilePath = "C:\\Users\\TapasB\\Desktop\\MHIS031522.jpg";
    private static String outFilePath = "C:\\Users\\TapasB\\Desktop\\MHIS031522-rotated.jpg";

    public static void main(String[] args) throws Exception {
        File imageFile = new File(inFilePath);
        BufferedImage originalImage = ImageIO.read(imageFile);

        Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
        ExifIFD0Directory exifIFD0Directory = metadata.getDirectory(ExifIFD0Directory.class);
        JpegDirectory jpegDirectory = (JpegDirectory) metadata.getDirectory(JpegDirectory.class);

        int orientation = 1;
        try {
            orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        int width = jpegDirectory.getImageWidth();
        int height = jpegDirectory.getImageHeight();

        AffineTransform affineTransform = new AffineTransform();

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

        AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_BILINEAR);  
        BufferedImage destinationImage = new BufferedImage(originalImage.getHeight(), originalImage.getWidth(), originalImage.getType());
        destinationImage = affineTransformOp.filter(originalImage, destinationImage);
        ImageIO.write(destinationImage, "jpg", new File(outFilePath));
    }
}

答案 1 :(得分:5)

我遇到了一些让一些开关案例工作的问题。即使没有旋转,AffineTransform也会在图像中创建一个带有黑色空间的新图像,并会切掉一些尺寸。小猪退出接受的答案,我使用元数据提取器类来确定方向应该是什么。然后我使用Imgscalr库进行缩放和旋转。

下面可以看到对我有用的完整解决方案。感谢Tapas Bose原始解决方案。我希望这对任何人都有帮助!

BufferedImage originalImage = Utils.prepareBufferedImage(fileUpload.getFile_data(), fileUpload.getFile_type());
                    BufferedImage scaledImg = Scalr.resize(originalImage, 200);

                    // ---- Begin orientation handling ----
                    Metadata metadata = ImageMetadataReader.readMetadata(fileUpload.getFile_data());
                    ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);

                    int orientation = Integer.parseInt(id);
                    try {
                        orientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
                    } catch (Exception ex) {
                        logger.debug("No EXIF information found for image: " + fileUpload.getFile_name());
                    }

                    switch (orientation) {
                    case 1:
                        break;
                    case 2: // Flip X
                        scaledImg = Scalr.rotate(scaledImg, Rotation.FLIP_HORZ);
                        break;
                    case 3: // PI rotation
                        scaledImg = Scalr.rotate(scaledImg, Rotation.CW_180);
                        break;
                    case 4: // Flip Y
                        scaledImg = Scalr.rotate(scaledImg, Rotation.FLIP_VERT);
                        break;
                    case 5: // - PI/2 and Flip X
                        scaledImg = Scalr.rotate(scaledImg, Rotation.CW_90);
                        scaledImg = Scalr.rotate(scaledImg, Rotation.FLIP_HORZ);
                        break;
                    case 6: // -PI/2 and -width
                        scaledImg = Scalr.rotate(scaledImg, Rotation.CW_90);
                        break;
                    case 7: // PI/2 and Flip
                        scaledImg = Scalr.rotate(scaledImg, Rotation.CW_90);
                        scaledImg = Scalr.rotate(scaledImg, Rotation.FLIP_VERT);
                        break;
                    case 8: // PI / 2
                        scaledImg = Scalr.rotate(scaledImg, Rotation.CW_270);
                        break;
                    default:
                        break;
                    }       
                    // ---- End orientation handling ----

                    if(fileUpload.getFile_type().toLowerCase().contains("jpeg")){
                        ImageIO.write(scaledImg, "jpeg", fileUpload.getFile_data());
                        user.setProfile_picture_ext("jpg");
                    }
                    else{
                        Sanselan.writeImage(scaledImg, fileUpload.getFile_data(), ImageFormat.IMAGE_FORMAT_PNG, null);
                        user.setProfile_picture_ext("png");
                    }

答案 2 :(得分:1)

有时您无法使用相机拍摄的图像来工作,并且没有EXIF数据可以使用。以我为例,我有一个项目可以扫描成千上万张老式明信片并将其导入到我们的数字存储库中,其中大部分是正面的风景,而只有一小部分是纵向的(即使背面仍然是风景)。为了尽量减少扫描,去偏斜和修剪这些对象所花费的时间,每次扫描和横向都要进行三遍(始终)。

我来到这个问题,是为了寻找自动检测和旋转这些纵向卡扫描的答案。

关于基于相机元数据执行此操作的讨论很多。有一些示例说明了如何使用机器学习来自动调平照片,而相机并没有将其垂直放置在地面/地平线上。我还没有找到对我的情况有帮助的东西(并不是说没有,但是如果由于其他情况而很难找到它们)...

编辑3/22/2019:这是一个https://d4nst.github.io/2017/01/12/image-orientation/

...,所以我确实想出了一个我要尝试的答案:

对于每个卡片正面(使用ImageMagick和简单脚本进行批量处理):

  1. 制作较小的jpeg图片版本(因为我们不希望使用200MB的图片)
  2. 再制作三个较小的jpeg副本,每个副本都附加应用90度旋转
  3. 将这四个方向中的每一个都提交给云机器学习API(过去我对Microsoft's感到很幸运)
  4. 分析响应。选择最详细和/或最有信心作为正确方向
  5. 将原始尺寸的原件扫描旋转适当的大小,然后删除四个较小的jpeg。

我已经进行了一次扫描测试,在我的n = 1情况下,API的标签列表更长,并且为正确的定位提供了更好(更长)的建议标题。

潜在问题:

  1. 云提供商可能会中止API或开始收取超出我们负担能力的费用(当我使用一批这类卡编写元数据创建测试脚本时,使用级别一直停留在免费类别中)。
  2. 我认为Microsoft如果已经开始将更通用的元数据AI应用于所有方向,那么Microsoft可能已经旋转了您发送给OCR的图像(以捕获以任何方向书写的单词),这可能会停止工作(尽管希望他们会在响应中添加一个键以进行最佳定向猜测。
  3. (在我的情况下)明信片上经常以与图片不符的方向(摄影工作室名称等)书写。如果他们不按照我的意愿进行上述操作,那么在一轮旋转中获得更好的OCR可能会使脚本蒙上阴影。如果事实证明存在问题,则可能不得不忽略OCR结果。
  4. 使用带宽。