当前位置: 首页 > 知识库问答 >
问题:

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

夏侯楷
2023-03-14

我有很多图像,其中一些图像需要旋转。

样品:

我想逆时针旋转这个图像90°。

我谷歌了一下如何旋转图像,发现了许多链接和SO线程。但是我如何确定图像是否需要旋转?Picasa有一个自动旋转功能。我想有类似的功能。

任何指针都会对我很有帮助。

我找到了一个链接,但它与Android有关。

共有3个答案

娄建义
2023-03-14

有时,您不使用摄像机捕获的图像,也没有EXIF数据。在我的案例中,我有一个项目来扫描成千上万张老式明信片,并将其导入我们的数字存储库,其中大部分明信片正面是风景,只有一小部分是肖像(即使在这些明信片上,背面仍然是风景)。为了最大限度地减少扫描、去扭曲和裁剪所花费的时间,每次扫描和横向(始终)进行三次。

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

有很多关于基于相机元数据这样做的讨论。有一些例子说明如何使用机器学习来自动调平相机没有放在地上/地平线上的照片。我还没有找到任何对我的情况有帮助的东西(这并不是说没有,但是如果有的话,因为其他情况很难找到)...

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

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

对于每个卡前端(使用ImageMagick和简单脚本进行批处理):

  1. 制作较小的jpeg版本的图像(因为我们不想使用200MB的图像)。
  2. 再制作三个较小jpeg的副本,每个副本都应用了额外的90度旋转。
  3. 将这四个方向中的每一个都提交到云机器学习API(我在微软的过去很幸运)
  4. 分析响应。选择最详细和/或置信度最高的方向作为正确的方向。
  5. 将原始全尺寸扫描旋转适当的量,并删除四个较小的JPEG

我已经用一次扫描对此进行了测试,在我的n = 1案例中,API具有更长的标签列表和更好(更长)的建议标题,以获得正确的方向。

潜在问题:

  1. 云提供商可能会停止API,或者开始收取超出我们承受能力的费用(当我使用一批此类卡编写元数据创建测试脚本时,使用级别仍处于免费类别)
  2. 我认为微软可能已经旋转了你发送给它的图像,用于OCR目的(捕捉任何方向的文字)。如果他们开始将更通用的元数据AI应用于所有方向,那么这可能会停止工作(尽管,有人希望他们会在响应中添加一个键,以获得最佳方向猜测)
  3. (在我的情况下)明信片上的书写方向往往与图像不符(摄影工作室名称等)。如果他们没有像我所怀疑的那样做上述操作,那么在一次旋转中更好的OCR可能会欺骗脚本。如果OCR结果被证明是一个问题,人们可能不得不忽略它
  4. 它使用带宽
朱承载
2023-03-14

我有一些问题得到一些开关案件的工作。即使没有旋转,仿射变换也会在图像中创建一个带有黑色空间的新图像,并砍掉一些维度。根据这里的公认答案,我使用元数据提取器类来确定方向。然后我使用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");
                    }
杜俊逸
2023-03-14

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));
    }
}
 类似资料:
  • 本文向大家介绍Android自定义View图片按Path运动和旋转,包括了Android自定义View图片按Path运动和旋转的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Android自定义View图片按Path运动旋转的具体代码,供大家参考,具体内容如下 View: Activity 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 使地球自动旋转,并控制旋转速率。地球的自动旋转功能在默认情况下是关闭的,如果启动旋转功能,默认的旋转速率是1。 // 启用自动旋转功能,将转速设置为1(同时1也是默认的转速) controller.setAutoRotation( true, 1 ); // 如果之前开启了自动旋转功能,可以用这种方式将其关闭 controller.setAutoRotation( false );

  • 我正在开发一款ios应用程序,它必须在横向和纵向两种模式下都能工作,但有一种视图应该始终在横向模式下。因此,我有: 如果我在横向模式下使用我的应用程序,并到达此视图并旋转设备(iPad),则调用此方法并返回false,因此界面不会旋转,好东西。 但是如果我在纵向视图中,并且到达该视图,则调用此方法并返回false,但是方向不会改变。在这种情况下,如果我将设备旋转到横向,该方法将返回true,接口将

  • 当我尝试使用旋转图像视图时,我现在开始学习JavaFX 它绕着它的中轴线旋转,但是当我尝试使用 它随机旋转,我不知道它的旋转轴是一样的

  • 我的应用程序支持所有方向,除了肖像向上向下。在我的视图层次结构中,我有一个AVCaptureVideoPreviewLayer作为俯视图中的一个子层,即UIImageView。然后,在视图层次结构的下方是几个显示控件的覆盖视图。 覆盖视图可以在方向改变的情况下正常工作,但我不知道如何使用这个AVCaptureVideoPreviewLayer。我希望它的行为像照相机应用程序一样,这样preview

  • 但似乎什么都没起作用。