program story

방향 메타 데이터를 기반으로 JPEG 이미지를 회전하는 방법은 무엇입니까?

inputbox 2020. 12. 5. 09:44
반응형

방향 메타 데이터를 기반으로 JPEG 이미지를 회전하는 방법은 무엇입니까?


이미지가 업로드 될 때 미리보기 이미지를 생성하는 서버 코드가 있습니다. 문제는 이미지를 촬영하고 카메라 / 장치를 회전 할 때 전체 크기 이미지 자체가 모든 이미지보기 소프트웨어에서 올바른 방향으로 표시 되더라도 축소판이 회전된다는 것입니다. 이것은 jpg에서만 발생합니다.

OSX에서 미리보기를 사용하면 jpg에 방향 메타 데이터가 포함되어 있음을 알 수 있습니다. ImageTools (Grails Plugin)를 사용하여 축소판을 생성 할 때 EXIF ​​메타 데이터가 축소판에 없기 때문에 축소판이 회전 된 것처럼 보입니다.

오프라인 대화를 통해 EXIF ​​메타 데이터를 읽는 것은 상대적으로 쉽지만 쓰기가 쉬운 방법이 없다는 것을 알게되었습니다. 이것이 jpg 썸네일을 생성 할 때 데이터가 손실되는 이유입니다.

그래서 두 가지 옵션이있는 것 같습니다.

  1. ImageMagick을 사용하여 축소판을 생성합니다. 단점은 서버에 더 많은 소프트웨어를 설치해야한다는 것입니다.
  2. EXIF 방향 데이터는 코드를 읽고 썸네일을 적절하게 회전합니다.

다른 옵션을 아는 사람이 있습니까?


이미지를 회전하려면 메타 데이터 추출기 라이브러리 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);
}

그런 다음 검색 한 방향이 주어지면 이미지를 올바른 방향으로 회전 및 / 또는 뒤집을 수 있습니다. EXIF 방향에 대한 Affine 변환은 다음 방법으로 제공됩니다.

// 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;
}

이미지 회전은 다음 방법으로 수행됩니다.

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;
}

서버 환경에서 실행하는 것을 잊지 마십시오 -Djava.awt.headless=true


Thumbnailator의 라이브러리 명예는 방향 플래그를 EXIF. 올바른 방향으로 전체 크기로 이미지를 읽으려면 :

BufferedImage image = Thumbnails.of(inputStream).scale(1).asBufferedImage();

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);

그게 다야!

Apache Commons Imaging을 사용해 보았지만 엉망이었습니다. JavaXT는 훨씬 더 우아합니다.


Exif는 독점적 인 내용 때문에 작성하기 어려운 것 같습니다. 그러나 다른 옵션을 고려할 수 있습니다.

원본을 읽되 썸네일에만 방향 태그를 씁니다.

Apache Sanselan에는이를 수행 할 수있는 멋진 도구 모음이있는 것 같습니다.

http://commons.apache.org/proper/commons-imaging/

예를 들어 ExifRewriter 클래스를 살펴보십시오.


옳게 보이기를 원한다면. 이미 추출한 '방향'에 따라 필요에 따라 "회전"-PI / 2 (-90도), PI / 2 (90도) 또는 PI (+180도)를 추가 할 수 있습니다. 방향이 적용되고 메타 데이터가 썸네일 출력에서 ​​제거되므로 브라우저 또는 기타 프로그램이 이미지를 올바르게 표시합니다.


으로 dnault는 이전의 코멘트에 언급, Thumbnaliator의 라이브러리는 문제를 해결합니다. 그러나이 자동 회전에서 색상 변경을 방지하려면 올바른 입력 / 출력 형식을 사용해야합니다.

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(file.getContents());
Thumbnails.of(in)
    .scale(1)
    .toOutputStream(baos);
byte[] bytes = baos.toByteArray();

내 솔루션은 @PerLindberg의 답변과 @AntoineMartin의 조합입니다. Windows 10에서 Java 8로 다른 답변을 시도했지만 아무도 트릭을 수행하지 않는 것 같습니다. @AntoinMartin의 com.drew.imaging 솔루션은 느리고 이미지가 흑백으로 나오고 인공물로 가득 차 있습니다. @PerLindberg의 JavaXT 솔루션은 Exif 2.2 데이터를 읽지 않았습니다.

1) com.drew.imaging를 사용하여 exif 정보를 읽으십시오.

// 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 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;
    if (directory != null) {
        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);
    } else {
        return null;
    }
}

2) JavaXT를 사용하여 Exif 데이터를 기반으로 회전을 수행합니다.

public void rotateMyImage(String imageDownloadFilenme);
    File imageDownloadFile =  new File(imgageDownloadFilenme);
    Image image = new Image(imgageDownloadFilenme);
    ImageInformation imageInformation = readImageInformation(imageDownloadFile);
    if (imageInformation != null) {
        rotate(imageInformation, image);
    }
    image.saveAs(imgageDownloadFilenme);
}

public void rotate(ImageInformation info, Image image) {

    switch(info.orientation) {
        case 1:
            return;
        case 2:
            image.flip();
            break;
        case 3:
            image.rotate(180.0D);
            break;
        case 4:
            image.flip();
            image.rotate(180.0D);
            break;
        case 5:
            image.flip();
            image.rotate(270.0D);
            break;
        case 6:
            image.rotate(90.0D);
            break;
        case 7:
            image.flip();
            image.rotate(90.0D);
            break;
        case 8:
            image.rotate(270.0D);
    }

}

Based on the answers of Antoine Martin I created an own class for correcting the orientation of a given jpeg image (in my case as an input stream) based on the exif information of the image. With his solution I had the problem, that the colors of the resulting image were wrong, therefore I created this one. For retrieving the metadata of the image I used the metadata-extractor library.

I hope it will help some people.

public class ImageOrientationUtil {

/**
 * Checks the orientation of the image and corrects it if necessary.
 * <p>If the orientation of the image does not need to be corrected, no operation will be performed.</p>
 * @param inputStream
 * @return
 * @throws ImageProcessingException
 * @throws IOException
 * @throws MetadataException
 */
public static BufferedImage correctOrientation(InputStream inputStream) throws ImageProcessingException, IOException, MetadataException {
    Metadata metadata = ImageMetadataReader.readMetadata(inputStream);
    if(metadata != null) {
        if(metadata.containsDirectoryOfType(ExifIFD0Directory.class)) {
            // Get the current orientation of the image
            Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
            int orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);

            // Create a buffered image from the input stream
            BufferedImage bimg = ImageIO.read(inputStream);


            // Get the current width and height of the image
            int[] imageSize = {bimg.getWidth(), bimg.getHeight()};
            int width = imageSize[0];
            int height = imageSize[1];

            // Determine which correction is needed
            AffineTransform t = new AffineTransform();
            switch(orientation) {
            case 1:
                // no correction necessary skip and return the image
                return bimg;
            case 2: // Flip X
                t.scale(-1.0, 1.0);
                t.translate(-width, 0);
                return transform(bimg, t);
            case 3: // PI rotation 
                t.translate(width, height);
                t.rotate(Math.PI);
                return transform(bimg, t);
            case 4: // Flip Y
                t.scale(1.0, -1.0);
                t.translate(0, -height);
                return transform(bimg, t);
            case 5: // - PI/2 and Flip X
                t.rotate(-Math.PI / 2);
                t.scale(-1.0, 1.0);
                return transform(bimg, t);
            case 6: // -PI/2 and -width
                t.translate(height, 0);
                t.rotate(Math.PI / 2);
                return transform(bimg, t);
            case 7: // PI/2 and Flip
                t.scale(-1.0, 1.0);
                t.translate(height, 0);
                t.translate(0, width);
                t.rotate(  3 * Math.PI / 2);
                return transform(bimg, t);
            case 8: // PI / 2
                t.translate(0, width);
                t.rotate(  3 * Math.PI / 2);
                return transform(bimg, t);
            }
        }
    }

    return null;
}

/**
 * Performs the tranformation
 * @param bimage
 * @param transform
 * @return
 * @throws IOException
 */
private static BufferedImage transform(BufferedImage bimage, AffineTransform transform) throws IOException {
    // Create an transformation operation
    AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);

    // Create an instance of the resulting image, with the same width, height and image type than the referenced one
    BufferedImage destinationImage = new BufferedImage( bimage.getWidth(), bimage.getHeight(), bimage.getType() );
    op.filter(bimage, destinationImage);

   return destinationImage;
}
}

참고URL : https://stackoverflow.com/questions/5905868/how-to-rotate-jpeg-images-based-on-the-orientation-metadata

반응형