Sham Dhiman
Sham Dhiman

Reputation: 1556

iphone image captured from camera rotate automatically Swift

Programmatically, I have captured image from my camera in my app. It has been fetched nicely, but when I shift to another, view and dismiss that view at that time my image I want to rotate into landscape. I captured images from camera. When I fetch image from photo library no issue has been found.

Following image is my original image. Screen Shot

And I want to rotate image. Screen Shot

Code Below:

var captureSesssion : AVCaptureSession!
var cameraOutput : AVCapturePhotoOutput!
var previewLayer : AVCaptureVideoPreviewLayer!
var device:AVCaptureDevice!
var currentViewControl = UIViewController()
var tempImageHolder = UIImage()

func beginCamera(_ targetView: UIView, _ currentVC: UIViewController) 
{ 
    currentViewControl = currentVC
    // Do any additional setup after loading the view, typically from a nib.
    captureSesssion = AVCaptureSession()
    captureSesssion.sessionPreset = AVCaptureSession.Preset.photo
    cameraOutput = AVCapturePhotoOutput()
    
    if #available(iOS 11.1, *) {
        let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInTelephotoCamera, .builtInTrueDepthCamera], mediaType: AVMediaType.video, position: .back).devices
        device = availableDevices.first
    } else {
        // Fallback on earlier versions
        device = AVCaptureDevice.default(for: .video)!
    }
    
    if let input = try? AVCaptureDeviceInput(device: device!) {
        if (captureSesssion.canAddInput(input)) {
            captureSesssion.addInput(input)
            if (captureSesssion.canAddOutput(cameraOutput)) {
                captureSesssion.addOutput(cameraOutput)
                let connection = cameraOutput.connection(with: AVFoundation.AVMediaType.video)
                connection?.videoOrientation = .portrait
                previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
                self.previewLayer.frame = targetView.frame
                self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
                //setOutputOrientation()
                targetView.layer.addSublayer(previewLayer)
                captureSesssion.startRunning()
            }
        } else {
            print("issue here : captureSesssion.canAddInput")
        }
    } else {
        print("some problem here")
    }
    previewLayer?.frame.size = targetView.frame.size
}

   func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
    if let error = error {
        // we got back an error!
        let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        currentViewControl.present(alert, animated: true)
    } else {
    }}


 func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    let imageData = photo.fileDataRepresentation()
    let imageSize: Int = imageData!.count
    print(imageSize)
    tempImageHolder = UIImage(data: (imageData! as NSData) as Data)!
    //When use this below line works fine when take images from left landscape, but when take images from right landscape image not show properly screen shot below:
    tempImageHolder = UIImage(cgImage: tempImageHolder.cgImage!, scale: tempImageHolder.scale, orientation: .up)
    }
}

When take images from left landscape:

Input: Screen Shot

Output: Screen Shot

When take images from right landscape

Input: Screen Shot

Output: Screen Shot

UIImage(cgImage: tempImageHolder.cgImage!, scale: tempImageHolder.scale, orientation: .up)

When use this above line works fine when take images from left landscape, but when take images from right landscape image not show properly screen shot

Can someone please explain to me how to rotate image. Any help would be greatly appreciated.

Upvotes: 2

Views: 7049

Answers (5)

YodagamaHeshan
YodagamaHeshan

Reputation: 6500

You should set the orientation in output before capture the photo.

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

Upvotes: 4

jnblanchard
jnblanchard

Reputation: 1200

This is a really annoying problem, and I really cannot believe AVFoundation does not have anything easier for us. But in my apps I do this by holding around the current device orientation. When photo output comes back with an image, rotate the image using orientation to make sure everything is pictured upward.

class MyViewController: UIViewController {

    var currentOrientation: UIDeviceOrientation = .portrait

