正确裁剪从照片库中获取的图像

lna*_*ger 8 objective-c cgimage ios alasset alassetslibrary

我一整天都在研究这个问题,并在SO和google上看了很多问题,但到目前为止我还没有提出任何正确的问题.

我在运行iOS 5.1.1的iPad上拍了一张照片,并使用照片应用裁剪了它.然后我从资产库中获取它的引用,并获得未裁剪的全分辨率图像.

我发现裁剪信息包含在我对象的AdjustmentXMP键中.metadataALAssetRepresentation

所以我使用XMP信息裁剪照片,这是我得到的:

原始照片(1,936 x 2,592):
原始照片

正确裁剪的照片,如照片应用程序(1,420 x 1,938)中所示:
正确裁剪的照片

使用下面的代码裁剪照片
(也是1,420 x 1,938,但是在右边太远处裁剪了大约200像素):
问题

这是照片中的XMP数据:

<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:aas="http://ns.apple.com/adjustment-settings/1.0/">
         <aas:AffineA>1</aas:AffineA>
         <aas:AffineB>0</aas:AffineB>
         <aas:AffineC>0</aas:AffineC>
         <aas:AffineD>1</aas:AffineD>
         <aas:AffineX>-331</aas:AffineX>
         <aas:AffineY>-161</aas:AffineY>
         <aas:CropX>0</aas:CropX>
         <aas:CropY>0</aas:CropY>
         <aas:CropW>1938</aas:CropW>
         <aas:CropH>1420</aas:CropH>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
Run Code Online (Sandbox Code Playgroud)

这是我用于裁剪照片的代码:

ALAssetRepresentation *rep = // Get asset representation
CGImageRef defaultImage = [rep fullResolutionImage];

// Values obtained from XMP data above:
CGRect cropBox = CGRectMake(0, 0, 1938, 1420);
CGAffineTransform transform = CGAffineTransformMake(1, 0, 0, 1, 331, 161);

// Apply the Affine Transform to the crop box:
CGRect transformedCropBox =  CGRectApplyAffineTransform(cropBox, transform);

// Created a new cropped image:
CGImageRef croppedImage = CGImageCreateWithImageInRect(defaultImage, transformedCropBox);

// Create the UIImage:
UIImage *image = [UIImage imageWithCGImage:croppedImage scale:[rep scale] orientation:[rep orientation]];

CGImageRelease(croppedImage);
Run Code Online (Sandbox Code Playgroud)

我用多张图片重现了这个问题.如果我只是使用fullScreenImage它完美显示,但我需要全尺寸图像.

Kur*_*vis 11

这是一个棘手的问题!显然没有关于此XMP数据的文档,因此我们必须猜测如何解释它.有许多选择可做,而错误导致产生微妙错误的结果.

TL; DR:理论上你的代码看起来是正确的,但实际上它给出了错误的结果,我们可以尝试一个相当明显的调整.

取向

图像文件可以包含额外的元数据,指定在显示时是否应该(以及如何)旋转和/或翻转图像的原始数据.UIImage用它的imageOrientation属性来表达这一点,并且ALAssetRepresentation是类似的.

但是,CGImages只是位图,没有存储方向.-[ALAssetRepresentation fullResolutionImage]为您提供CGImage原始方向,不应用任何调整.

在您的情况下,方向是3,意义ALAssetOrientationRightUIImageOrientationRight.查看软件(例如UIImage)查看此值,看到图像朝向右90°(顺时针),然后向左旋转90°(逆时针),然后再显示它.或者,换句话说,CGImage从您在屏幕上看到的图像顺时针旋转90°.

(为了验证这一点,使用CGImageGetWidth()和得到CGImage的宽度和高度CGImageGetHeight().你应该发现CGImage是2592宽和1936高.它旋转了90° ALAssetRepresentation,它dimensions应该是1936宽,高2592.你也可以UIImageCGImage使用正常方向创建一个UIImageOrientationUp,写入UIImage文件,看看它是什么样的.)

XMP字典中的值似乎与CGImage方向相关.例如,裁剪矩形比它高,X平移大于Y平移等.有意义.

坐标系

我们还必须决定XMP值应该在哪个坐标系中.很可能它是这两个中的一个:

  • "笛卡儿":原点位于图像的左下角,X向右增加,Y向上增加.这是Core Graphics通常使用的系统.
  • "翻转":原点位于图像的左上角,X向右增加,Y向下增加.这是UIKit通常使用的系统.令人惊讶的是,与大多数CG不同,CGImageCreateWithImageInRect()rect这种方式解释其论点.

让我们假设"翻转"是正确的,因为它通常更方便.无论如何,你的代码已经尝试这样做了.

解释XMP词典

字典包含仿射变换和裁剪矩形.我们猜测应该按此顺序解释:

  1. 应用转换
  2. 以自然矩形(0,0,w,h)绘制图像
  3. 取消应用转换(弹出转换堆栈)
  4. 裁剪到裁剪矩形

如果我们手动尝试,这些数字似乎有效.这是一个粗略的图表,裁剪矩形为半透明的紫色:

翻盖案例图

现在为一些代码

在调用CG时,我们实际上不必遵循这些确切的步骤,但我们应该像我们一样行事.

我们只想打电话CGImageCreateWithImageInRect,而且很明显如何计算合适的裁剪矩形(331,161,1938,1420).您的代码似乎正确执行此操作.

如果我们将图像裁剪到该矩形,然后UIImage从中创建一个(指定正确的方向UIImageOrientationRight),那么我们应该得到正确的结果.

但是,结果是错误的! 你得到的就像我们在笛卡尔坐标系中进行操作一样:

笛卡儿案例图

或者,就像图像旋转方向相反UIImageOrientationLeft,但我们保持相同的裁剪矩形:

定向左案例图

纠正

这一切都很奇怪,我不明白出了什么问题,尽管我很乐意.

但修复似乎相当简单:只需翻转剪辑矩形即可.在计算之后如上:

// flip the transformedCropBox in the image
transformedCropBox.origin.y = CGImageGetHeight(defaultImage) - CGRectGetMaxY(transformedCropBox);
Run Code Online (Sandbox Code Playgroud)

那样有用吗?(对于这种情况,以及其他方向的图像?)