为什么AVCaptureVideoOrientation横向模式会导致静止图像颠倒? [英] Why does AVCaptureVideoOrientation landscape modes result in upside down still images?

查看:2671
本文介绍了为什么AVCaptureVideoOrientation横向模式会导致静止图像颠倒?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using AVFoundation classes to implement a custom camera in my app. I am only capturing still images, not video. I have everything working but am stumped by something. I take into account the device orientation when a still image is captured and set the videoOrientation of the video connection appropriately. A code snippet:

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

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

Note my comments in the landscape cases. I have to reverse the orientation mapping or the resulting image is upside down. I capture and save the image with the following code:

[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;
}];

没有其他图像处理或操纵。

There is no other image processing or manipulation.

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

My app works with the code above. I'm just trying to understand why the orientation constants must be reversed for landscape orientations. Thanks!

推荐答案

嘿,似乎没有人觉得在这个问题上有点兴趣。事实证明答案很简单。通过 stillImageOutput captureStillImageAsynchronouslyFromConnection:... 方法捕获的图像总是最终得到以下属性:

Heh, it seems nobody felt like chiming in on this one. Turns out the answer is straightforward. Images captured via the stillImageOutput captureStillImageAsynchronouslyFromConnection:... method always end up with the following properties:


  • UIImage方向=总是UIImageOrientationRight,无论设备方向如何

  • UIImage尺寸= W x H(例如纵向宽度x纵向高度,取决于您的相机分辨率)

  • CGImage size =取决于设备方向(例如肖像或风景)

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

So the solution to rotate the image up is to use the device orientation in conjunction with the CGImage size to apply an appropriate affine transform. As I'm answering my own question, I'm not the solution in code but I ended up writing a routine called:

- (UIImage *)imageRotatedUpForDeviceOrientation:(UIDeviceOrientation)deviceOrientation

在含有各种图像处理的增强一个UIImage类别。

in a UIImage category containing various image processing enhancements.

编辑 - 实施示例

我收到了一些请求关于此的功能代码。我已从工作应用程序中提取相关实现。

I've received a number of requests for functional code on this. I've extracted the relevant implementation from a working app.

// 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;
}

这篇关于为什么AVCaptureVideoOrientation横向模式会导致静止图像颠倒?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