Java 如何确定和自动旋转图像?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21951892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Determine and Auto-Rotate Images?
提问by Tapas Bose
I have bunch of images, among them some of the images have to be rotated.
我有一堆图像,其中一些图像必须旋转。
Sample:
样本:
I want to rotate this image 90° counter-clockwise.
我想逆时针旋转这个图像 90°。
I Googled to know how can I rotate an image and found many links and SO threads. But how can I determine if the image needs to be rotated? Picasa has a Auto-Rotating feature. I want to have similar functionality.
我通过谷歌搜索知道如何旋转图像并找到了许多链接和 SO 线程。但是如何确定图像是否需要旋转?Picasa 具有自动旋转功能。我想要类似的功能。
Any pointer would be very helpful to me.
任何指针都会对我很有帮助。
I have found a linkbut it is related to Android.
我找到了一个链接,但它与 Android 相关。
采纳答案by Tapas Bose
The pointer of metadata-extractorwhich Roger Rowlandhas provided solved the problem. I am posting it here for future reference:
的指针元数据提取器,其罗杰·罗兰提供解决了这个问题。我把它贴在这里以供将来参考:
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));
}
}
回答by rawkfist0215
I had some issues getting some of the switch cases to work. Even when there was no rotation to be done, the AffineTransform would create a new image with black space in the image and would chop off some of the dimensions. Piggy backing off of the accepted answer here, I used the metadata-extractor class to determine what the orientation should be. Then I used the Imgscalrlibrary for scaling and rotation.
我在让一些开关盒工作时遇到了一些问题。即使没有旋转,AffineTransform 也会在图像中创建一个带有黑色空间的新图像,并切掉一些尺寸。撇开这里接受的答案,我使用了元数据提取器类来确定方向应该是什么。然后我使用Imgscalr库进行缩放和旋转。
The complete solution that worked for me can be seen below. Thank you Tapas Bosefor the original solution. I hope that this helps anyone!
对我有用的完整解决方案如下所示。感谢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");
}
回答by pr3sidentspence
Sometimes you aren't working from images captured by a camera and don't have EXIF data to work with. In my case, I have a project to scan and import into our digital repository tens of thousands of vintage postcards most of which are landscape on the front, and only a small percentage of which are portrait (the backs remain landscape even on these). To minimize the time spent scanning, deskewing, and cropping these they are done three-per-scan and landscape (always).
有时,您不使用相机拍摄的图像,也没有可用的 EXIF 数据。就我而言,我有一个项目要扫描并将数以万计的老式明信片导入我们的数字存储库,其中大部分是正面风景,只有一小部分是肖像(即使在这些背面仍然是风景)。为了最大限度地减少扫描、纠偏和裁剪所花费的时间,每次扫描和横向(始终)执行三个。
I came to this question looking for an answer to automating the detection and rotation of these portrait orientation card scans.
我来到这个问题是为了寻找自动检测和旋转这些纵向卡片扫描的答案。
There are many discussions about doing this based on camera metadata. There are some examples of how to use machine learning to automatically level a photo where the camera was not held plum to the ground/horizon. I haven't found any that would help in my situation (which is not to say there aren't any, but if there are they are hard to find because of the other situations)...
有很多关于基于相机元数据执行此操作的讨论。有一些示例说明如何使用机器学习来自动调平相机未将其固定在地面/地平线上的照片。我还没有找到对我的情况有帮助的任何东西(这并不是说没有,但如果有的话,由于其他情况而很难找到)......
EDIT 3/22/2019: Here's one https://d4nst.github.io/2017/01/12/image-orientation/
编辑 3/22/2019:这是一个https://d4nst.github.io/2017/01/12/image-orientation/
..., so I did come up with an answer that I'm going to try:
...,所以我确实想出了一个我要尝试的答案:
For each card front (batch processing using ImageMagick and simple scripts):
对于每张卡片正面(使用 ImageMagick 和简单脚本进行批处理):
- Make a smaller jpeg version of the image (because we don't want to 200MB images to work with)
- Make three more copies of that smaller jpeg, each with an additional 90 degrees of rotation applied to it
- Submit each of these four orientations to a cloud machine learning API (I've had good luck with Microsoft'sin the past)
- Analyze the responses. Chose the most detailed and/or with the highest confidence as the correct orientation
- Rotate the original full-size scan the appropriate amount and delete the four smaller jpegs.
- 制作一个较小的 jpeg 版本的图像(因为我们不想使用 200MB 的图像)
- 再制作三个较小的 jpeg 副本,每个副本都应用额外的 90 度旋转
- 将这四个方向中的每一个提交给云机器学习 API(我过去在Microsoft 上运气不错)
- 分析响应。选择最详细和/或最有信心的作为正确方向
- 将原始全尺寸扫描旋转适当的量并删除四个较小的 jpeg。
I've tested this with one scan and in my n=1 case the API had a much longer list of tags and a better (and longer) suggested caption for the correct orientation.
我用一次扫描对此进行了测试,在我的 n=1 情况下,API 有更长的标签列表和更好(更长)的正确方向建议标题。
Potential problems:
潜在问题:
- The cloud provider may discontinue the API or start charging more than we can afford (when I scripted a test of metadata creation using a batch of these cards the level of use stayed in the free category).
- I think Microsoft may already rotate the image you send it for OCR purposes (to catch words written in any orientation) if they start applying the more generalized metadata AI to all the orientations as well then this could stop working (although, one would hope they would add a key in the response for best orientation guess).
- (In my situation) postcards often have writing on them at orientations not true to the image (photography studio names, etc.). If they aren't doing the above as I suspect then better OCR at one rotation could fool the script. One might have to ignore OCR results if that proves to be a problem.
- Uses bandwidth.
- 云提供商可能会停止使用 API 或开始收取超出我们承受能力的费用(当我使用一批这些卡编写元数据创建测试时,使用级别仍处于免费类别)。
- 我认为,如果微软开始将更通用的元数据 AI 应用于所有方向,那么他们可能已经出于 OCR 目的旋转您发送的图像(以捕获以任何方向书写的单词),然后这可能会停止工作(尽管,人们希望他们将在响应中添加一个键以获得最佳方向猜测)。
- (在我的情况下)明信片上通常会以与图像不真实的方向(摄影工作室名称等)书写。如果他们没有像我怀疑的那样执行上述操作,那么在一次轮换中更好的 OCR 可能会欺骗脚本。如果证明这是一个问题,人们可能不得不忽略 OCR 结果。
- 使用带宽。