8

私は一日中これに取り組んでいて、ここSOとグーグルでたくさんの質問を見てきました、しかし今のところ私は完全に正しいものを思い付くことができません。

iOS 5.1.1を実行しているiPadで写真を撮り、Photosアプリを使用してトリミングしました。次に、アセットライブラリから参照を取得し、トリミングされていないフル解像度の画像を取得しています。

トリミング情報がオブジェクトののAdjustmentXMPキーにmetadata含まれていることがわかりました。ALAssetRepresentation

したがって、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>

写真を切り抜くために使用しているコードは次のとおりです。

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

複数の画像で問題を再現しました。使用するだけfullScreenImageで完璧に表示されますが、フルサイズの画像が必要です。

4

1 に答える 1

11

これはトリッキーです!この XMP データに関するドキュメントは明らかに存在しないため、解釈方法を推測する必要があります。選択すべき選択肢は数多くあり、選択を誤ると微妙に間違った結果につながる可能性があります。

TL;DR: 理論的にはあなたのコードは正しいように見えますが、実際には間違った結果になっています。かなり明らかな調整を試すことができます。

オリエンテーション

画像ファイルには、表示時に画像の生データを回転および/または反転するかどうか (およびその方法) を指定する追加のメタデータが含まれる場合があります。UIImageはこれをそのimageOrientation性質で表現し、ALAssetRepresentation類似している.

ただし、CGImageは単なるビットマップであり、向きは格納されていません。-[ALAssetRepresentation fullResolutionImage]は、CGImage調整が適用されていない元の向きの を提供します。

あなたの場合、方向は3、意味ALAssetOrientationRightまたはUIImageOrientationRightです。表示ソフトウェア (たとえば、UIImage) はこの値を見て、画像が右に 90° (時計回り) に向いていることを確認し、表示する前に左に 90° (反時計回り) 回転させます。別の言い方をCGImageすれば、画面上で見ている画像から時計回りに 90° 回転しています。

CGImageGetWidth()(これを確認するには、 と を使用して CGImage の幅と高さを取得しCGImageGetHeight()ます。CGImage は幅 2592、高さ 1936 であることがわかります。これは、幅 1936、ALAssetRepresentationdimensionsさ 2592 の から 90° 回転しています。通常の向きを使用してUIImageからを作成し、 をファイルに書き込んで、それがどのように見えるかを確認してください。)CGImageUIImageOrientationUpUIImage

CGImageXMP ディクショナリの値は、の向きに関連しているように見えます。たとえば、トリミング範囲が高さよりも幅が広い、X 移動が Y 移動よりも大きいなどです。これは理にかなっています。

座標系

また、XMP 値がどの座標系にあると想定されるかを決定する必要があります。ほとんどの場合、次の 2 つのいずれかになります。

  • "Cartesian" : 原点は画像の左下隅にあり、X は右に向かって増加し、Y は上に向かって増加します。Core Graphics が通常使用するシステムです。
  • 「反転」: 原点は画像の左上隅にあり、X は右に向かって増加し、Y は下に向かって増加します。これは、UIKit が通常使用するシステムです。驚くべきことに、ほとんどの CG とは異なり、はその議論をこのようにCGImageCreateWithImageInRect()解釈します。rect

一般的には「反転」の方が便利なので、「反転」が正しいと仮定しましょう。とにかく、あなたのコードはすでにそのようにしようとしています。

XMP 辞書の解釈

ディクショナリには、アフィン変換とクロップ rect が含まれています。次の順序で解釈する必要があると推測しましょう。

  1. 変換を適用する
  2. 画像を自然な四角形 (0,0,w,h) に描画します
  3. 変換の適用を解除します (変換スタックをポップします)
  4. トリミング範囲に合わせてトリミング

これを手で試してみると、数値はうまくいくようです。以下は大まかな図で、切り取り四角形は半透明の紫色で表示されています。

反転ケースの図

いくつかのコードについて

CG の呼び出しに関しては、実際にはこれらの正確な手順に従う必要はありませんが、従ったかのように振る舞う必要があります。

を呼び出したいだけCGImageCreateWithImageInRectで、適切なクロップ rect を計算する方法は明らか(331,161,1938,1420)です。あなたのコードはこれを正しく行っているようです。

画像をその四角形にトリミングし、UIImageそこから (正しい方向を指定して) を作成するUIImageOrientationRightと、正しい結果が得られるはずです。

しかし、結果は間違っています! 得られるものは、あたかもデカルト座標系で操作を行ったかのようです。

デカルトの場合の図

または、画像が反対方向に回転したようにUIImageOrientationLeft見えますが、切り抜き長方形は同じままです。

左向きの場合の図

訂正

それはすべて非常に奇妙で、何がうまくいかなかったのか理解できません。

しかし、修正はかなり簡単に思えます: クリップの四角形を反転するだけです。上記のように計算した後:

// flip the transformedCropBox in the image
transformedCropBox.origin.y = CGImageGetHeight(defaultImage) - CGRectGetMaxY(transformedCropBox);

それは動作しますか?(この場合、および他の向きの画像の場合は?)

于 2012-12-30T11:21:13.310 に答える