繁体   English   中英

从相机拍摄的 UIImage 裁剪 CGRect

[英]Crop CGRect from UIImage taken from camera

我有一个视图 controller ,它在中心拍摄了一张圆形视图的照片。 拍照后,我需要裁剪用于创建圆形视图的 CGRect。 我需要裁剪矩形,而不是圆形。 我尝试了 https://stackoverflow.com/a/57258806/12411655和许多其他解决方案,但它没有裁剪我需要的 CGRect 。 如何将视图坐标中的 CGRect 转换为 UIImage 的坐标?

class CircularCameraViewController: UIViewController {
    
       var captureSession: AVCaptureSession!
       var capturePhotoOutput: AVCapturePhotoOutput!
       var cropRect: CGRect!
    
    public lazy var shutterButton: ShutterButton = {
        let button = ShutterButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(capturePhoto), for: .touchUpInside)
        return button
    }()
    
    private lazy var cancelButton: UIButton = {
        let button = UIButton()
        button.setTitle("Cancel", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(dismissCamera), for: .touchUpInside)
        return button
    }()
    
    private lazy var flashButton: UIButton = {
           let image = UIImage(named: "flash", in: Bundle(for: ScannerViewController.self), compatibleWith: nil)?.withRenderingMode(.alwaysTemplate)
           let button = UIButton()
           button.setImage(image, for: .normal)
           button.translatesAutoresizingMaskIntoConstraints = false
           button.addTarget(self, action: #selector(toggleFlash), for: .touchUpInside)
           button.tintColor = .white
           
           return button
       }()
    
       override func viewDidLoad() {
           super.viewDidLoad()
            setupCamera()
            setupPhotoOutput()
            setupViews()
            setupConstraints()
            captureSession.startRunning()
       }
    
    override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
      
    }
    
    override func viewWillDisappear(_ animated: Bool) {
      captureSession.stopRunning()
    }

    
    private func setupCamera() {
        
      let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
      var input: AVCaptureDeviceInput
      do {
        input = try AVCaptureDeviceInput(device: captureDevice!)
      } catch {
        fatalError("Error configuring capture device: \(error)");
      }
      captureSession = AVCaptureSession()
      captureSession.addInput(input)
      
      // Setup the preview view.
      let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
      videoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
      videoPreviewLayer.frame = view.layer.bounds
      view.layer.addSublayer(videoPreviewLayer)
        
        let camPreviewBounds = view.bounds
        cropRect = CGRect(
            x: camPreviewBounds.minX + (camPreviewBounds.width - 150) * 0.5,
            y: camPreviewBounds.minY + (camPreviewBounds.height - 150) * 0.5,
            width: 150,
            height: 150
        )
        
        let path = UIBezierPath(roundedRect: camPreviewBounds, cornerRadius: 0)
        path.append(UIBezierPath(ovalIn: cropRect))

        let layer = CAShapeLayer()
        layer.path = path.cgPath
        layer.fillRule = CAShapeLayerFillRule.evenOdd;
        layer.fillColor = UIColor.black.cgColor
        layer.opacity = 0.5;

        view.layer.addSublayer(layer)
    }
    
    private func setupViews() {
        view.addSubview(shutterButton)
        view.addSubview(flashButton)
        view.addSubview(cancelButton)

    }
    
    private func setupConstraints() {
        var cancelButtonConstraints = [NSLayoutConstraint]()
        var shutterButtonConstraints = [NSLayoutConstraint]()
        var flashConstraints = [NSLayoutConstraint]()

        shutterButtonConstraints = [
            shutterButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            shutterButton.widthAnchor.constraint(equalToConstant: 65.0),
            shutterButton.heightAnchor.constraint(equalToConstant: 65.0)
        ]
        
        flashConstraints = [
            flashButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24.0),
            flashButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 30)
        ]
        
        if #available(iOS 11.0, *) {
               cancelButtonConstraints = [
                   cancelButton.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 24.0),
                   view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: cancelButton.bottomAnchor, constant: (65.0 / 2) - 10.0)
            ]
               let shutterButtonBottomConstraint = view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: shutterButton.bottomAnchor, constant: 8.0)
               shutterButtonConstraints.append(shutterButtonBottomConstraint)
                   
               } else {
                   cancelButtonConstraints = [
                       cancelButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24.0),
                       view.bottomAnchor.constraint(equalTo: cancelButton.bottomAnchor, constant: (65.0 / 2) - 10.0)
                   ]
                   let shutterButtonBottomConstraint = view.bottomAnchor.constraint(equalTo: shutterButton.bottomAnchor, constant: 8.0)
                   shutterButtonConstraints.append(shutterButtonBottomConstraint)
               
               }
               NSLayoutConstraint.activate(cancelButtonConstraints + shutterButtonConstraints + flashConstraints)
    }
    
    private func setupPhotoOutput() {
      capturePhotoOutput = AVCapturePhotoOutput()
      capturePhotoOutput.isHighResolutionCaptureEnabled = true
      captureSession.addOutput(capturePhotoOutput!)
    }
    
    @objc func dismissCamera() {
        self.dismiss(animated: true, completion: nil)
    }
    
    @objc private func toggleFlash() {
        if let avDevice = AVCaptureDevice.default(for: AVMediaType.video) {
            if (avDevice.hasTorch) {
                do {
                    try avDevice.lockForConfiguration()
                } catch {
                    print("aaaa")
                }

                if avDevice.isTorchActive {
                    avDevice.torchMode = AVCaptureDevice.TorchMode.off
                } else {
                    avDevice.torchMode = AVCaptureDevice.TorchMode.on
                }
            }
            // unlock your device
            avDevice.unlockForConfiguration()
        }
    }

}


