Search code examples
iosswiftavfoundationavcapturesession

Swift 3 - How to capture image and its subview?


I am working on a project that contains custom camera view.And i am unable to capture the subview over the main view.My main view consist of AVCaptureSession and i want to take photo of superview and subview both in single image.

What i am trying in code:

class ViewController: UIViewController {

@IBOutlet weak var cameraButton: UIButton!

var captureSession = AVCaptureSession()

var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentDevice: AVCaptureDevice?

var photoOutput: AVCapturePhotoOutput?

var cameraPreviewLayer:AVCaptureVideoPreviewLayer?

var image: UIImage?

var toggleCameraGestureRecognizer = UISwipeGestureRecognizer()

var zoomInGestureRecognizer = UISwipeGestureRecognizer()
var zoomOutGestureRecognizer = UISwipeGestureRecognizer()

override func viewDidLoad() {
    super.viewDidLoad()
    setupCaptureSession()
    setupDevice()
    setupInputOutput()
    setupPreviewLayer()
    captureSession.startRunning()

    toggleCameraGestureRecognizer.direction = .up
    toggleCameraGestureRecognizer.addTarget(self, action: #selector(self.switchCamera))
    view.addGestureRecognizer(toggleCameraGestureRecognizer)

    // Zoom In recognizer
    zoomInGestureRecognizer.direction = .right
    zoomInGestureRecognizer.addTarget(self, action: #selector(zoomIn))
    view.addGestureRecognizer(zoomInGestureRecognizer)

    // Zoom Out recognizer
    zoomOutGestureRecognizer.direction = .left
    zoomOutGestureRecognizer.addTarget(self, action: #selector(zoomOut))
    view.addGestureRecognizer(zoomOutGestureRecognizer)
    styleCaptureButton()
}
  cameraButton.layer.borderColor = UIColor.white.cgColor
    cameraButton.layer.borderWidth = 5
    cameraButton.clipsToBounds = true
    cameraButton.layer.cornerRadius = min(cameraButton.frame.width, cameraButton.frame.height) / 2
}

func setupCaptureSession() {
    captureSession.sessionPreset = AVCaptureSession.Preset.photo
}

func setupDevice() {
    let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
    let devices = deviceDiscoverySession.devices

    for device in devices {
        if device.position == AVCaptureDevice.Position.back {
            backCamera = device
        } else if device.position == AVCaptureDevice.Position.front {
            frontCamera = device
        }
    }
    currentDevice = backCamera
}

func setupInputOutput() {
    do {

        let captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice!)
        captureSession.addInput(captureDeviceInput)
        photoOutput = AVCapturePhotoOutput()
        photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
        captureSession.addOutput(photoOutput!)


    } catch {
        print(error)
    }
}

func setupPreviewLayer() {
    self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
    self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
    self.cameraPreviewLayer?.frame = view.frame

    self.view.layer.insertSublayer(self.cameraPreviewLayer!, at: 0)
}
@IBAction func cameraButton_TouchUpInside(_ sender: Any) {
    let settings = AVCapturePhotoSettings()
    self.photoOutput?.capturePhoto(with: settings, delegate: self)
}
}
extension ViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    if let imageData = photo.fileDataRepresentation() {
        self.image = UIImage(data: imageData)
        performSegue(withIdentifier: "Preview_Segue", sender: nil)
    }
}
}

Please help me


Solution

  • If I understood you are trying to get the content (as an image) of what the camera is grabbing and some overlay views.
    As far as I remember is not possible to grab what is inside the AVPreviewLayer, maybe they changed something in the latest version. When I tried (iOS6) it wasn't possible, the area with the AVPreviewLayer was always empty.
    What you can do is take the current camera buffer and draw inside it. By setting a class as a session delegate you can receive this callback optional

    func captureOutput(_ output: AVCaptureOutput, 
                      didOutput sampleBuffer: CMSampleBuffer, 
                           from connection: AVCaptureConnection)
    

    Here you will receive the image from the camera, this buffer can be converted into images using Accelerate framework or CoreImage.
    Is not easy, but also not impossible.