it-roy-ru.com

Как определить и автоматически повернуть изображения?

У меня есть куча изображений, среди которых некоторые изображения должны быть повернуты.

Образец:

enter image description here

Я хочу повернуть это изображение на 90 ° против часовой стрелки.

Я гуглил, чтобы узнать, как я могу повернуть изображение, и нашел много ссылок и SO потоков. Но как я могу определить, нужно ли повернуть изображение ? Picasa имеет функцию автоповорота. Я хочу иметь подобную функциональность.

Любой указатель был бы очень полезен для меня.

Я нашел ссылка но это связано с Android.

12
Tapas Bose

Указатель извлечение метаданных , который Роджер Роуланд предоставил, решил проблему. Я публикую его здесь для дальнейшего использования:

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));
    }
}
17
Tapas Bose

У меня были некоторые проблемы, заставляющие некоторые случаи переключения работать. Даже если вращение не было выполнено, 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");
                    }
5
rawkfist0215

Иногда вы не работаете с изображениями, снятыми камерой, и у вас нет EXIF-данных для работы. В моем случае у меня есть проект для сканирования и импорта в наше цифровое хранилище десятков тысяч старинных открыток, большинство из которых имеют альбомную ориентацию на лицевой стороне, и только небольшой процент из которых являются портретными (задняя часть остается альбомной даже на них). Чтобы минимизировать время, затрачиваемое на сканирование, выравнивание и обрезку, они выполняются по три на сканирование и в горизонтальной ориентации (всегда).

Я пришел к этому вопросу в поисках ответа на вопрос об автоматическом обнаружении и повороте этих сканирований карты портретной ориентации.

Есть много дискуссий о том, как сделать это на основе метаданных камеры. Есть несколько примеров того, как использовать машинное обучение для автоматического выравнивания фотографии, когда камера не удерживала слив на земле/горизонте. Я не нашел ничего, что могло бы помочь в моей ситуации (это не означает, что их нет, но если они есть, их трудно найти из-за других ситуаций), поэтому я нашел ответ, который собираюсь попробовать:

Для каждого фронта карты (пакетная обработка с использованием ImageMagick и простых скриптов):

  1. Сделайте уменьшенную версию изображения в формате jpeg (потому что мы не хотим, чтобы изображения размером 200 МБ работали)
  2. Сделайте еще три копии этого меньшего JPEG, каждый с дополнительным поворотом на 90 градусов к нему
  3. Отправьте каждую из этих четырех ориентаций в API обучения на облачных машинах (в прошлом мне повезло с Microsoft )
  4. Проанализируйте ответы. Выберите наиболее подробный и/или с наибольшей уверенностью в качестве правильной ориентации
  5. Поверните оригинал полноразмерного сканирования на соответствующую величину и удалите четыре меньших JPEG-изображения.

Я проверил это с помощью одного сканирования, и в моем случае n = 1 у API был гораздо более длинный список тегов и более качественный (и более длинный) заголовок для правильной ориентации.

Потенциальные проблемы:

  1. Облачный провайдер может прекратить API или начать взимать больше, чем мы можем себе позволить (когда я писал сценарий теста создания метаданных с использованием пакета этих карт, уровень использования оставался в бесплатной категории).
  2. Я думаю, что Microsoft может уже повернуть изображение, которое вы отправляете, для целей OCR (чтобы поймать слова, написанные в любой ориентации), если они начнут применять более обобщенный AI метаданных ко всем ориентациям, тогда это может перестать работать (хотя, можно надеяться, что они добавил бы ключ в ответе для лучшего угадывания ориентации).
  3. (В моей ситуации) на открытках часто пишут в ориентациях, не соответствующих изображению (названия фотостудий и т.д.). Если они не делают вышеизложенного, как я подозреваю, тогда лучшее распознавание текста за один оборот может обмануть сценарий. Возможно, придется игнорировать результаты распознавания, если это окажется проблемой.
  4. Использует пропускную способность.
1
pr3sidentspence