I'm using custom camera implementation in my app using Swift.
When the image is captured, is called func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?). I get the image data with photo.fileDataRepresentation() and after that I'm fixing the orientation of the photo using the following extension on UIImage.
func fixedOrientation() -> UIImage? {
guard imageOrientation != UIImage.Orientation.up else {
// This is default orientation, don't need to do anything
return self.copy() as? UIImage
}
guard let cgImage = self.cgImage else {
// CGImage is not available
return nil
}
guard let colorSpace = cgImage.colorSpace, let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
return nil // Not able to create CGContext
}
var transform = CGAffineTransform.identity
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi / 2.0)
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: CGFloat.pi / -2.0)
case .up, .upMirrored:
break
#unknown default:
break
}
// Flip image one more time if needed to, this is to prevent flipped image
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)
case .up, .down, .left, .right:
break
#unknown default:
break
}
ctx.concatenate(transform)
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
}
guard let newCGImage = ctx.makeImage() else { return nil }
return UIImage(cgImage: newCGImage, scale: 1, orientation: .up)
}
Apparently this is working fine for images taken with back camera, but taking with frontal one I'm facing issues.
If the selfie photo is taken in portrait, the the method returns the photo mirrored.(which is not a big deal)
If the selfie photo is taken in landscape right/left, the outcode is photo also mirrored but wrongfully rotated. Here is where I want your help, to get the correct rotation of the photo.
Note: I'm also changing the videoOrientation from AVCaptureConnection when rotating the device.
It turned out that i was changing the videoOrientation when rotation did change, which was wrong. I had to move that logic before capturing the photo. Now it works fine. Also I've fixed the mirroring problem, by setting isVideoMirrored to true if using the front camera.
Related
I'm Capturing image and storing it in NSData format. When i capture my image in portrait device orientation, the image is rotating left 90 degrees. when i'm capturing image in landscape device orientation, i'm getting image in proper manner. i trying below function. but i don't know what cgImage need to take as CGImage.
func rotateImageProperly(image:UIImage) -> UIImage {
let normalImage:UIImage!
if(image.imageOrientation == .up) {
normalImage = image
}else if (image.imageOrientation == .left){
normalImage = UIImage(cgImage: <#T##CGImage#>, scale: 1.0, orientation: UIImageOrientation.right)
}
return normalImage
}
use following code to properly rotate image :
func imageOrientation(_ src:UIImage)->UIImage {
if src.imageOrientation == UIImageOrientation.up {
return src
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch src.imageOrientation {
case UIImageOrientation.down, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: src.size.width, y: src.size.height)
transform = transform.rotated(by: CGFloat(Double.pi))
break
case UIImageOrientation.left, UIImageOrientation.leftMirrored:
transform = transform.translatedBy(x: src.size.width, y: 0)
transform = transform.rotated(by: CGFloat(Double.pi))
break
case UIImageOrientation.right, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: 0, y: src.size.height)
transform = transform.rotated(by: CGFloat(Double.pi))
break
case UIImageOrientation.up, UIImageOrientation.upMirrored:
break
}
switch src.imageOrientation {
case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
transform.translatedBy(x: src.size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
break
case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
transform.translatedBy(x: src.size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:
break
}
let ctx:CGContext = CGContext(data: nil, width: Int(src.size.width), height: Int(src.size.height), bitsPerComponent: (src.cgImage)!.bitsPerComponent, bytesPerRow: 0, space: (src.cgImage)!.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
ctx.concatenate(transform)
switch src.imageOrientation {
case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
ctx.draw(src.cgImage!, in: CGRect(x: 0, y: 0, width: src.size.height, height: src.size.width))
break
default:
ctx.draw(src.cgImage!, in: CGRect(x: 0, y: 0, width: src.size.width, height: src.size.height))
break
}
let cgimg:CGImage = ctx.makeImage()!
let img:UIImage = UIImage(cgImage: cgimg)
return img
}
Usage :
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]){
var image = info[UIImagePickerControllerOriginalImage] as! UIImage
image = self.imageOrientation(image)
}
I hate to say that I'm the new guy to iOS development but the shoe fits...
My app needs to either load an image/capture one from camera (which it does) but then needs to crop in LANDSCAPE.
I now have an extension that takes the image and shrinks it down to a landscape image (looks good if the camera is taken from landscape position OR scrunches it to a landscape if taken in portrait). It places it in my "viewfinder" as a tiny landscape image (seen below) and I can swipe outward to make it the size of the "view finder" but that's not what I need.
(the "viewfinder" I had to draw because I didn't put borders on it but you can see the thumbnail in the top left)
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
setImageToCrop(image: info[UIImagePickerControllerOriginalImage] as!
UIImage)
picker.dismiss(animated: true, completion: nil)
}
// -------------
func setImageToCrop(image:UIImage){
imageView.image = image
imageViewWidth.constant = image.size.width
imageViewHeight.constant = image.size.height
let scaleHeight = scrollView.frame.size.width/image.size.width
let scaleWidth = scrollView.frame.size.height/image.size.height
scrollView.minimumZoomScale = max(scaleWidth, scaleHeight)
scrollView.zoomScale = max(scaleWidth, scaleHeight)
self.cropButton.isHidden = false
}
// ------------------
#IBAction func cropButtonPressed(_ sender: Any) {
let scale:CGFloat = 1/scrollView.zoomScale
let x:CGFloat = scrollView.contentOffset.x * scale
let y:CGFloat = scrollView.contentOffset.y * scale
let width:CGFloat = scrollView.frame.size.width * scale
let height:CGFloat = scrollView.frame.size.height * scale
let croppedCGImage = imageView.image?.cgImage?.cropping(to: CGRect(x: x, y: y, width: width, height: height))
let croppedImage = UIImage(cgImage: croppedCGImage!)
// "now you can use croppedImage as you like...."
setImageToCrop(image: croppedImage)
}
// ---------------
And then the extension that shrinks the image...
public extension UIImage {
/// Extension to fix orientation of an UIImage without EXIF
func fixOrientation() -> UIImage {
guard let cgImage = cgImage else { return self }
if imageOrientation == .up { return self }
var transform = CGAffineTransform.identity
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat(M_PI))
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat(M_PI_2))
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: CGFloat(-M_PI_2))
case .up, .upMirrored:
break
}
switch imageOrientation {
case .upMirrored, .downMirrored:
transform.translatedBy(x: size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform.translatedBy(x: size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case .up, .down, .left, .right:
break
}
if let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: cgImage.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) {
ctx.concatenate(transform)
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
}
if let finalImage = ctx.makeImage() {
return (UIImage(cgImage: finalImage))
}
}
// something failed -- return original
return self
}
}
I have been around and around with different answers in StackOverFlow but haven't found the solution I'm looking for. I first found an extension that was the "fix all holy grail" but it did nothing. (The app still crops the image but stil rotates when it's saved.)
I'm 100% sure it's something stupid/small that I'm missing but I'm now lost in my code and if it means anything, I've been chasing this issue for WEEKS, if not MONTHS, now. I didn't want to ask unless I absolutely had to. Is there a better way to get an image and crop it it landscape without having it rotate? or is there a fix to this code?
When Image data is returned and applied to a UIImage, if the data comes from the camera then the image appears rotated 90 degrees.
I tried adding the
extension UIImage {
func correctlyOrientedImage() -> UIImage {
if self.imageOrientation == UIImageOrientation.up {
return self
}
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
self.draw(in: CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: self.size.width, height: self.size.height))
)
let normalizedImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext();
return normalizedImage;
}
}
in my code, I check to see if I have data saved for a specific user and if so I load the image data into profile_image, a UIImageView.
//profile_image
if let profile_imager = UserDefaults.standard.object(forKey: String(UserID)) as? Data {
let data = UserDefaults.standard.object(forKey: String(UserID)) as? Data
print("profile_imager: \(profile_imager)")
profile_image.image = UIImage(data: data!)
profile_image.backgroundColor = .white
}
How would I go about to use correctlyOrientedImage correctly
Thank you
Swift 4.2,
Created an UIImage extension to keep the image in the original position,
extension UIImage {
func fixedOrientation() -> UIImage {
if imageOrientation == .up {
return self
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi / 2)
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: CGFloat.pi / -2)
case .up, .upMirrored:
break
}
switch imageOrientation {
case .upMirrored, .downMirrored:
transform.translatedBy(x: size.width, y: 0)
transform.scaledBy(x: -1, y: 1)
case .leftMirrored, .rightMirrored:
transform.translatedBy(x: size.height, y: 0)
transform.scaledBy(x: -1, y: 1)
case .up, .down, .left, .right:
break
}
if let cgImage = self.cgImage, let colorSpace = cgImage.colorSpace,
let ctx: CGContext = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) {
ctx.concatenate(transform)
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
}
if let ctxImage: CGImage = ctx.makeImage() {
return UIImage(cgImage: ctxImage)
} else {
return self
}
} else {
return self
}
}
}
Then set profile_image from fixedOrientation() method,
//profile_image
if let profile_imager = UserDefaults.standard.object(forKey: String(UserID)) as? Data {
let data = UserDefaults.standard.object(forKey: String(UserID)) as? Data
print("profile_imager: \(profile_imager)")
let actualImage = UIImage(data: data!)?.fixedOrientation()
profile_image.image = actualImage
profile_image.backgroundColor = .white
}
Part of my iOS app uses Firebase Storage to store and load images taken/uploaded by the user.
However, every time I upload a picture to Firebase in portrait orientation, when I retrieve it back from Firebase later on it comes in landscape orientation and thus displays incorrectly.
I've read that Firebase stores metadata to determine the correct orientation of an uploaded picture but it doesn't seem to be working for me.
Any one know how to get the orientation the image was uploaded in without manually having to rotate it within the app's code?
Thanks!
edit for code:
Uploading:
let imageMetaData = FIRStorageMetadata()
imageMetaData.contentType = "image/png"
var imageData = Data()
imageData = UIImagePNGRepresentation(chosenImage)!
let currentUserProfilePictureRef = currentUserStorageRef.child("ProfilePicture")
currentUserProfilePictureRef.put(imageData, metadata: imageMetaData) { (metaData, error) in
if error == nil {
let downloadUrl = metaData?.downloadURL()?.absoluteString
self.currentUserRef.updateChildValues(["profilePictureUrl": downloadUrl!])
if (chosenImage.size.width.isLess(than: (chosenImage.size.height))) {
self.currentUserRef.updateChildValues(["profilePictureOrientation": "portrait"])
}
else {
self.currentUserRef.updateChildValues(["profilePictureOrientation": "landscape"])
}
}
}
Retrieving:
self.currentUserStorageRef.child("ProfilePicture").data(withMaxSize: 20*1024*1024, completion: {(data, error) in
var profilePicture = UIImage(data:data!)
if(profilePicture?.size.width.isLess(than: (profilePicture?.size.height)!))! {
if (pictureOrientation == "landscape") {
profilePicture = profilePicture?.rotated(by: Measurement(value: -90.0, unit: .degrees))
}
} else {
if (pictureOrientation == "portrait") {
profilePicture = profilePicture?.rotated(by: Measurement(value: 90.0, unit: .degrees))
}
}
self.buttonProfilePicture.setImage(profilePicture, for: .normal)
self.buttonProfilePicture.imageView?.contentMode = .scaleAspectFill
})
From my own personal experience (and asking virtually the same question a year ago), I've found that when you save an image using UIImagePNGRepresentation, it doesn't actually store the orientation data for that specific image. You should use UIImageJPEGRepresentation when you're having orientation issues. JPEG uses the exif format that specifies the orientation for an image, whereas PNG apparently does not. Your issue should be resolved if you save your image using UIImageJPEGRepresentation
Swift 5 - Update
This worked for me, just apply this on your UIImage before uploading it.
extension UIImage {
func fixedOrientation() -> UIImage? {
guard imageOrientation != UIImage.Orientation.up else {
// This is default orientation, don't need to do anything
return self.copy() as? UIImage
}
guard let cgImage = self.cgImage else {
// CGImage is not available
return nil
}
guard let colorSpace = cgImage.colorSpace, let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
return nil // Not able to create CGContext
}
var transform: CGAffineTransform = CGAffineTransform.identity
switch imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: size.width, y: size.height)
transform = transform.rotated(by: CGFloat.pi)
case .left, .leftMirrored:
transform = transform.translatedBy(x: size.width, y: 0)
transform = transform.rotated(by: CGFloat.pi / 2.0)
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: size.height)
transform = transform.rotated(by: CGFloat.pi / -2.0)
case .up, .upMirrored:
break
#unknown default:
break
}
// Flip image one more time if needed to, this is to prevent flipped image
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)
case .up, .down, .left, .right:
break
#unknown default:
break
}
ctx.concatenate(transform)
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
default:
ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
break
}
guard let newCGImage = ctx.makeImage() else { return nil }
return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
}
}
Just an update, UIImageJPEGRepresentation has changed to jpegData in the recent versions of xcode.
jpegData(compressionQuality: 1.0) 1.0 being high quality and 0 being low quality.
I have a user pick a picture to crop from their Camera Roll. I display all of these pictures as thumbnails for the user to press.
I use UIImage(CGImage: asset.fullScreenImage!.CGImage!, scale: scale, orientation: UIImageOrientation.Up) to ensure that my image remains the same way it faces in the thumbnail. With asset.fullResolutionImage! this doesn't work.
Now comes the weird part. When I take a picture with the iPhone camera, then head to my cropping ViewController, the image is rotated 90 degrees! I have no idea why.
If I leave my app, take a picture with the camera normally, save it to my camera roll, and then choose it from the list of thumbnails like I do other pictures, it works perfectly fine.
What is the cause of this? How does one fix it?
Edit: There are two solutions posted below. One in Objective-C, and the other being a translated copy of that into Swift.
You can try to use this category:
UIImage+FixOrientation.h
UIImage+FixOrientation.m
It is built in Objective-C, then you can use bridging header to use ObjC in combination with Swift, or you can just have a look to understand how to fix that.
Usage example:
UIImage *image = <image from camera image>
image = [image fixOrientation];
This answer is to complement gontovnik's answer.
I have taken his Objective-C solution and written it in Swift. Put this function wherever needed. Just pass in the UIImage in question.
That said, you could probably just make this a UIImage class function. Would actually be nice to just call UIImage.fixOrientation(image: imageInQuestion).
func fixOrientation(image: UIImage) -> UIImage {
// No-op if the orientation is already correct
if (image.imageOrientation == UIImageOrientation.Up) { return image; }
println(image.imageOrientation)
// 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))
break
case .Left, .LeftMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
break
case .Right, .RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, image.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
break
case .Up, .UpMirrored:
break
}
switch (image.imageOrientation) {
case .UpMirrored, .DownMirrored:
transform = CGAffineTransformTranslate(transform, image.size.width, 0)
transform = CGAffineTransformScale(transform, -1, 1)
break
case .LeftMirrored, .RightMirrored:
transform = CGAffineTransformTranslate(transform, image.size.height, 0)
transform = CGAffineTransformScale(transform, -1, 1)
break
case .Up, .Down, .Left, .Right:
break
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
var ctx = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage))
CGContextConcatCTM(ctx, transform);
switch (image.imageOrientation) {
case .Left, .LeftMirrored, .Right, .RightMirrored:
// 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
var cgimg = CGBitmapContextCreateImage(ctx)
var img = UIImage(CGImage: cgimg)
return img!
}
Swift 3 version
func fixOrientation(image: UIImage) -> UIImage {
// No-op if the orientation is already correct
if (image.imageOrientation == UIImageOrientation.up) { return image; }
print(image.imageOrientation)
// 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(M_PI))
break
case .left, .leftMirrored:
transform = transform.translatedBy(x: image.size.width, y: 0)
transform = transform.rotated(by: CGFloat(M_PI_2))
break
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: image.size.height)
transform = transform.rotated(by: CGFloat(-M_PI_2))
break
case .up, .upMirrored:
break
}
switch (image.imageOrientation) {
case .upMirrored, .downMirrored:
transform = transform.translatedBy(x: image.size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
break
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: image.size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
break
case .up, .down, .left, .right:
break
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
let ctx = 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)
ctx!.concatenate(transform);
switch (image.imageOrientation) {
case .left, .leftMirrored, .right, .rightMirrored:
// Grr...
ctx?.draw(image.cgImage!, in: CGRect(origin: .zero, size: CGSize(width: image.size.height, height: image.size.width)))
break
default:
ctx?.draw(image.cgImage!, in: CGRect(origin: .zero, size: CGSize(width: image.size.width, height: image.size.height)))
break
}
// And now we just create a new UIImage from the drawing context
let cgimg = ctx!.makeImage()
let img = UIImage(cgImage: cgimg!)
return img
}