65206

Custom Camera and Crop image in Swift

Question:

I have created a custom camera and have implemented below code to crop the taken image, I have shown guides in the preview layer so I want to crop the image which appears in that area.

func imageByCropToRect(rect:CGRect, scale:Bool) -> UIImage { var rect = rect var scaleFactor: CGFloat = 1.0 if scale { scaleFactor = self.scale rect.origin.x *= scaleFactor rect.origin.y *= scaleFactor rect.size.width *= scaleFactor rect.size.height *= scaleFactor } var image: UIImage? = nil; if rect.size.width > 0 && rect.size.height > 0 { let imageRef = self.cgImage!.cropping(to: rect) image = UIImage(cgImage: imageRef!, scale: scaleFactor, orientation: self.imageOrientation) } return image! }

This code just works fine when & give the exact cropped image when the below line of code is commented, though I want the image streaming to be full screen so I have to use the below line of code. The image comes zoomed out sort of.

(self.previewLayer as! AVCaptureVideoPreviewLayer).videoGravity = AVLayerVideoGravity.resizeAspectFill

How do I solve this issue? Is the cropping code wrong?

Here is the full Class code

import UIKit import AVFoundation class CameraViewController: UIViewController { @IBOutlet weak var guideImageView: UIImageView! @IBOutlet weak var guidesView: UIView! @IBOutlet weak var cameraPreviewView: UIView! @IBOutlet weak var cameraButtonView: UIView! @IBOutlet weak var captureButton: UIButton! var captureSession = AVCaptureSession() var previewLayer: CALayer! var captureDevice: AVCaptureDevice! /// This will be true when the user clicks on the click photo button. var takePhoto = false override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) captureSession = AVCaptureSession() previewLayer = CALayer() takePhoto = false requestAuthorization() } private func userinteractionToButton(_ interaction: Bool) { captureButton.isEnabled = interaction } /// This function will request authorization, If authorized then start the camera. private func requestAuthorization() { switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) { case .authorized: prepareCamera() case .denied, .restricted, .notDetermined: AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted) in if !Thread.isMainThread { DispatchQueue.main.async { if granted { self.prepareCamera() } else { let alert = UIAlertController(title: "unable_to_access_the_Camera", message: "to_enable_access_go_to_setting_privacy_camera_and_turn_on_camera_access_for_this_app", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "ok", style: .default, handler: {_ in self.navigationController?.popToRootViewController(animated: true) })) self.present(alert, animated: true, completion: nil) } } } else { if granted { self.prepareCamera() } else { let alert = UIAlertController(title: "unable_to_access_the_Camera", message: "to_enable_access_go_to_setting_privacy_camera_and_turn_on_camera_access_for_this_app", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "ok", style: .default, handler: {_ in self.navigationController?.popToRootViewController(animated: true) })) self.present(alert, animated: true, completion: nil) } } }) } } /// Will see if the primary camera is avilable, If found will call method which will asign the available device to the AVCaptureDevice. private func prepareCamera() { // Resets the session. self.captureSession.sessionPreset = AVCaptureSession.Preset.photo if #available(iOS 10.0, *) { let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices self.assignCamera(availableDevices) } else { // Fallback on earlier versions // development, need to test this on iOS 8 if let availableDevices = AVCaptureDevice.default(for: AVMediaType.video) { self.assignCamera([availableDevices]) } else { self.showAlert() } } } /// Assigns AVCaptureDevice to the respected the variable, will begin the session. /// /// - Parameter availableDevices: [AVCaptureDevice] private func assignCamera(_ availableDevices: [AVCaptureDevice]) { if availableDevices.first != nil { captureDevice = availableDevices.first beginSession() } else { self.showAlert() } } /// Configures the camera settings and begins the session, this function will be responsible for showing the image on the UI. private func beginSession() { do { let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice) captureSession.addInput(captureDeviceInput) } catch { print(error.localizedDescription) } let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) self.previewLayer = previewLayer self.cameraPreviewView.layer.addSublayer(self.previewLayer) self.previewLayer.frame = self.view.layer.frame self.previewLayer.frame.origin.y = +self.cameraPreviewView.frame.origin.y (self.previewLayer as! AVCaptureVideoPreviewLayer).videoGravity = AVLayerVideoGravity.resizeAspectFill self.previewLayer.masksToBounds = true self.cameraPreviewView.clipsToBounds = true captureSession.startRunning() self.view.bringSubview(toFront: self.cameraPreviewView) self.view.bringSubview(toFront: self.cameraButtonView) self.view.bringSubview(toFront: self.guidesView) let dataOutput = AVCaptureVideoDataOutput() dataOutput.videoSettings = [((kCVPixelBufferPixelFormatTypeKey as NSString) as String):NSNumber(value:kCVPixelFormatType_32BGRA)] dataOutput.alwaysDiscardsLateVideoFrames = true if captureSession.canAddOutput(dataOutput) { captureSession.addOutput(dataOutput) } captureSession.commitConfiguration() let queue = DispatchQueue(label: "com.letsappit.camera") dataOutput.setSampleBufferDelegate(self, queue: queue) self.userinteractionToButton(true) } /// Get the UIImage from the given CMSampleBuffer. /// /// - Parameter buffer: CMSampleBuffer /// - Returns: UIImage? func getImageFromSampleBuffer(buffer:CMSampleBuffer, orientation: UIImageOrientation) -> UIImage? { if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) { let ciImage = CIImage(cvPixelBuffer: pixelBuffer) let context = CIContext() let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer)) if let image = context.createCGImage(ciImage, from: imageRect) { return UIImage(cgImage: image, scale: UIScreen.main.scale, orientation: orientation) } } return nil } /// This function will destroy the capture session. func stopCaptureSession() { self.captureSession.stopRunning() if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] { for input in inputs { self.captureSession.removeInput(input) } } } func showAlert() { let alert = UIAlertController(title: "Unable to access the camera", message: "It appears that either your device doesn't have camera or its broken", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: {_ in self.navigationController?.dismiss(animated: true, completion: nil) })) self.present(alert, animated: true, completion: nil) } @IBAction func didTapClick(_ sender: Any) { userinteractionToButton(false) takePhoto = true } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showImage" { let vc = segue.destination as! ShowImageViewController vc.image = sender as! UIImage } } } extension CameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { if connection.isVideoOrientationSupported { connection.videoOrientation = .portrait } if takePhoto { takePhoto = false // Rotation should be unlocked to work. var orientation = UIImageOrientation.up switch UIDevice.current.orientation { case .landscapeLeft: orientation = .left case .landscapeRight: orientation = .right case .portraitUpsideDown: orientation = .down default: orientation = .up } if let image = self.getImageFromSampleBuffer(buffer: sampleBuffer, orientation: orientation) { DispatchQueue.main.async { let newImage = image.imageByCropToRect(rect: self.guideImageView.frame, scale: true) self.stopCaptureSession() self.previewLayer.removeFromSuperlayer() self.performSegue(withIdentifier: "showImage", sender: newImage) } } } } }

Here is the view hierarchy image <a href="https://i.stack.imgur.com/9CYtD.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/9CYtD.png" data-original="https://i.stack.imgur.com/9CYtD.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Answer1:

It's not clear where the problem is. I would either use the debugger or some print statements to figure out whether the issue is with the image or the view displaying the image. Print out size of the cropped image to make sure it is correct.

Then, print out the image view size in the ShowImageViewController in viewDidAppear to make sure it is correct.

Recommend

  • Xamarin Firebase for iOS giving fcm 501 error
  • Experimenting with Firebase - Push Notifications
  • iOS FCM Push notifications not working in background state in swift?
  • How to deal with concurrency issues brought by NSStream run loop scheduling using GCD?
  • JQuery Mobile 1.4 How to Disable Hover Effect on Mobile Devices
  • Convert adjacency matrix to a csv file
  • Create File Command in Batch Files (*.bat)
  • Problem in Creating a View Dynamically ( in Android ) [Error : couldn't save which view …]
  • onActivityResult() not called after startActivityForResult() with Intent.ACTION_GET_CONTENT
  • Store additional information during registration with Firebase in Android
  • Shuffling an Array in Android
  • How to add an item to a ListView with each Button click
  • SQLiteOpenHelper: onCreate() method not called on physical device
  • When is locking on types a good idea?
  • android-How to enable cache or buffering when videoView have downloaded a video
  • Django Migrations fail during django initialization
  • Change border corlor of NSTableView
  • Animate a CAShapeLayer's subclass custom property based on the path property
  • Listview - list item selection and initial setting (custom made items)
  • redirect user after update in class based view in django
  • Installing apk from within application in android
  • Retrieving a double from a JTextArea while solving for X
  • MRO with multiple inheritance in python
  • Extending the Django 1.11 User Model
  • Fully customized Python Help Usage
  • Swift Initialization Rule Confusion
  • How to upload specific List image using click on Upload button
  • Blackberry 6: how to detect a long click on track pad?
  • Creating Java object from class name with constructor, which contains parameters [duplicate]
  • how to avoid repetitive constructor in children
  • Uncaught Error: Could not find module `ember-load-initializers`
  • Deleting and Updating values from a cusrsor adapter
  • Opengl-es onTouchEvents problem or a draw problem? [closed]
  • Possible to stop flickering java tooltip in heavyweight mode?
  • output of program is not same as passed argument
  • Javascript + PHP Encryption with pidCrypt
  • Getting Messege Twice Using IMvxMessenger
  • How can i traverse a binary tree from right to left in java?
  • How can I use threading to 'tick' a timer to be accessed by other threads?