extension CircularCameraViewController : AVCapturePhotoCaptureDelegate {
    
    @objc private func capturePhoto() {
          let photoSettings = AVCapturePhotoSettings()
          photoSettings.isAutoStillImageStabilizationEnabled = true
          photoSettings.isHighResolutionPhotoEnabled = true
          photoSettings.flashMode = .auto
          // Set ourselves as the delegate for `capturePhoto`.
          capturePhotoOutput?.capturePhoto(with: photoSettings, delegate: self)
    }
    
    @available(iOS 11.0, *)
    func photoOutput(_ output: AVCapturePhotoOutput,
                     didFinishProcessingPhoto photo: AVCapturePhoto,
                     error: Error?) {
          guard error == nil else {
            fatalError("Failed to capture photo: \(String(describing: error))")
          }
          guard let imageData = photo.fileDataRepresentation() else {
            fatalError("Failed to convert pixel buffer")
          }
          guard let image = UIImage(data: imageData) else {
            fatalError("Failed to convert image data to UIImage")
          }
        
          guard let croppedImg = image.cropToRect(rect: cropRect) else {
            fatalError("Failed to crop image")
          }
          
        UIImageWriteToSavedPhotosAlbum(croppedImg, nil, nil, nil);

    }
    
    
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
        guard error == nil, let photoSample = photoSampleBuffer else {
          fatalError("Failed to capture photo: \(String(describing: error))")
        }
        guard let imgData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSample, previewPhotoSampleBuffer: previewPhotoSampleBuffer) else {
          fatalError("Failed to get image data: \(String(describing: error))")
        }
        guard let image = UIImage(data: imgData) else {
        fatalError("Failed to convert image data to UIImage: \(String(describing: error))")
    }
        
        
  }
}

UIImage 扩展:

func cropToRect(rect: CGRect!) -> UIImage? {

            let scaledRect = CGRect(x: rect.origin.x * self.scale, y: rect.origin.y * self.scale, width: rect.size.width * self.scale, height: rect.size.height * self.scale);


            guard let imageRef: CGImage = self.cgImage?.cropping(to:scaledRect)
            else {
                return nil
            }

            let croppedImage: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
            return croppedImage
        }
    

裁剪图像时,您需要从相对于图像大小的大小缩放“裁剪矩形”。

此外,从相机拍摄时,您需要考虑.imageOrientation

尝试将您的UIImage扩展更改为:

extension UIImage {
    func cropToRect(rect: CGRect, viewSize: CGSize) -> UIImage? {
        
        var cr = rect

        switch self.imageOrientation {
        case .right, .rightMirrored, .left, .leftMirrored:
            // rotate the crop rect if needed
            cr.origin.x = rect.origin.y
            cr.origin.y = rect.origin.x
            cr.size.width = rect.size.height
            cr.size.height = rect.size.width
            
        default:
            break
        }
        
        let imageViewScale = max(self.size.width / viewSize.width,
                                 self.size.height / viewSize.height)

        // scale the crop rect
        let cropZone = CGRect(x:cr.origin.x * imageViewScale,
                              y:cr.origin.y * imageViewScale,
                              width:cr.size.width * imageViewScale,
                              height:cr.size.height * imageViewScale)
        
        // Perform cropping in Core Graphics
        guard let cutImageRef: CGImage = self.cgImage?.cropping(to:cropZone)
            else {
                return nil
        }
        
        // Return image to UIImage
        let croppedImage: UIImage = UIImage(cgImage: cutImageRef, scale: self.scale, orientation: self.imageOrientation)
        return croppedImage
    }
}

并将您在photoOutput()中的调用更改为:

    guard let croppedImg = image.cropToRect(rect: cropRect, viewSize: view.frame.size) else {
        fatalError("Failed to crop image")
    }

由于您的代码使用的是完整视图,因此应该可以正常工作。 如果您将其更改为使用不同大小的视图作为videoPreviewLayer ,则使用该大小而不是view.frame.size

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM