How to change orientation of image after capture? - ios

This is code which i used in my custom camera for capture photo but it capturing photo correctly but if suppose user rotate device(but camera will still in portrait like iphone camera) and take picture i want that picture in portrait.how can i do that?
let stillImageOutput = self._getStillImageOutput()
if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
// videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
stillImageOutput.captureStillImageAsynchronously(from: videoConnection , completionHandler: { [weak self] sample, error in
if let error = error {
DispatchQueue.main.async(execute: {
self?._show(NSLocalizedString("Error", comment:""), message: error.localizedDescription)
})
imageCompletion(nil,error as NSError?)
return
}
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sample)
var capturedImage = UIImage(data: imageData!)[![This how photo showing if i capture by rotating Device][1]][1]

add below code after the initialization of videoConnection and you good to go
switch UIDevice.current.orientation {
case .landscapeLeft:
videoConnection.videoOrientation = AVCaptureVideoOrientation.landscapeRight
break
case .landscapeRight:
videoConnection.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
break
case .portrait:
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
break
case .portraitUpsideDown:
videoConnection.videoOrientation = AVCaptureVideoOrientation.portraitUpsideDown
break
default:
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
break
}

Set the orientation before capturing the image
// set the image orientation in output
if let photoOutputConnection = self.photoOutput.connection(with: .video) {
photoOutputConnection.videoOrientation = videoPreviewLayerOrientation! // set the orientation you want
}
self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureProcessor) // capture image

Related

Firebase MLKit text recognition fail with the front camera iOS

I have integrated the Firebase MLKit SDK as per the document, texts are detected correctly with the back camera photo. When I am using a captured photo from the front camera, texts are detecting wrongly and bad results are coming out.
#IBAction func findTextDidTouch(_ sender: UIButton) {
runTextRecognition(with:imageView.image!)
}
func runCloudTextRecognition(with image: UIImage) {
let visionImage = VisionImage(image: image)
cloudTextDetector.detect(in: visionImage) { features, error in
if let error = error {
print("Received error: \(error)")
return
}
self.processCloudResult(from: features, error: error)
}
}
for text in features {
if let block = text as? VisionTextBlock {
print(block.text)
}
}
I am not able to figure out. Do I need change with the camera or firebase ml kit?
You need to provide image orientation as well. Adding code snippet may help you.
let visionImage = VisionImage(image: image)
let metadata = VisionImageMetadata()
metadata.orientation = self.detectorOrientation(in: image)
visionImage.metadata = metadata
textDetector.process(visionImage) { (features, error) in
if features != nil
{
self.processResult(from: [features!], error: error)
}
}
//Detects orientation of the selected or captured image
func detectorOrientation(in image: UIImage) -> VisionDetectorImageOrientation {
switch image.imageOrientation {
case .up:
return .topLeft
case .down:
return .bottomRight
case .left:
return .leftBottom
case .right:
return .rightTop
case .upMirrored:
return .topRight
case .downMirrored:
return .bottomLeft
case .leftMirrored:
return .leftTop
case .rightMirrored:
return .rightBottom
}
}

Camera only takes pictures in portrait mode swift

