使用 Objective C 自定义相机捕捉图像和裁剪

Posted

技术标签:

【中文标题】使用 Objective C 自定义相机捕捉图像和裁剪【英文标题】:Customised Camera capturing image and Cropping using Objective C 【发布时间】:2018-08-29 04:08:43 【问题描述】:

我正在使用定制的相机来捕捉图像。(我正在使用 AVCaptureStillImageOutput jpegStillImageNSDataRepresentation 将其转换为 UIImage) 我的目标是裁剪图像的特定区域。在我的图像捕获视图中,我绘制了一个动态大小的矩形,例如现在宽度:50px 和高度:30px,我只想捕获矩形内的区域。

我该怎么做?

【问题讨论】:

【参考方案1】:

试试这段代码,我想这会对你有所帮助

添加框架:

     import GLKit
      import CoreImage

        //Extension for image
        extension UIImage 
            func crop( rect: CGRect) -> UIImage 
                var rect = rect
                rect.origin.x*=self.scale
                rect.origin.y*=self.scale
                rect.size.width*=self.scale
                rect.size.height*=self.scale

                let imageRef = self.cgImage!.cropping(to: rect)
                let image = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
                return image
            
        
        extension UIImage 
            func fixed() -> UIImage 

                let ciContext = CIContext(options: nil)

                let cgImg = ciContext.createCGImage(ciImage!, from: ciImage!.extent)

                let image = UIImage(cgImage: cgImg!, scale: originalImage.scale, orientation: originalImage.imageOrientation)

                return image
            
        
        extension CIDetector 
            static let rectangle: CIDetector? = 
                let detector = CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: [:])
                return detector
            ()

            static let text: CIDetector? = 
                let detector = CIDetector(ofType: CIDetectorTypeText, context: nil, options: [:])
                return detector
            ()
            static let face: CIDetector? = 
                let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [:])
                return detector
            ()
        


        extension CIColor 
            static let redTone: CIColor = 
                return CIColor(red: 1.0, green: 0, blue: 0, alpha: 0.2)
            ()

            static let blueTone: CIColor = 
                return CIColor(red: 0.0, green: 0, blue: 1.0, alpha: 0.3)
            ()
        

        extension CITextFeature: Measurable 
        extension CIRectangleFeature: Measurable 
        protocol Measurable 
            var bounds: CGRect  get 
            var topLeft: CGPoint  get 
            var topRight: CGPoint  get 
            var bottomLeft: CGPoint  get 
            var bottomRight: CGPoint  get 
        

        extension Measurable 
            func perspectiveOverlay(on image: CIImage, with color: CIColor) -> CIImage? 
                var overlay = CIImage(color: color)

                overlay = overlay.cropped(to: bounds)
                overlay = overlay.applyingFilter(
                    "CIPerspectiveTransformWithExtent",
                    parameters: [
                        "inputExtent": CIVector(cgRect: image.extent),
                        "inputTopLeft": CIVector(cgPoint: topLeft),
                        "inputTopRight": CIVector(cgPoint: topRight),
                        "inputBottomLeft": CIVector(cgPoint: bottomLeft),
                        "inputBottomRight": CIVector(cgPoint: bottomRight)])


                return overlay
            
        
         private var originalImage = UIImage()

       class ViewController: UIViewController
             //Camera Capture requiered properties
        var videoDataOutput: AVCaptureVideoDataOutput!
        var videoDataOutputQueue: DispatchQueue!
        var previewLayer:AVCaptureVideoPreviewLayer!
        var captureDevice : AVCaptureDevice!
        let session = AVCaptureSession()
        var detector : CIDetector?;
        var context: CIContext?;
        private var rect: CIRectangleFeature = CIRectangleFeature()

        var testimatch: CIImage!
        fileprivate var testextent: CGRect!
        fileprivate let glkView: GLKView = 
            let view = GLKView(frame: .zero, context: EAGLContext(api: .openGLES2)!)
            view.transform = CGAffineTransform(rotationAngle: .pi / 2)
            return view
        ()
        var imatch: CIImage!
        @IBOutlet weak var imageView: UIImageView!
        var testMeasurable : Measurable!
       private var cropedImage: UIImage!
        fileprivate var extent: CGRect!
        fileprivate var ciContextt: CIContext!
        fileprivate var glContext: EAGLContext!
            override func viewDidLoad() 
                context = CIContext();
                detector = CIDetector(ofType: CIDetectorTypeFace, context: context);
                self.setupAVCapture()
            
         
