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

时间:2012-12-30 06:27:17

标签: objective-c ios cgimage alassetslibrary alasset

我一整天都在研究这个问题,并且已经在SO和google上查看了很多问题,但到目前为止我还没有想出任何正确的事情。

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

我发现裁剪信息包含在AdjustmentXMP对象metadata的{​​{1}}键中。

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

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

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

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

这是照片中的XMP数据:

ALAssetRepresentation

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

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

我用多张图片重现了这个问题。如果我只使用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); 它会完美显示,但我需要全尺寸图像。

1 个答案:

答案 0 :(得分:11)

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

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

取向

图像文件可能包含其他元数据,指定在显示时是否(以及如何)旋转和/或翻转图像的原始数据。 UIImage使用imageOrientation属性和ALAssetRepresentation is similar表达了这一点。

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

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

(为了验证这一点,使用CGImageGetWidth()CGImageGetHeight()获取CGImage的宽度和高度。你会发现CGImage是2592宽和1936高。这是从旋转90° ALAssetRepresentation,其dimensions应该是1936宽,高度为2592。您还可以使用正常方向UIImageCGImage创建UIImageOrientationUp,写下{ {1}}到一个文件,看看它是什么样的。)

XMP词典中的值似乎与UIImage的方向相关。例如,裁剪矩形比它高,X平移大于Y平移等。有道理。

坐标系

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

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

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

解释XMP词典

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

  1. 应用转换
  2. 以自然矩形(0,0,w,h)
  3. 绘制图像
  4. 取消应用转换(弹出转换堆栈)
  5. 裁剪到裁剪矩形
  6. 如果我们手动尝试,这些数字似乎有用。这是一个粗略的图表,裁剪矩形为半透明的紫色:

    diagram for flipped case

    现在有些代码

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

    我们只想调用rect,而且很明显如何计算合适的裁剪矩形CGImageCreateWithImageInRect。您的代码似乎正确执行此操作。

    如果我们将图片裁剪到该矩形,然后从中创建(331,161,1938,1420)(指定正确的方向,UIImage),那么我们应该得到正确的结果。

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

    diagram for cartesian case

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

    diagram for oriented-left case

    更正

    这一切都很奇怪,我不明白出了什么问题,虽然我很乐意。

    但修复似乎相当简单:只需翻转剪辑矩形即可。按上述方法计算后:

    UIImageOrientationLeft

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