Kamera Khusus dan Pangkas gambar di Swift

Saya telah membuat kamera khusus dan menerapkan kode di bawah ini untuk memotong gambar yang diambil, saya telah menunjukkan panduan di lapisan pratinjau jadi saya ingin memotong gambar yang muncul di area itu.

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!
}

Kode ini hanya berfungsi dengan baik ketika & memberikan gambar yang dipotong persis ketika baris kode di bawah ini dikomentari, meskipun saya ingin streaming gambar dalam layar penuh jadi saya harus menggunakan baris kode di bawah ini. Gambarnya diperbesar.

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

Bagaimana cara mengatasi masalah ini? Apakah kode pemangkasannya salah?

Berikut adalah kode Kelas selengkapnya

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)
                }
            }
        }
    }
}

Berikut gambar hierarki tampilan masukkan deskripsi gambar di sini


person Anirudha Mahale    schedule 15.03.2018    source sumber
comment
Ingatlah hal ini dengan proyek Anda: openradar.me/36292067   -  person Scriptable    schedule 15.03.2018
comment
@Scriptable silakan lihat kode saya, saya tidak menggunakan UIImagePickerControllerEditedImage.   -  person Anirudha Mahale    schedule 15.03.2018
comment
sebaiknya jelaskan sebanyak yang Anda bisa di sini, saya tidak perlu mengklik di tempat lain untuk memahami masalah Anda   -  person Scriptable    schedule 15.03.2018
comment
@Scriptable Saya telah membangun aplikasi kamera menggunakan AVCaptureSession, CALayer, AVCaptureDevice dan kemudian streaming video pada lapisan pratinjau, setelah tombol klik ditekan, saya mengambil gambar dari CMSampleBuffer.   -  person Anirudha Mahale    schedule 15.03.2018
comment
Jika Anda hanya punya waktu luang, Anda dapat menjalankan proyek tersebut. Terima kasih sebelumnya.   -  person Anirudha Mahale    schedule 15.03.2018
comment
Anda meminta bantuan orang-orang di sini, jadi Anda perlu membuatnya semudah mungkin bagi orang-orang untuk membantu Anda. Posting sedetail yang diperlukan untuk menjawab pertanyaan (dan tidak lebih). Jangan paksa kami melakukan pekerjaan lebih dari yang diperlukan. Merujuk kode di luar pertanyaan tidak berfungsi karena kode tersebut dapat berubah sewaktu-waktu.   -  person Chris Garrett    schedule 16.03.2018
comment
@ChrisGarrett Saya melakukan apa yang Anda sarankan.   -  person Anirudha Mahale    schedule 17.03.2018
comment
Oke, ini lebih baik, tapi kami masih memerlukan lebih banyak informasi. Dari mana Anda memanggil fungsi pemangkasan? Sepertinya Anda mencoba melakukan ini untuk setiap frame pratinjau, dan jika demikian, itu akan menjadi hambatan kinerja yang besar.   -  person Chris Garrett    schedule 18.03.2018
comment
@ChrisGarrett di sini saya menambahkan kode kelas lengkap dan hierarki tampilan.   -  person Anirudha Mahale    schedule 18.03.2018


Jawaban (2)


Tidak jelas di mana masalahnya. Saya akan menggunakan debugger atau beberapa pernyataan cetak untuk mencari tahu apakah masalahnya ada pada gambar atau tampilan yang menampilkan gambar. Cetak ukuran gambar yang dipotong untuk memastikan kebenarannya.

Kemudian, cetak ukuran tampilan gambar di ShowImageViewController di viewDidAppear untuk memastikan sudah benar.

person Chris Garrett    schedule 19.03.2018
comment
Oke saya akan mencoba menggunakan debugger dan melihat hierarki tampilan di sana. - person Anirudha Mahale; 20.03.2018

Untuk koreksi zoom out pada gambar yang dipotong, Anda harus mengubah fungsi pemotongan menjadi ini dengan menggunakan orientasi gambar.

func croppedInRect(rect: CGRect) -> UIImage? {
    func rad(_ degree: Double) -> CGFloat {
        return CGFloat(degree / 180.0 * .pi)
    }

    var rectTransform: CGAffineTransform
    switch imageOrientation {
    case .left:
        rectTransform = CGAffineTransform(rotationAngle: rad(90)).translatedBy(x: 0, y: -self.size.height)
    case .right:
        rectTransform = CGAffineTransform(rotationAngle: rad(-90)).translatedBy(x: -self.size.width, y: 0)
    case .down:
        rectTransform = CGAffineTransform(rotationAngle: rad(-180)).translatedBy(x: -self.size.width, y: -self.size.height)
    default:
        rectTransform = .identity
    }
    rectTransform = rectTransform.scaledBy(x: self.scale, y: self.scale)

    var cgImage = self.cgImage

    if cgImage == nil{

        let ciContext = CIContext()
        if let ciImage = self.ciImage{
            cgImage = ciContext.createCGImage(ciImage, from: ciImage.extent)
        }
    }

    if let imageRef = cgImage?.cropping(to: rect.applying(rectTransform)){
        let result = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
        return result
    }


    return nil

}
person Taranjeet Kaur    schedule 17.07.2019