12

基本任务:更新与 UIImage 关联的元数据的 EXIF 方向属性。我的问题是我不知道方向属性在所有 EXIF 信息中的位置。


复杂的背景:我正在更改返回的图像的方向,imagePickerController:didFinishPickingMediaWithInfo:所以我认为我还需要在保存图像之前更新元数据writeImageToSavedPhotosAlbum:(CGImageRef)imageRef metadata:(NSDictionary *)metadata

换句话说,除非我更改它,否则元数据将包含旧的/初始方向,因此是错误的。我改变方向的原因是因为当我运行Core Image人脸检测例程时它总是让我绊倒。使用前置摄像头在人像模式下使用 iPhone(设备)拍照,方向为UIImageOrientationRight(3)。如果我重写图像使方向为UIImageOrientationUp(0),我会得到很好的人脸检测结果。作为参考,重写图像的例程如下。

我发现整个相机方向的事情非常令人困惑,我似乎正在将自己更深地挖掘到所有这些代码洞中。我看过帖子(这里这里这里。根据这篇文章(https://stackoverflow.com/a/3781192/840992):

“相机实际上是横向原生的,所以当你在风景中拍摄照片时你会向上或向下,当你在纵向拍摄照片时你会向左或向右(取决于你拿着设备的方式)。”

...这完全令人困惑。如果上述情况属实,我认为我应该了解前置摄像头UIImageOrientationLeftMirroredUIImageOrientationRightMirrored前置摄像头的方向。这些都不能解释为什么CIDetector选择器返回的原始图像会失败。

我正在向后接近这个屁股,但我似乎无法定向......


-(UIImage *)normalizedImage:(UIImage *) thisImage
{
    if (thisImage.imageOrientation == UIImageOrientationUp) return thisImage;

    UIGraphicsBeginImageContextWithOptions(thisImage.size, NO, thisImage.scale);
    [thisImage drawInRect:(CGRect){0, 0, thisImage.size}];
    UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return normalizedImage;
}
4

2 回答 2

23

看看我的答案:
强制 UIImagePickerController 以纵向/尺寸 iOS 拍照

以及github 上的相关项目(您不需要运行该项目,只需查看自述文件)。

它更关心读取而不是写入元数据——但它包括一些关于 Apple 的 imageOrientation 和相应的方向“Exif”元数据的注释。

这可能也值得一读
在 IOS 6.0 或 iPhone 中上传期间自动旋转的捕获照片

有两种不同的常数编号约定来指示图像方向。

  1. 在 TIFF/IPTC 图像元数据标签中使用的 kCGImagePropertyOrientation 常量
  2. UIImage imageOrientation 属性使用的 UIImageOrientation 常量。

iPhone 的原生相机方向是横向左侧(右侧带有主页按钮)。原生像素尺寸始终反映这一点,旋转标志用于根据显示方向正确定位图像。

Apple            UIImage.imageOrientation     TIFF/IPTC kCGImagePropertyOrientation

iPhone native     UIImageOrientationUp    = 0  =  Landscape left  = 1  
rotate 180deg     UIImageOrientationDown  = 1  =  Landscape right = 3  
rotate 90CCW      UIImageOrientationLeft  = 2  =  Portrait  down  = 8  
rotate 90CW       UIImageOrientationRight = 3  =  Portrait  up    = 6  

UIImageOrientation4-7 映射到kCGImagePropertyOrientation2,4,5,7 - 这些是镜像对应物。

UIImage 从底层的 kCGImagePropertyOrientation 标志派生它的 imagerOrientation 属性——这就是它是只读属性的原因。这意味着只要您正确获取元数据标志,imageOrientation 就会正确执行。但是,如果您正在阅读数字以应用转换,则需要知道您正在查看哪些数字。

于 2013-01-28T00:36:17.433 回答
5

A few gleanings from my world o' pain in looking into this:

Background: Core Image face detection was failing and it seemed to be related to using featuresInImage:options: and using the UIImage.imageOrientation property as an argument. With an image adjusted to have no rotation and not mirrored, detection worked fine but when passing in an image directly from the camera detection failed.

Well...UIImage.imageOrientation is DIFFERENT than the actual orientation of the image.

In other words...

UIImage* tmpImage = [self.imageInfo objectForKey:UIImagePickerControllerOriginalImage];
printf("tmpImage.imageOrientation: %d\n", tmpImage.imageOrientation);

Reports a value of 3 or UIImageOrientationRight whereas using the metaData returned by the UIImagePickerControllerDelegate method...

   NSMutableDictionary* metaData = [[tmpInfo objectForKey:@"UIImagePickerControllerMediaMetadata"] mutableCopy];
   printf(" metaData orientation    %d\n", [[metaData objectForKey:@"Orientation"] integerValue]);

Reports a value of 6 or UIImageOrientationLeftMirrored.

I suppose it seems obvious now that UIImage.imageOrientation is a display orientation which appears to determined by source image orientation and device rotation. (I may be wrong here) Since the display orientation is different than the actual image data, using that will cause the CIDetector to fail. Ugh.

I'm sure all of that serves very good and important purposes for liquid GUIs etc. but it is too much to deal with for me since all the CIDetector coordinates will also be in the original image orientation making CALayer drawing sick making. So, for posterity here is how to change the Orientation property of the metaData AND the image contained therein. This metaData can then be used to save the image to the cameraRoll.

Solution

// NORMALIZE IMAGE
UIImage* tmpImage = [self normalizedImage:[self.imageInfo objectForKey:UIImagePickerControllerOriginalImage]];
NSMutableDictionary * tmpInfo =[self.imageInfo mutableCopy];
NSMutableDictionary* metaData = [[tmpInfo objectForKey:@"UIImagePickerControllerMediaMetadata"] mutableCopy];

[metaData setObject:[NSNumber numberWithInt:0] forKey:@"Orientation"];
[tmpInfo setObject:tmpImage forKey:@"UIImagePickerControllerOriginalImage"];
[tmpInfo setObject:metaData forKey:@"UIImagePickerControllerMediaMetadata"];

self.imageInfo = tmpInfo;
于 2013-01-28T02:24:29.803 回答