Reputation: 1556
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
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
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
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
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
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