AVFoundation图像方向在预览中偏离90度,但在相机胶卷中很好

时间:2013-04-11 19:02:48

标签: ios avfoundation

发生了一些非常奇怪的事情,我正在尝试使用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);
 }];

3 个答案:

答案 0 :(得分:49)

是的,当您在设备的纵向方向捕获图像并在应用程序中使用该图像时会发生这种情况,因为图像的默认方向是任何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)
}

答案 1 :(得分:1)

接受的答案有效,但要比它需要的复杂得多。您可以使用以下内容旋转图像。

/*Use this after the jquery require in this file*/

要旋转图像而不是裁剪,您可以简单地调用它:

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

答案 2 :(得分:0)

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。