AVFoundation 图像方向在预览中偏离了 90 度,但在相机胶卷中很好
Posted
技术标签:
【中文标题】AVFoundation 图像方向在预览中偏离了 90 度,但在相机胶卷中很好【英文标题】:AVFoundation Image orientation off by 90 degrees in the preview but fine in Camera roll 【发布时间】:2013-04-11 19:02:48 【问题描述】:发生了一些非常奇怪的事情,我正在尝试使用 AVFoundation 捕获图像,相机胶卷图像看起来还不错,但图像预览将图像旋转了 90 度。
这是我用来捕捉图像的代码
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections)
for (AVCaptureInputPort *port in [connection inputPorts])
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
videoConnection = connection;
break;
if (videoConnection)
break;
//NSLog(@"about to request a capture from: %@", stillImageOutput);
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error)
CFDictionaryRef exifAttachments = CMGetAttachment( imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments)
// Do something with the attachments.
//NSLog(@"attachements: %@", exifAttachments);
else
NSLog(@"no attachments");
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
self.vImage.image = image;
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
];
【问题讨论】:
为什么顺便投了反对票?解决方案是微不足道的吗?如果是请赐教!这是一个学习不被回避的地方。我正在尽我最大的努力学习,就像你在某个时间点所做的一样! 已经让这个预览工作正常,如果是,请与我们分享这个 ***.com/a/69580115/9440709 【参考方案1】:是的,当您以设备的纵向方向捕获图像并在应用程序中使用该图像时会发生这种情况,因为图像的默认方向是任何 ios 设备中的横向,因此您需要在选择后更改图像的方向在您的应用中使用的图库。
我已经放了代码来实现这个
Objective-C 代码
- (UIImage *)fixOrientationOfImage:(UIImage *)image
// No-op if the orientation is already correct
if (image.imageOrientation == UIImageOrientationUp) return image;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (image.imageOrientation)
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, image.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
break;
switch (image.imageOrientation)
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, image.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationUp:
case UIImageOrientationDown:
case UIImageOrientationLeft:
case UIImageOrientationRight:
break;
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height,
CGImageGetBitsPerComponent(image.CGImage), 0,
CGImageGetColorSpace(image.CGImage),
CGImageGetBitmapInfo(image.CGImage));
CGContextConcatCTM(ctx, transform);
switch (image.imageOrientation)
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,image.size.width,image.size.height), image.CGImage);
break;
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
Swift 代码
func fixOrientationOfImage(image: UIImage) -> UIImage?
if image.imageOrientation == .Up
return image
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
var transform = CGAffineTransformIdentity
switch image.imageOrientation
case .Down, .DownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
case .Left, .LeftMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
case .Right, .RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, image.size.height)
transform = CGAffineTransformRotate(transform, -CGFloat(M_PI_2))
default:
break
switch image.imageOrientation
case .UpMirrored, .DownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformScale(transform, -1, 1)
case .LeftMirrored, .RightMirrored:
transform = CGAffineTransformTranslate(transform, image.size.height, 0)
transform = CGAffineTransformScale(transform, -1, 1)
default:
break
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
guard let context = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage).rawValue) else
return nil
CGContextConcatCTM(context, transform)
switch image.imageOrientation
case .Left, .LeftMirrored, .Right, .RightMirrored:
CGContextDrawImage(context, CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width), image.CGImage)
default:
CGContextDrawImage(context, CGRect(origin: .zero, size: image.size), image.CGImage)
// And now we just create a new UIImage from the drawing context
guard let CGImage = CGBitmapContextCreateImage(context) else
return nil
return UIImage(CGImage: CGImage)
Swift 3.0
func fixOrientationOfImage(image: UIImage) -> UIImage?
if image.imageOrientation == .up
return image
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
var transform = CGAffineTransform.identity
switch image.imageOrientation
case .down, .downMirrored:
transform = transform.translatedBy(x: image.size.width, y: image.size.height)
transform = transform.rotated(by: CGFloat(Double.pi))
case .left, .leftMirrored:
transform = transform.translatedBy(x: image.size.width, y: 0)
transform = transform.rotated(by: CGFloat(Double.pi / 2))
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: image.size.height)
transform = transform.rotated(by: -CGFloat(Double.pi / 2))
default:
break
switch image.imageOrientation
case .upMirrored, .downMirrored:
transform = transform.translatedBy(x: image.size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: image.size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
default:
break
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
guard let context = CGContext(data: nil, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: image.cgImage!.bitsPerComponent, bytesPerRow: 0, space: image.cgImage!.colorSpace!, bitmapInfo: image.cgImage!.bitmapInfo.rawValue) else
return nil
context.concatenate(transform)
switch image.imageOrientation
case .left, .leftMirrored, .right, .rightMirrored:
context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
default:
context.draw(image.cgImage!, in: CGRect(origin: .zero, size: image.size))
// And now we just create a new UIImage from the drawing context
guard let CGImage = context.makeImage() else
return nil
return UIImage(cgImage: CGImage)
【讨论】:
Dipen,是的,我最终使用了类似的东西。但我面临另一个问题,我希望将保存的图像裁剪为我在方形视频容器中显示的内容。但事实并非如此,它甚至保存了框中不可见的图像部分。 因为你需要使用一些裁剪方法来裁剪图像,这些方法在 QuartzCore 框架中可用。 @DipenPanchasara - 很棒的东西!!你很棒,迪彭。我需要一些关于这里的逻辑的帮助。我将它复制到我的项目中并使用它,“瞧”我的图像被旋转了。耶!但是当执行到重绘部分时,方向不是我需要的。我试图了解这里的每一步都发生了什么,但还没有得到它。我会继续研究,但如果您或其他人可以插话并启发我,我会很高兴。再次感谢您提供这么棒的帖子! 谢谢先生!我一直在为此寻找几天..现在我只需要找到修复在 stillImageOutput 被触发后 UIImageView 拉伸的代码哈哈。非常感谢! 图像出现黑屏【参考方案2】:接受的答案有效,但比它需要的复杂得多。您可以使用以下方法来旋转图像。
- (UIImage *)cropImage:(UIImage*)image toRect:(CGRect)rect
CGFloat (^rad)(CGFloat) = ^CGFloat(CGFloat deg)
return deg / 180.0f * (CGFloat) M_PI;
;
// determine the orientation of the image and apply a transformation to the crop rectangle to shift it to the correct position
CGAffineTransform rectTransform;
switch (image.imageOrientation)
case UIImageOrientationLeft:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -image.size.height);
break;
case UIImageOrientationRight:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -image.size.width, 0);
break;
case UIImageOrientationDown:
rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -image.size.width, -image.size.height);
break;
default:
rectTransform = CGAffineTransformIdentity;
;
// adjust the transformation scale based on the image scale
rectTransform = CGAffineTransformScale(rectTransform, image.scale, image.scale);
// apply the transformation to the rect to create a new, shifted rect
CGRect transformedCropSquare = CGRectApplyAffineTransform(rect, rectTransform);
// use the rect to crop the image
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, transformedCropSquare);
// create a new UIImage and set the scale and orientation appropriately
UIImage *result = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:image.imageOrientation];
// memory cleanup
CGImageRelease(imageRef);
return result;
要只旋转图像而不是裁剪,您可以这样简单地调用它:
UIImage *image;
[self cropImage:image toRect:rect.bounds];
【讨论】:
@decades 它不适用于哪个方向和设备?另外,请记住,这是查看图像的方向,所以如果它没有记录到图像中(如果你围绕 AV 框架构建自己的框架,我相信你必须手动设置),这将不起作用。 我正在尝试将您的代码与 captureStillImageAsynchronouslyFromConnection 的结果结合使用。也许我在这里错了 最终答案 17 以及我评论的更改是我的问题的完美解决方案***.com/questions/7845520/… @decades 有没有办法旋转requestImageDataForAsset得到的数据 @TejasPadliya 在结果处理程序中,您可以使用[[UIImage alloc] initWithData:data]
从 NSData
对象实例化 UIImage
【参考方案3】:
Dipen Panchasara 的回答很棒,但可能存在一个问题。当您处理大图像(例如来自 iPhoneX)时,会出现大量内存峰值,这在某些情况下可能会成为问题。
所以,也许你想改变那行:
context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
对于一些内存优化的东西。例如,这将在 16 (4*4) 步中中断图像绘制,从而显着减少内存消耗:
let partInAxis: CGFloat = 4
let partWidth = image.size.height/partInAxis
let partHeight = image.size.width/partInAxis
for i in 0...Int(partInAxis)-1
for j in 0...Int(partInAxis)-1
let partialImage = image.cgImage?.cropping(to: CGRect(x: CGFloat(i)*partWidth, y: CGFloat(j)*partHeight, width: partWidth, height: partHeight))
context.draw(partialImage!, in: CGRect(x: CGFloat(i)*partWidth, y: CGFloat(Int(partInAxis)-1-j)*partHeight, width: partWidth, height: partHeight))
请注意,在这种情况下,图像的高度和宽度必须除以 4。
【讨论】:
【参考方案4】:Swift 5.5 +
您应该在捕获图像之前设置输出的方向。
// set the image orientation in output
if let photoOutputConnection = self.photoOutput.connection(with: .video)
photoOutputConnection.videoOrientation = videoPreviewLayerOrientation!
self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureProcessor) // capture image
【讨论】:
【参考方案5】:我建议使用Yodagama's answer,因为它是单行且更清洁的。但是,如果您需要以另一种方式更正方向,这里是 Dipen's answer,没有强制选项,作为 UIImage 扩展。要获得更好的内存优化,请将其与this answer 混合使用。
extension UIImage
var fixedOrientation: UIImage?
if imageOrientation == .up
return self
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
var transform = CGAffineTransform.identity
switch imageOrientation
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: .pi)
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: .pi / 2)
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: -.pi / 2)
default: break
switch imageOrientation
case .upMirrored, .downMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
default: break
guard let cgImage = cgImage,
let colorSpace = cgImage.colorSpace else
return nil
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
let context = CGContext(
data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: cgImage.bitsPerComponent,
bytesPerRow: 0,
space: colorSpace,
bitmapInfo: cgImage.bitmapInfo.rawValue
)
guard let context = context else
return nil
context.concatenate(transform)
switch imageOrientation
case .left, .leftMirrored, .right, .rightMirrored:
context.draw(cgImage, in: .init(x: 0, y: 0, width: size.height, height: size.width))
default:
context.draw(cgImage, in: .init(origin: .zero, size: size))
// And now we just create a new UIImage from the drawing context
guard let newCgImage = context.makeImage() else
return nil
return .init(cgImage: newCgImage)
【讨论】:
以上是关于AVFoundation 图像方向在预览中偏离了 90 度,但在相机胶卷中很好的主要内容,如果未能解决你的问题,请参考以下文章