14

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

5 回答 5

50

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

斯威夫特 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)
}
于 2013-04-18T04:39:04.063 回答
1

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

- (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];
于 2015-06-15T18:16:55.753 回答
1

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。

于 2019-03-04T13:42:00.343 回答
1

斯威夫特 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
于 2021-10-15T04:49:46.703 回答
0

我建议使用Yodagama 的答案,因为它是单行且更清洁的。但是,如果您需要以另一种方式更正方向,这是Dipen 的答案,没有强制选项,作为 UIImage 扩展。为了更好的内存优化,将它与这个答案混合。

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)
    }
}
于 2021-11-15T17:08:15.903 回答