    override func viewDidLoad() {
        NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)
    }

    @objc func orientationChanged() {
        currentOrientation = UIDevice.current.orientation
    }

    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        guard let img = photo.cgImageRepresentation()?.takeUnretainedValue() else { return }
        let temp = CIImage(cgImage: img)
        var ciImage = temp;
        switch currentOrientation {
            case .portrait:
                ciImage = temp.oriented(forExifOrientation: 6)
            case .landscapeRight:
                ciImage = temp.oriented(forExifOrientation: 3)
            case .landscapeLeft:
                ciImage = temp.oriented(forExifOrientation: 1)
            default:
                break
        }

        guard let cgImage = CIContext(options: nil).createCGImage(ciImage, from: ciImage.extent) else { return }
        let fixedImage = UIImage(cgImage: cgImage)
    }


}

In the end, use fixedImage for your needs. Please note that I subscribe to notification center for changes in orientation.

Upvotes: 4

user12688502
user12688502

Reputation:

Try following function to fix the orientation:

extension UIImage {
    func makeFixOrientation() -> UIImage {
        if self.imageOrientation == UIImage.Orientation.up {
            return self
        }

        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
        let normalizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return normalizedImage;
    }
}

Upvotes: 0

Leo Dabus
Leo Dabus

Reputation: 236340

The problem there is that you are discarding your image metadata (including the orientation) when converting your UIImage to CGImage. You need to render a new UIImage before accessing its cgImage property as I showed in this post


if let data = photo.fileDataRepresentation(), let image = UIImage(data: data) {
    let renderedImage = UIGraphicsImageRenderer(size: size, format: imageRendererFormat).image { _ in image.draw(at: .zero) }
    // use renderedImage image
}

Upvotes: 0

Pushpendra
Pushpendra

Reputation: 986

enum CGImageOrientation {
case up
case down
case left
case right
case upMirrored
case downMirrored
case leftMirrored
case rightMirrored
}

  extension CGImage {
   func orientImage(_ imageOrientation: CGImageOrientation) -> 
   CGImage? {
    return orientImageWithTransform(imageOrientation).0
 }

func orientImageWithTransform(_ imageOrientation: CGImageOrientation) -> (CGImage?, CGAffineTransform) {

    var transform = CGAffineTransform.identity
    if imageOrientation == .up { return (self.copy(), transform)}


    let size = CGSize(width: width, height: height)
    let newSize = [.left,.leftMirrored, .right, .rightMirrored].contains(imageOrientation)
        ? CGSize(width: size.height, height: size.width) : size

    // Guard that we have color space and core graphics context.
    guard let colorSpace = self.colorSpace,
        // New graphic context uses transformed width and height.
        let ctx = CGContext(data: nil, width: Int(newSize.width), height: Int(newSize.height),
                            bitsPerComponent: self.bitsPerComponent, bytesPerRow: 0,
                            space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
        else { return (nil, transform)}

    // OK, now the actual work of constructing transform and creating new image.
    switch imageOrientation {
    case .down, .downMirrored:
        transform = transform.translatedBy(x: size.width, y: size.height)
        transform = transform.rotated(by: CGFloat.pi)
        break
    case .left,.leftMirrored:
        transform = transform.translatedBy(x: size.height, y: 0)
        transform = transform.rotated(by: CGFloat.pi/2)
        break
    case .right, .rightMirrored:
        transform = transform.translatedBy(x: 0, y: size.width)
        transform = transform.rotated(by: -CGFloat.pi/2)
        break
    case .up, .upMirrored:
        break
    }

    if [.upMirrored, .downMirrored,.leftMirrored, .rightMirrored].contains(imageOrientation) {
        transform = transform.translatedBy(x: size.width, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)
    }

    ctx.concatenate(transform)
    // Interestingly, drawing with the original width and height?!
    // So width and height here are pre-transform.

    ctx.draw(self, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

    return (ctx.makeImage(), transform)
}

}

To use:-

var tempImage = imgae
        guard let cgImage = imgae?.cgImage else {return}

        if let orientedImage = cgImage.orientImage(.upMirrored) {
            tempImage = UIImage(cgImage: orientedImage)
        }
        let image = UIImageView(image: tempImage)

Upvotes: 0

Related Questions