How can you set a camera into landscape mode? Everytime I take a photo, the image gets saved as a portrait image. When the device is in landscape mode the photo looks fine but if I see it in the camera roll it's still portrait mode.
This is my take photo function:
// take a photo
#IBAction func takePhoto(sender: AnyObject) {
self.fullScreenView.hidden = false
self.recordButton.enabled = false
self.takephoto.enabled = false
self.recordButton.hidden = true
self.takephoto.hidden = true
session.startRunning()
// customize the quality level or bitrate of the output photo
session.sessionPreset = AVCaptureSessionPresetPhoto
// add the AVCaptureVideoPreviewLayer to the view and set the view in fullscreen
fullScreenView.frame = view.bounds
videoPreviewLayer.frame = fullScreenView.bounds
fullScreenView.layer.addSublayer(videoPreviewLayer)
// add action to fullScreenView
gestureFullScreenView = UITapGestureRecognizer(target: self, action: #selector(ViewController.takePhoto(_:)))
self.fullScreenView.addGestureRecognizer(gestureFullScreenView)
// add action to myView
gestureView = UITapGestureRecognizer(target: self, action: #selector(ViewController.setFrontpage(_:)))
self.view.addGestureRecognizer(gestureView)
if (preview == true) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
// code for photo capture goes here...
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
// process the image data (sampleBuffer) here to get an image file we can put in our view
if (sampleBuffer != nil) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let image = UIImage(data: imageData, scale: 1.0)
self.fullScreenView.hidden = true
self.fullScreenView.gestureRecognizers?.forEach(self.fullScreenView.removeGestureRecognizer)
self.session.stopRunning()
// save image to the library
UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
self.imageViewBackground = UIImageView(frame: self.view.bounds)
self.imageViewBackground.image = image
self.imageViewBackground.tag = self.key
self.view.addSubview(self.imageViewBackground)
}
})
}
}
else {
preview = true
}
}
My preview looks like that and that's ok:
http://img5.fotos-hochladen.net/uploads/bildschirmfotom4s7diaehy.png
but in the end it looks like that:
http://img5.fotos-hochladen.net/uploads/bildschirmfoto3c2rlwtevf.png
Thanks in advance!
Because your videoConnection's orientation is always in portrait no matters your device is in portrait or landscape. So you should adjust the videoConnection's orientation to the correct one before taking the still photo
Add the following method to get videoOrientation for the current deviceOrientation
func videoOrientation(for deviceOrientation: UIDeviceOrientation) -> AVCaptureVideoOrientation {
switch deviceOrientation {
case UIDeviceOrientation.portrait:
return AVCaptureVideoOrientation.portrait
case UIDeviceOrientation.landscapeLeft:
return AVCaptureVideoOrientation.landscapeRight
case UIDeviceOrientation.landscapeRight:
return AVCaptureVideoOrientation.landscapeLeft
case UIDeviceOrientation.portraitUpsideDown:
return AVCaptureVideoOrientation.portraitUpsideDown
default:
return AVCaptureVideoOrientation.portrait
}
}
Right after the following line
if let videoConnection = stillImageOutput.connectionWithMediaType(AVMediaTypeVideo) {
Add the following line
videoConnection.videoOrientation = videoOrientation(for: UIDevice.current.orientation)
Note: If your app supports only portrait or landscape mode, this issue still happens because UIDevice.current.orientation will always return the supported orientation. To overcome this, you can use CoreMotion to detect device orientation, then pass it to videoOrientation(for:) method.
Hope this helps :)
I got the answer from #Yodagama but he didn't add how to detect the orientation
if let photoOutputConnection = self.photoOutput.connection(with: .video) {
// USE the below function HERE
photoOutputConnection.videoOrientation = videoOrientation()
}
photoOutput.capturePhoto(with: settings, delegate: self)
func to detect device orientation:
func videoOrientation() -> AVCaptureVideoOrientation {
var videoOrientation: AVCaptureVideoOrientation!
let orientation: UIDeviceOrientation = UIDevice.current.orientation
switch orientation {
case .faceUp, .faceDown, .unknown:
// let interfaceOrientation = UIApplication.shared.statusBarOrientation
if let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation {
switch interfaceOrientation {
case .portrait, .portraitUpsideDown, .unknown:
videoOrientation = .portrait
case .landscapeLeft:
videoOrientation = .landscapeRight
case .landscapeRight:
videoOrientation = .landscapeLeft
#unknown default:
videoOrientation = .portrait
}
}
case .portrait, .portraitUpsideDown:
videoOrientation = .portrait
case .landscapeLeft:
videoOrientation = .landscapeRight
case .landscapeRight:
videoOrientation = .landscapeLeft
#unknown default:
videoOrientation = .portrait
}
return videoOrientation
}
Possible Solution: Saving Image as JPEG instead of PNG
This occurs because PNGs do not store orientation information. Save the photo as a JPG instead and it will be oriented correctly.
Use this code to convert your image to JPG immediately after taking the image (the second line is the operative one here):
let image = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let imageData:NSData = UIImageJPEGRepresentation(image, 0.9)! // 0.9 is compression value: 0.0 is most compressed/lowest quality and 1.0 is least compressed/highest quality
Source + more info: https://stackoverflow.com/a/34796890/5700898
If that doesn't work
I've edited your code in a couple places, see if the below code now works as you hope:
// initialize saving photo capabilities
func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafePointer<Void>) {
if error == nil {
print("image saved")
} else {
print("save error: \(error?.localizedDescription)")
}
}
// take a photo
#IBAction func takePhoto(sender: AnyObject) {
self.fullScreenView.hidden = false
self.recordButton.enabled = false
self.takephoto.enabled = false
self.recordButton.hidden = true
self.takephoto.hidden = true
session.startRunning()
// customize the quality level or bitrate of the output photo
session.sessionPreset = AVCaptureSessionPresetPhoto
// add the AVCaptureVideoPreviewLayer to the view and set the view in fullscreen
fullScreenView.frame = view.bounds
videoPreviewLayer.frame = fullScreenView.bounds
fullScreenView.layer.addSublayer(videoPreviewLayer)
// add action to fullScreenView
gestureFullScreenView = UITapGestureRecognizer(target: self, action: #selector(ViewController.takePhoto(_:)))
self.fullScreenView.addGestureRecognizer(gestureFullScreenView)
// add action to myView
gestureView = UITapGestureRecognizer(target: self, action: #selector(ViewController.setFrontpage(_:)))
self.view.addGestureRecognizer(gestureView)
if (preview == true) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
// code for photo capture goes here...
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
// process the image data (sampleBuffer) here to get an image file we can put in our view
if (sampleBuffer != nil) {
self.stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if let videoConnection = self.stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = self.interfaceToVideoOrientation()
self.stillImageOutput!.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let image = UIImage(data: imageData)
self.fullScreenView.hidden = true
self.fullScreenView.gestureRecognizers?.forEach(self.fullScreenView.removeGestureRecognizer)
self.session.stopRunning()
// save image to the library
UIImageWriteToSavedPhotosAlbum(image, self, #selector(ViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
self.imageViewBackground = UIImageView(frame: self.view.bounds)
self.imageViewBackground.image = image
self.imageViewBackground.tag = self.key
self.view.addSubview(self.imageViewBackground)
}
})
}
}
else {
preview = true
}
}

