Avfoundation Image Orientation Off by 90 Degrees in the Preview But Fine in Camera Roll

AVFoundation Image orientation off by 90 degrees in the preview but fine in Camera roll

yes ,it happens when you capture image in Portrait orientation of your device and use that image in your application, because the default orientation of image is Landscape in any IOS Device, so you need to change the orientation of image after picking from Gallery to use in your app.

I have put code to achieve this

Objective-C code

- (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 code

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)
}

Swift - AVFoundation capture image orientation

You need to run the following code before calling capturePhoto()

if let photoOutputConnection = CustomCamera.photoOutput.connection(with: AVMediaType.video) {
photoOutputConnection.videoOrientation = videoDeviceOrientation
}

You could use your managePhotoOrientation() function to get videoDeviceOrientation.

Realtime capture using AVFoundation rotated 90º in AVCaptureVideoPreviewLayer

Found that the videoOrientation attribute of the preview layer connection needed to be set.

Here's the change to my addVideoPreviewLayer method...

- (void)addVideoPreviewLayer {
[self setPreviewLayer:[[[AVCaptureVideoPreviewLayer alloc] initWithSession:[self captureSession]] autorelease]];
[[self previewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[[previewLayer connection] setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
}

AVFoundation CaptureStillImage issue with orientation of result image

Try using something like this, I use this to transform the buttons on my view when I rotate the device. This can be used with the image taken. Recall your standard transformation rotation matrices from linear algebra if you have ever taken that class. Basically, just alter this code to rotate the frame of the image you have captured and then make sure you set its frame bounds back to the view's frame in order to have it fit back correctly in the view. This may be more complicated than need be, But I have never had this issue before with images. Keep in mind this is being called due to a rotation change observer that has been registered, which you may not need to use. But this underlying concept is used A LOT

Any time I have rotation issues, I use this.

/**************************************************************************
DEVICE ORIENTATION DID CHANGE
**************************************************************************/
func deviceOrientationDidChange() {

println("DEVICE ORIENTATION DID CHANGE CALLED")

let orientation: UIDeviceOrientation = UIDevice.currentDevice().orientation

//------ IGNORE THESE ORIENTATIONS ------
if orientation == UIDeviceOrientation.FaceUp || orientation == UIDeviceOrientation.FaceDown || orientation == UIDeviceOrientation.Unknown || orientation == UIDeviceOrientation.PortraitUpsideDown || self.currentOrientation == orientation {

println("device orientation does not need to change --- returning...")

return
}

self.currentOrientation = orientation

//------ APPLY A ROTATION USING THE STANDARD ROTATION TRANSFORMATION MATRIX in R3 ------

/*

x y z
--- ---
x | cosø sinø 0 |
y | -sinø consø 0 |
z | 0 0 1 |
--- ---

*/

//----- PERFORM BUTTON AND VIDEO DATA BUFFER ROTATIONS ------

switch orientation {

case UIDeviceOrientation.Portrait:

println("Device Orientation Portrait")

if self.usingFrontCamera == true {
}
else {

self.playBackTransformation = CGAffineTransformMakeRotation(self.degrees0)
self.switchCaptureSession.transform = self.playBackTransformation!
self.switchCaptureSession.frame = self.view.bounds
self.flipCamera.transform = self.playBackTransformation!
self.flipCamera.frame = self.view.bounds
self.captureButton.transform = self.playBackTransformation!
self.captureButton.frame = self.view.bounds
}

break

case UIDeviceOrientation.LandscapeLeft:

println("Device Orientation LandScapeLeft")

if self.usingFrontCamera == true {
}
else {

self.playBackTransformation = CGAffineTransformMakeRotation(CGFloat(self.degrees90))
self.switchCaptureSession.transform = self.playBackTransformation!
self.switchCaptureSession.frame = self.view.bounds
self.flipCamera.transform = self.playBackTransformation!
self.flipCamera.frame = self.view.bounds
self.captureButton.transform = self.playBackTransformation!
self.captureButton.frame = self.view.bounds
}

break

case UIDeviceOrientation.LandscapeRight:

println("Device Orientation LandscapeRight")

if self.usingFrontCamera == true {
}
else {

self.playBackTransformation = CGAffineTransformMakeRotation(-self.degrees90)
self.switchCaptureSession.transform = self.playBackTransformation!
self.switchCaptureSession.frame = self.view.bounds
self.flipCamera.transform = self.playBackTransformation!
self.flipCamera.frame = self.view.bounds
self.captureButton.transform = self.playBackTransformation!
self.captureButton.frame = self.view.bounds
}

break

default:

break
}

Affine Transformation are great for rotations. There may be a function that sets the default image orientation when captured. Check Apple's references.

AVFoundation Camera Preview Wrong Orientation

The layer’s orientation. (Deprecated in iOS 6.0. Use videoOrientation
(AVCaptureConnection) instead.)

so use:

[[AVCaptureVideoPreviewLayer connection] setVideoOrientation: AVCaptureVideoOrientationLandscapeRight];

or

AVCaptureVideoPreviewLayer.connection.videoOrientation= AVCaptureVideoOrientationLandscapeRight;

AVFoundation, cut off edges in the preview layer

This is because the aspect ratio of the video that you're capturing is different to that of the screen. There's not much you can do - it's either letter boxing or non uniform scaling of the image. You can throw away some pixels on the short edge, which is probably what UIImagePickerController does.

If you want this behaviour, try setting

  previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

Why does AVCaptureVideoOrientation landscape modes result in upside down still images?

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 orientation = always UIImageOrientationRight regardless of device orientation
  • UIImage size = W x H (e.g. portrait width x portrait height, depends on your camera resolution)
  • CGImage size = depends on device orientation (e.g. portrait or landscape)

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

in a UIImage category containing various image processing enhancements.

EDIT - Implementation Example

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

AVCaptureMovieFileOutput causing wrong orientation when saving photo to Camera Roll

I'm wondering if you the newly created image really has it's orientation set as you are assuming here:

[library writeImageToSavedPhotosAlbum:[image CGImage] orientation: (ALAssetOrientation) [image imageOrientation] completionBlock:completionBlock];

[image imageOrientation] in particular seems potentially the source of the problem, especially with the cast required...

you say you are seeing the image orientation somewhere, is it from [image imageOrientation] just after creating it with:

NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];

I'm wondering if you are assuming that this metadata is in the imageData returned from AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: when that in fact only contains the image pixel data itself?



Related Topics



Leave a reply



Submit