私が書いている私のiOSアプリケーションでは、アルファチャネルを扱うため、PNGを扱います。何らかの理由で、PNGをimageView
に問題なくロードできますが、アプリケーションから(PasteBoardに)画像をコピーするか、画像をカメラロールに保存するとき、画像90度回転します。
私はこれについてあらゆる場所を検索しましたが、私が学んだことの1つは、JPEGを使用した場合、EXIF情報により、この問題は発生しない(音がする)ことです。
私のアプリには完全なコピー/貼り付け機能があり、ここにキッカーがあります(これを簡単に説明するので、わかりやすくなります)。
上記の手順2に戻って[保存]をクリックすると、写真がライブラリに保存されますが、90度回転しているため、コピーと貼り付けのコードがここで問題ないことを100%確信しています。
さらに奇妙なのは、インターネットからダウンロードした画像では問題なく動作するように見えますが、電話で手動で撮った画像では非常に不評です。機能するものもあれば、機能しないものもあります...
誰かこれについて何か考えはありますか?使用できる回避策はありますか?私の画像の75%で機能するというコードには、かなり自信があります。リクエストに応じてコードを投稿することもできます。
数日かかったが、@ Dondragmerが投稿した回答のおかげで、ようやくそれを理解した。しかし、私は自分の完全な解決策を投稿すると思いました。
したがって、基本的には画像をインテリジェントに自動回転させるメソッドを作成する必要がありました。欠点は、コード全体でこのメソッドを呼び出す必要があり、特にモバイルデバイスで作業する場合は一種のプロセッサ負荷が高いことですが、プラス面は、画像の取得、画像のコピー、画像の貼り付け、および画像の保存ができることです。それらはすべて正しく回転します。これが私が最終的に使用したコードです(この方法はまだ100%完了していませんが、それでもメモリリークを編集する必要があります)。
初めて画像がアプリケーションに挿入されたのは初めてでした(ユーザーが「画像を撮る」、「画像を貼り付ける」、または「画像を選択する」を押したことが原因であるかどうかに関係なく、何らかの理由で自動でなくても問題なく挿入されました)回転します。この時点で、回転値がimageOrientationWhenAddedToScreen
というグローバル変数に保存されていました。これにより、画像を操作してプログラムから画像を保存するときに、このキャッシュされたグローバル変数を確認し、画像を適切に回転する必要があるかどうかを判断しました。
- (UIImage*) rotateImageAppropriately:(UIImage*) imageToRotate {
//This method will properly rotate our image, we need to make sure that
//We call this method everywhere pretty much...
CGImageRef imageRef = [imageToRotate CGImage];
UIImage* properlyRotatedImage;
if (imageOrientationWhenAddedToScreen == 0) {
//Don't rotate the image
properlyRotatedImage = imageToRotate;
} else if (imageOrientationWhenAddedToScreen == 3) {
//We need to rotate the image back to a 3
properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:3];
} else if (imageOrientationWhenAddedToScreen == 1) {
//We need to rotate the image back to a 1
properlyRotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:1];
}
return properlyRotatedImage;
}
Appleにこの奇妙な画像回転動作がある理由はまだ100%わかりません(これを試してください...携帯電話を裏返して写真を撮ると、最後の写真が表示されます。絵は右側が上になる-おそらくこれがAppleにこのタイプの機能があるのか?)理由です。
私はこれを理解するために多くの時間を費やしたことを知っているので、それが他の人々に役立つことを願っています!
Swiftソリューションが必要な場合は、UIImageの拡張機能を作成し、次のメソッドを追加します。
func correctlyOrientedImage() -> UIImage {
if self.imageOrientation == .up {
return self
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage ?? self;
}
既存の画像imageOrientation
プロパティが原因で問題が発生している場合は、次のように、方向が異なる他の点では同一の画像を作成できます。
CGImageRef imageRef = [sourceImage CGImage];
UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];
置換後の画像に設定する向きだけを試して、場合によっては開始時の向きに基づいて切り替える必要があるかもしれません。
また、メモリ使用量にも注意してください。写真アプリが不足することがよくあります。これにより、ソース画像を解放するまで、写真ごとにストレージが2倍になります。
この「奇妙な回転」の振る舞いは、実際にはそれほど変ではありません。それはスマートで、スマートとはメモリ効率が良いという意味です。 iOSデバイスを回転させると、カメラハードウェアも一緒に回転します。あなたが写真を撮ると、その写真はキャプチャされますが、カメラの向きは異なります。 UIImageは、本来あるべき方向を追跡するだけで、コピーせずにこの生の画像データを使用できます。 UIImagePNGRepresentation()
を使用すると、この方向データが失われ、PNGが取得されます。カメラで撮影された下の画像の。回転する代わりにこれを修正するには、元の画像にそれ自体を新しいコンテキストに描画し、そのコンテキストから適切な方向のUIImage
を取得するように指示できます。
UIImage *image = ...;
//Have the image draw itself in the correct orientation if necessary
if(!(image.imageOrientation == UIImageOrientationUp ||
image.imageOrientation == UIImageOrientationUpMirrored))
{
CGSize imgsize = image.size;
UIGraphicsBeginImageContext(imgsize);
[image drawInRect:CGRectMake(0.0, 0.0, imgsize.width, imgsize.height)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
NSData *png = UIImagePNGRepresentation(image);
これを実現するもう1つの方法を次に示します。
@IBAction func rightRotateAction(sender: AnyObject) {
let imgToRotate = CIImage(CGImage: sourceImageView.image?.CGImage)
let transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
let rotatedImage = imgToRotate.imageByApplyingTransform(transform)
let extent = rotatedImage.extent()
let contex = CIContext(options: [kCIContextUseSoftwareRenderer: false])
let cgImage = contex.createCGImage(rotatedImage, fromRect: extent)
adjustedImage = UIImage(CGImage: cgImage)!
UIView.transitionWithView(sourceImageView, duration: 0.5, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.sourceImageView.image = self.adjustedImage
}, completion: nil)
}
画像I/Oを使用して、PNG画像をファイル(またはNSMutableData
)に保存し、画像の向きを指定できます。以下の例では、PNG画像をpath
のファイルに保存します。
- (BOOL)savePngFile:(UIImage *)image toPath:(NSString *)path {
NSData *data = UIImagePNGRepresentation(image);
int exifOrientation = [UIImage cc_iOSOrientationToExifOrientation:image.imageOrientation];
NSDictionary *metadata = @{(__bridge id)kCGImagePropertyOrientation:@(exifOrientation)};
NSURL *url = [NSURL fileURLWithPath:path];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
if (!source) {
return NO;
}
CFStringRef UTI = CGImageSourceGetType(source);
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)url, UTI, 1, NULL);
if (!destination) {
CFRelease(source);
return NO;
}
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metadata);
BOOL success = CGImageDestinationFinalize(destination);
CFRelease(destination);
CFRelease(source);
return success;
}
cc_iOSOrientationToExifOrientation:
はUIImage
カテゴリのメソッドです。
+ (int)cc_iOSOrientationToExifOrientation:(UIImageOrientation)iOSOrientation {
int exifOrientation = -1;
switch (iOSOrientation) {
case UIImageOrientationUp:
exifOrientation = 1;
break;
case UIImageOrientationDown:
exifOrientation = 3;
break;
case UIImageOrientationLeft:
exifOrientation = 8;
break;
case UIImageOrientationRight:
exifOrientation = 6;
break;
case UIImageOrientationUpMirrored:
exifOrientation = 2;
break;
case UIImageOrientationDownMirrored:
exifOrientation = 4;
break;
case UIImageOrientationLeftMirrored:
exifOrientation = 5;
break;
case UIImageOrientationRightMirrored:
exifOrientation = 7;
break;
default:
exifOrientation = -1;
}
return exifOrientation;
}
あるいは、NSData
を使用してCGImageDestinationCreateWithData
に画像を保存し、NSMutableData
の代わりにNSURL
をCGImageDestinationCreateWithURL
に渡すこともできます。