AVCaptureSession Image Is Size Of Screen [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm using AVCaptureSession to create a camera and I'm trying to take a picture with it. Here is the code that loads the camera...
func reloadCamera() {
cameraView.backgroundColor = UIColor.clearColor()
captureSession = AVCaptureSession()
captureSession!.sessionPreset = AVCaptureSessionPresetHigh
let backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
if (camera == false) {
let videoDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
for device in videoDevices {
let device = device as! AVCaptureDevice
if device.position == AVCaptureDevicePosition.Front {
captureDevice = device
break
} else {
captureDevice = backCamera
}
}
} else {
captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
}
do {
let input = try AVCaptureDeviceInput(device: captureDevice)
if captureSession!.canAddInput(input) {
captureSession!.addInput(input)
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if captureSession!.canAddOutput(stillImageOutput) {
captureSession!.addOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
previewLayer?.frame = cameraView.bounds
cameraView.layer.addSublayer(previewLayer!)
captureSession!.startRunning()
}
}
} catch let error as NSError {
// Handle any errors
print(error)
}
}
Here is how I take a photo...
func didPressTakePhoto(){
toggleFlash()
if let videoConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = (previewLayer?.connection.videoOrientation)!
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
if sampleBuffer != nil {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
self.capturedImage = UIImage(data: imageData)
if self.camera == true {
self.capturedImage = UIImage(CGImage: self.capturedImage.CGImage!, scale: 1.0, orientation: UIImageOrientation.Right)
} else {
self.capturedImage = UIImage(CGImage: self.capturedImage.CGImage!, scale: 1.0, orientation: UIImageOrientation.LeftMirrored)
}
self.tempImageView.image = self.capturedImage
UIImageWriteToSavedPhotosAlbum(self.capturedImage, nil, nil, nil);
self.tempImageView.hidden = false
self.goButton.hidden = false
self.cameraView.hidden = true
self.removeImageButton.hidden = false
self.captureButton.hidden = true
self.flashChanger.hidden = true
self.switchCameraButton.hidden = true
}
})
}
}
But what's happening is the picture that is taken is as large as the entire screen (like Snapchat), but I only want it to be as big as the UIView I'm taking it from. Tell me if I you need any more information. Thank you!
First of all, you are the one who is setting the capture session's preset to AVCaptureSessionPresetHigh. If you don't need that, don't do that; use a smaller-size preset. For example, use AVCaptureSessionPreset640x480 to get a smaller size.
Second, no matter what the size of the resulting photo may be, reducing it to the size you need, and displaying it at that size, is entirely up to you. Ultimately, it's just a matter of the size and content mode of your image view, though it is always better to reduce the image size as well, in order to avoid wasting memory (as you would be doing by displaying an image that is significantly larger then what the user is shown).