// AVCaptureVideoDataOutputSampleBufferDelegate protocol and related methods
extension ViewController:  AVCaptureVideoDataOutputSampleBufferDelegate
    func setupAVCapture()
        session.sessionPreset = AVCaptureSession.Preset.vga640x480
        guard let device = AVCaptureDevice
            .default(AVCaptureDevice.DeviceType.builtInWideAngleCamera,
                     for: .video,
                     position: AVCaptureDevice.Position.back) else 
                        return
        
        captureDevice = device
        beginSession()
    
    func beginSession()
        var deviceInput: AVCaptureDeviceInput!

        do 
            deviceInput = try AVCaptureDeviceInput(device: captureDevice)
            guard deviceInput != nil else 
                print("error: cant get deviceInput")
                return
            

            if self.session.canAddInput(deviceInput)
                self.session.addInput(deviceInput)
            

            videoDataOutput = AVCaptureVideoDataOutput()

            videoDataOutput.alwaysDiscardsLateVideoFrames=true
            videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
            videoDataOutput.setSampleBufferDelegate(self, queue:self.videoDataOutputQueue)
            videoDataOutput!.videoSettings = [kCVPixelBufferPixelFormatTypeKey: Int(kCVPixelFormatType_32BGRA)] as [String : Any]
            if session.canAddOutput(self.videoDataOutput)
                session.addOutput(self.videoDataOutput)
            
            videoDataOutput.connection(with: .video)?.isEnabled = true

            // let connection = videoDataOutput.connection(with: .video)
            // connection?.videoOrientation = .portrait

            previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
            previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

            let rootLayer :CALayer = self.view.layer
            rootLayer.masksToBounds = true
            previewLayer.frame = rootLayer.bounds

            glkView.frame = rootLayer.bounds
            glkView.bindDrawable()
            glkView.isOpaque = false
            glkView.layer.isOpaque = false
            self.previewLayer.addSublayer(glkView.layer)

            rootLayer.addSublayer(self.previewLayer)

            ciContextt = CIContext(eaglContext: glkView.context)
            extent = CGRect(x: -5, y: -60, width: glkView.drawableWidth + 20, height: glkView.drawableHeight + 120)
            //  extent = CGRect(x: 0, y: 0, width: glkView.drawableWidth , height: glkView.drawableHeight)

            session.startRunning()
         catch let error as NSError 
            deviceInput = nil
            print("error: \(error.localizedDescription)")
        
    



    func newimagerect(feature : Measurable) -> UIImage

        let cgimg = self.context?.createCGImage(self.imatch!, from: testimatch!.extent)

        originalImage = UIImage(ciImage: self.imatch)

        let newImage = UIImage(cgImage: cgimg!, scale: originalImage.scale, orientation: originalImage.imageOrientation)
        self.imageView.image = newImage
        self.imageView.image?.crop(rect: testextent)

        let perspectiveCorrection = CIFilter(name: "CIPerspectiveCorrection")!
        let docImage = self.imatch!
        perspectiveCorrection.setValue(CIVector(cgPoint:feature.topLeft),
                                       forKey: "inputTopLeft")
        perspectiveCorrection.setValue(CIVector(cgPoint:feature.topRight),
                                       forKey: "inputTopRight")
        perspectiveCorrection.setValue(CIVector(cgPoint:feature.bottomRight),
                                       forKey: "inputBottomRight")
        perspectiveCorrection.setValue(CIVector(cgPoint:feature.bottomLeft),
                                       forKey: "inputBottomLeft")
        perspectiveCorrection.setValue(docImage,
                                       forKey: kCIInputImageKey)

        let outputImage = perspectiveCorrection.outputImage

        let updatedImage = UIImage(ciImage: outputImage!, scale: originalImage.scale, orientation: originalImage.imageOrientation)


        cropedImage = updatedImage.fixed()
       return cropedImage

    

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

        let pixelBuffer : CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!;
        var image : CIImage = CIImage(cvPixelBuffer: pixelBuffer);

        DispatchQueue.main.async 
            self.imatch =  image

                let stDetect = CIDetector.rectangle?.features(in: image, options: [ CIDetectorAccuracy: CIDetectorAccuracyLow , CIDetectorTracking: true, CIDetectorMinFeatureSize  : 0.2]).first.flatMap f in
                    if let feature = f as? Measurable 
                        feature.perspectiveOverlay(on: image, with: CIColor.redTone).flatMap
                            image = $0
                            self.glkView.isHidden = false
                            self.testMeasurable = feature
                            self.draw(image: image)
                        
                    
                
                if stDetect == nil
                    self.glkView.isHidden = true

                

        
    
    func draw(image: CIImage) 
        DispatchQueue.main.async 
            //Draw rect
            self.glkView.bindDrawable()
            if self.glkView.context != EAGLContext.current() 
                EAGLContext.setCurrent(self.glkView.context)
            
            glClearColor(0.0,0.0,0.0,0.0);
            self.ciContextt.draw(image, in: self.extent, from: image.extent)
            self.testimatch = image
            self.testextent = self.extent
            self.glkView.display()

        
    
    // clean up AVCapture
    func stopCamera()
        session.stopRunning()
    

您可以仅使用调用从相机获取带有矩形的 uiimage:

让 image = self.newimagerect(feature: self.testMeasurable)

【讨论】:

以上是关于使用 Objective C 自定义相机捕捉图像和裁剪的主要内容,如果未能解决你的问题,请参考以下文章

具有多个页面的 Windows Phone 8.1 自定义控件

iOS 自定义相机:基础知识储备

Objective C中iOS的图像模糊检测

Android - 如何从相机捕获图像或从库中添加

使用AVFoundation 自定义相机和录制视频

使用自定义相机的图像质量很差