为什么AVCaptureVideoOrientation风景模式导致颠倒的静止图像?

我正在使用AVFoundation类来实现我的应用程序中的自定义相机。 我只是捕捉静止图像,而不是video。 我有一切工作,但是被某些东西困住了。 当拍摄静止图像时,我会考虑设备方向,并适当地设置video连接的video方向。 代码片段:

// set the videoOrientation based on the device orientation to // ensure the pic is right side up for all orientations AVCaptureVideoOrientation videoOrientation; switch ([UIDevice currentDevice].orientation) { case UIDeviceOrientationLandscapeLeft: // Not clear why but the landscape orientations are reversed // if I use AVCaptureVideoOrientationLandscapeLeft here the pic ends up upside down videoOrientation = AVCaptureVideoOrientationLandscapeRight; break; case UIDeviceOrientationLandscapeRight: // Not clear why but the landscape orientations are reversed // if I use AVCaptureVideoOrientationLandscapeRight here the pic ends up upside down videoOrientation = AVCaptureVideoOrientationLandscapeLeft; break; case UIDeviceOrientationPortraitUpsideDown: videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; break; default: videoOrientation = AVCaptureVideoOrientationPortrait; break; } videoConnection.videoOrientation = videoOrientation; 

请注意我在景观案例中的评论。 我必须扭转方向映射或者产生的图像颠倒。 我用下面的代码捕获并保存图像:

 [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; self.stillImage = [UIImage imageWithData:imageData]; // notify observers (image gets saved to the camera roll) [[NSNotificationCenter defaultCenter] postNotificationName:CaptureSessionManagerDidCaptureStillImageNotification object:self]; self.stillImage = nil; }]; 

没有其他image processing或操纵。

我的应用程序使用上面的代码。 我只是想了解为什么方向常数必须颠倒的风景取向。 谢谢!

嘿,似乎没有人在这个问题上打瞌睡。 原来,答案很简单。 通过stillImageOutput captureStillImageAsynchronouslyFromConnection:...方法捕获的图像始终以下列属性结束:

  • UIImage方向=始终UIImageOrientationRight无论设备的方向
  • UIImage size = W x H(例如,纵向宽度x纵向高度,取决于您的相机分辨率)
  • CGImage大小=取决于设备的方向(如纵向或横向)

因此,旋转图像的解决scheme是将设备方向与CGImage大小配合使用以应用适当的仿射变换。 当我回答我自己的问题时,我不是代码中的解决scheme,但是我最终编写了一个名为“

 - (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation 

在包含各种image processing增强function的UIImage类别中。

编辑 – 实施例

我已经收到了许多function代码的请求。 我已经从一个工作的应用程序中提取相关的实现。

 // this method is implemented in your capture session manager (wherever AVCaptureSession is used) // capture a still image and save the device orientation - (void)captureStillImage { UIDeviceOrientation currentDeviceOrientation = UIDevice.currentDevice.orientation; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; if (imageData) { UIImage *image = [UIImage imageWithData:imageData]; NSDictionary *captureInfo = { @"image" : image, @"deviceOrientation" : @(currentDeviceOrientation) }; // TODO: send image & orientation to delegate or post notification to observers } else { // TODO: handle image capture error } }]; } // this method rotates the UIImage captured by the capture session manager based on the // device orientation when the image was captured - (UIImage *)imageRotatedUpFromCaptureInfo:(NSDictionary *)captureInfo { UIImage *image = [captureInfo objectForKey:@"image"]; UIDeviceOrientation deviceOrientation = [[captureInfo objectForKey:@"deviceOrientation"] integerValue]; UIImageOrientation rotationOrientation = [self rotationNeededForImageCapturedWithDeviceOrientation:deviceOrientation]; // TODO: scale the image if desired CGSize newSize = image.size; return [imageScaledToSize:newSize andRotatedByOrientation:rotationOrientation]; } // return a scaled and rotated an image - (UIImage *)imageScaledToSize:(CGSize)newSize andRotatedByOrientation:(UIImageOrientation)orientation { CGImageRef imageRef = self.CGImage; CGRect imageRect = CGRectMake(0.0, 0.0, newSize.width, newSize.height); CGRect contextRect = imageRect; CGAffineTransform transform = CGAffineTransformIdentity; switch (orientation) { case UIImageOrientationDown: { // rotate 180 deg transform = CGAffineTransformTranslate(transform, imageRect.size.width, imageRect.size.height); transform = CGAffineTransformRotate(transform, M_PI); } break; case UIImageOrientationLeft: { // rotate 90 deg left contextRect = CGRectTranspose(contextRect); transform = CGAffineTransformTranslate(transform, imageRect.size.height, 0.0); transform = CGAffineTransformRotate(transform, M_PI / 2.0); } break; case UIImageOrientationRight: { // rotate 90 deg right contextRect = CGRectTranspose(contextRect); transform = CGAffineTransformTranslate(transform, 0.0, imageRect.size.width); transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0); } break; case UIImageOrientationUp: // no rotation default: break; } CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); CGColorSpaceRef colorSpaceRef = CGImageGetColorSpace(imageRef); // madify bitmapInfo to work with PNG if necessary if (bitmapInfo == kCGImageAlphaNone) { bitmapInfo = kCGImageAlphaNoneSkipLast; } else if (bitmapInfo == kCGImageAlphaLast) { bitmapInfo = kCGImageAlphaPremultipliedLast; } // Build a context that's the same dimensions as the new size CGContextRef context = CGBitmapContextCreate(NULL, contextRect.size.width, contextRect.size.height, CGImageGetBitsPerComponent(imageRef), 0, colorSpaceRef, bitmapInfo); CGContextConcatCTM(context, transform); CGContextDrawImage(context, imageRect, imageRef); // Get the rotated image from the context and a UIImage CGImageRef rotatedImageRef = CGBitmapContextCreateImage(context); UIImage *rotatedImage = [UIImage imageWithCGImage:rotatedImageRef]; // Clean up CGImageRelease(rotatedImageRef); CGContextRelease(context); return rotatedImage; } // return the UIImageOrientation needed for an image captured with a specific deviceOrientation - (UIImageOrientation)rotationNeededForImageCapturedWithDeviceOrientation:(UIDeviceOrientation)deviceOrientation { UIImageOrientation rotationOrientation; switch (deviceOrientation) { case UIDeviceOrientationPortraitUpsideDown: { rotationOrientation = UIImageOrientationLeft; } break; case UIDeviceOrientationLandscapeRight: { rotationOrientation = UIImageOrientationDown; } break; case UIDeviceOrientationLandscapeLeft: { rotationOrientation = UIImageOrientationUp; } break; case UIDeviceOrientationPortrait: default: { rotationOrientation = UIImageOrientationRight; } break; } return rotationOrientation; } 

至于你为什么需要AVCaptureVideoOrientationLandscape当设备的方向是UIDeviceOrientationLandscape ,这是因为,由于某种原因,UIDeviceOrientationLandscape == UIInterfaceOrientationLandscape 正确 ,而AVCaptureVideoOrientations遵循UIInterfaceOrientation约定。

此外,UIDeviceOrientation包含其他选项,如UIDeviceOrientationFaceUp,UIDeviceOrientationFaceDown和UIDeviceOrientationUnknown。 如果您将界面旋转以匹配设备的方向,则可以尝试从[UIApplication sharedApplication] .statusBarOrientation获取UIDeviceOrientation。

实际上,你的实现没有错误,你可以消除整个交换机的情况。

为什么切换案例可以消除:

设备和video方向的相应值在数值上相等,即只要设备方向不是未知的,面朝上或面朝下 ,就可以将UIDeviceOrientation和AVCaptureVideoOrientation

关于混淆的术语:

设备方向LandscapeLeft =>手机的顶部在左侧。

所以,想象一下,video的起点在哪里……没错,右上angular=>右转。

同样在另一种风景方向,video方向远离手机的顶部。 这不是在肖像和颠倒的情况下混淆

certificate

捕捉静止图像时,检查方向EXIF值。 对于左侧的景观,EXIF方向为3(180度反转)对于右侧景观,EXIF方向为1(不旋转)

当你显示时,你的exif方向被转换成相应的UIImageOrientation,所以不应该有倒置。

在前置摄像头的情况下,我注意到EXIF值,只需按照video方向值(镜像效果不被照顾)。 只使用EXIF的1,3,6,8个值。

另外,镜子总是沿着从设备的顶部到底部的线条。所以,景观将看起来颠倒,而肖像和颠倒的肖像只是镜像

Interesting Posts