Swift, has got me on this one: interfaceOrientation was deprecated in iOS 8.0

It's not a do or die, but have not had any luck with hacking this. Thanks.
if session.canAddInput(videoDeviceInput){
session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput
dispatch_async(dispatch_get_main_queue(), {
// ERROR HERE
let orientation: AVCaptureVideoOrientation = AVCaptureVideoOrientation(rawValue: self.interfaceOrientation.rawValue)!
(self.previewView.layer as! AVCaptureVideoPreviewLayer).connection.videoOrientation = orientation
})
}
Yeah, it was depecrated. You can use UIApplication.sharedApplication().statusBarOrientation instead.
let orientation: AVCaptureVideoOrientation?
switch UIApplication.sharedApplication().statusBarOrientation{
case .LandscapeLeft:
orientation = .LandscapeLeft
case .LandscapeRight:
orientation = .LandscapeRight
case .Portrait:
orientation = .Portrait
case .PortraitUpsideDown:
orientation = .PortraitUpsideDown
case .Unknown:
orientation = nil
}
if let orientation = orientation{
(self.previewView.layer as! AVCaptureVideoPreviewLayer).connection.videoOrientation = orientation
}

Need to Mirror Video Orientation and Handle Rotation When Using Front Camera

I can't quite figure out how to handle front facing camera video capture orientations. I have all rotations handled for the back camera when capturing video and pictures and all rotations handled for the front camera when taking pictures, as well as saving the captured videos and pictures with the correct orientations as well, except for the front camera video capture.
The first problem is when in either landscape mode, the video is not saved correctly with the correct orientation. The second problem is that the saved video is mirrored. Although i know how to handle this mirroring effect for picture using the front camera, I am not sure what to call to handle it for videos.
I had a lot of trouble trying to find anything specifically for this one issue and failed to do so. If anybody could point me to a thread that addresses this specific issue, that would be great.
Either way, here is the method that is called that handles video orientation when device orientation changes. I am not sure exactly what to add to my code if the front camera is being used.
/**************************************************************************
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:
rotateButtons(self.degrees0)
if self.usingFrontCamera == true {
}
else {
}
println("Device Orientation Portrait")
break
case UIDeviceOrientation.LandscapeLeft:
println("Device Orientation LandScapeLeft")
rotateButtons(self.degrees90)
if self.usingFrontCamera == true {
println("Using front camera, rotation in landscape left")
// if let connection = self.captureConnection {
//
// connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
//
// println("Capture connection Orientation is LandScape Right")
// }
// else {
//
// println("Capture connection is nil, could not change video orientation")
// }
}
else {
if let connection = self.captureConnection {
connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
println("Capture connection Orientation is LandScape Right")
}
else {
println("Capture connection is nil, could not change video orientation")
}
}
break
case UIDeviceOrientation.LandscapeRight:
println("Device Orientation LandscapeRight")
rotateButtons(-self.degrees90)
if self.usingFrontCamera == true {
println("Using front camera, rotation in landscape right")
// if let connection = self.captureConnection {
//
// connection.videoOrientation = AVCaptureVideoOrientation.LandscapeRight
//
// println("Capture connection Orientation is LandScape Left")
// }
// else {
//
// println("Capture connection is nil, could not change video orientation")
// }
}
else {
if let connection = self.captureConnection {
connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft
println("Capture connection Orientation is LandScape Left")
}
else {
println("Capture connection is nil, could not change video orientation")
}
}
break
default:
break
}
}
Based on this answer: Video Saving in the wrong orientation AVCaptureSession
I faced the same issue and was able to fix it following this post.
var videoConnection:AVCaptureConnection?
for connection in self.fileOutput.connections {
for port in connection.inputPorts! {
if port.mediaType == AVMediaTypeVideo {
videoConnection = connection as? AVCaptureConnection
if videoConnection!.supportsVideoMirroring {
videoConnection!.videoMirrored = true
}
}
}
}
}
Please let me know if it helps you James
The accepted answer will only mirror the video in preview. You need to transform your video.
func mirrorVideo(inputURL: URL, completion: #escaping (_ outputURL : URL?) -> ())
{
let videoAsset: AVAsset = AVAsset( url: inputURL )
let clipVideoTrack = videoAsset.tracks( withMediaType: AVMediaType.video ).first! as AVAssetTrack
let composition = AVMutableComposition()
composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.width)
videoComposition.frameDuration = CMTimeMake(1, 30)
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
var transform:CGAffineTransform = CGAffineTransform(scaleX: -1.0, y: 1.0)
transform = transform.translatedBy(x: -clipVideoTrack.naturalSize.width, y: 0.0)
transform = transform.rotated(by: CGFloat(Double.pi/2))
transform = transform.translatedBy(x: 0.0, y: -clipVideoTrack.naturalSize.width)
transformer.setTransform(transform, at: kCMTimeZero)
instruction.layerInstructions = [transformer]
videoComposition.instructions = [instruction]
// Export
let exportSession = AVAssetExportSession(asset: videoAsset, presetName: AVAssetExportPreset640x480)!
let fileName = UniqueIDGenerator.generate().appending(".mp4")
let filePath = documentsURL.appendingPathComponent(fileName)
let croppedOutputFileUrl = filePath
exportSession.outputURL = croppedOutputFileUrl
exportSession.outputFileType = AVFileType.mp4
exportSession.videoComposition = videoComposition
exportSession.exportAsynchronously {
if exportSession.status == .completed {
DispatchQueue.main.async(execute: {
completion(croppedOutputFileUrl)
})
return
} else if exportSession.status == .failed {
print("Export failed - \(String(describing: exportSession.error))")
}
completion(nil)
return
}
}
In your AVCaptureFileOutputRecordingDelegate
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
self.mirrorVideo(inputURL: outputFileURL) { (url) in
self.delegate!.videoRecordingEnded(videoURL: url!)
}
}
Kesong's comment in Sam's answer works for transforming the video itself and not just the preview. Make sure to check if videoMirroring is supported first and make sure to disable automatically adjusting video mirroring before setting the isVideoMirrored property or the app can crash.
The below code works for Swift 5. You can use this code after attaching your AVCaptureSession to your AVCaptureMovieFileOutput.
if let videoConnection = movieFileOutput.connection(with: .video) {
if videoConnection.isVideoMirroringSupported {
if frontCamera { //For selfie camera, flip the output video so it doesn't appear mirrored
videoConnection.automaticallyAdjustsVideoMirroring = false
videoConnection.isVideoMirrored = true
} else {
videoConnection.automaticallyAdjustsVideoMirroring = true
}
}
}

Resources