방향 메타 데이터를 기반으로 JPEG 이미지를 회전하는 방법은 무엇입니까?
이미지가 업로드 될 때 미리보기 이미지를 생성하는 서버 코드가 있습니다. 문제는 이미지를 촬영하고 카메라 / 장치를 회전 할 때 전체 크기 이미지 자체가 모든 이미지보기 소프트웨어에서 올바른 방향으로 표시 되더라도 축소판이 회전된다는 것입니다. 이것은 jpg에서만 발생합니다.
OSX에서 미리보기를 사용하면 jpg에 방향 메타 데이터가 포함되어 있음을 알 수 있습니다. ImageTools (Grails Plugin)를 사용하여 축소판을 생성 할 때 EXIF 메타 데이터가 축소판에 없기 때문에 축소판이 회전 된 것처럼 보입니다.
오프라인 대화를 통해 EXIF 메타 데이터를 읽는 것은 상대적으로 쉽지만 쓰기가 쉬운 방법이 없다는 것을 알게되었습니다. 이것이 jpg 썸네일을 생성 할 때 데이터가 손실되는 이유입니다.
그래서 두 가지 옵션이있는 것 같습니다.
- ImageMagick을 사용하여 축소판을 생성합니다. 단점은 서버에 더 많은 소프트웨어를 설치해야한다는 것입니다.
- 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;
}
}
'program story' 카테고리의 다른 글
소급하여 git repo에 --recursive 추가 (0) | 2020.12.05 |
---|---|
Factory Girl-목적이 무엇인가요? (0) | 2020.12.05 |
mysql 디스크의 tmp 테이블에 복사 건너 뛰기 (0) | 2020.12.05 |
데이터 프레임에있는 모든 열의 클래스를 가져 오는 방법은 무엇입니까? (0) | 2020.12.05 |
C ++ 용 NumPy 스타일 배열? (0) | 2020.12.05 |