24/01/2019, 15:22

[Swift] Sử dụng CoreImage để detect khuôn mặt, text hay bar code.

Có bao giờ các bạn tự hỏi các ứng dụng về ảnh làm mịn da mặt, hay các ứng dụng về ORC (scan ảnh ra text), các ứng dụng quét mã QR code, bar code là làm thế nào để các ứng dụng đó có thể detect chính xác được mặt, hay text trong ảnh hay không? Hôm nay tôi sẽ giới thiệu đến các bạn một cách mà có ...

Có bao giờ các bạn tự hỏi các ứng dụng về ảnh làm mịn da mặt, hay các ứng dụng về ORC (scan ảnh ra text), các ứng dụng quét mã QR code, bar code là làm thế nào để các ứng dụng đó có thể detect chính xác được mặt, hay text trong ảnh hay không? Hôm nay tôi sẽ giới thiệu đến các bạn một cách mà có thể detect khuôn mặt, text hay bar code bằng Framework CoreImage.

Trong project demo tôi sẽ sử dụng CIDetector trong framework CoreImage để thực hiện việc detect khuôn mặt và mã bar code trong một bức ảnh. Ngoài ra, CIDetector cũng cung cấp rất nhiều các kiểu mà bạn có thể thực hiện việc detect.

func setImageAndFocusOn(image: UIImage?, type: String, detectorType: String, imageView: UIImageView) {
        DispatchQueue.global(qos: .default).async {
            guard let image = image else {
                return
            }
            let cImage = image.ciImage ?? CIImage(cgImage: image.cgImage!)
            
            let detector = CIDetector(ofType: detectorType, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyLow])
            let features = detector!.features(in: cImage)
            
            if features.count > 0 {
                print("found (features.count) (type)")
                let imgSize = CGSize(awidth: Double(image.cgImage!.awidth), height: (Double(image.cgImage!.height)))
                imageView.applyFaceDetection(for: features, size: imgSize, image: image)

            } else {
                print("No (type) found")
            }
        }
    }

Chúng ta có thể thực hiện việc detect Image theo hàm bên trên, input vào là một UIImage + detectorType, nếu việc detect thành công nó sẽ trả về cho bạn một mảng các [CIFeature].

Khi chúng ta detect được theo kiểu detectorType, chúng ta sẽ thực hiện việc xử lý tiếp theo cho bức ảnh mà ta đã detect, ở demo này tôi sẽ thực hiện việc vẽ một ô vuông hình vuông lên khuôn mặt và barcode mà tôi đã detect được.

extension UIImageView {
    func applyFaceDetection(for features: [AnyObject], size: CGSize, image: UIImage) {
        var rect = features[0].bounds!
        rect.origin.y = size.height - rect.origin.y - rect.size.height
        var rightBorder = Double(rect.origin.x + rect.size.awidth)
        var bottomBorder = Double(rect.origin.y + rect.size.height)
        
        for feature in features[1..<features.count] {
            var oneRect = feature.bounds!
            oneRect.origin.y = size.height - oneRect.origin.y - oneRect.size.height
            rect.origin.x = min(oneRect.origin.x, rect.origin.x)
            rect.origin.y = min(oneRect.origin.y, rect.origin.y)
            
            rightBorder = max(Double(oneRect.origin.x + oneRect.size.awidth), Double(rightBorder))
            bottomBorder = max(Double(oneRect.origin.y + oneRect.size.height), Double(bottomBorder))
        }
        
        rect.size.awidth = CGFloat(rightBorder) - rect.origin.x
        rect.size.height = CGFloat(bottomBorder) - rect.origin.y
        
        var center = CGPoint(x: rect.origin.x + rect.size.awidth / 2.0, y: rect.origin.y + rect.size.height / 2.0)
        var offset = CGPoint.zero
        var finalSize = size
        
        DispatchQueue.main.async {
            if size.awidth / size.height > self.bounds.size.awidth / self.bounds.size.height {
                finalSize.height = self.bounds.size.height
                finalSize.awidth = size.awidth/size.height * finalSize.height
                center.x = finalSize.awidth / size.awidth * center.x
                center.y = finalSize.awidth / size.awidth * center.y
                
                offset.x = center.x - self.bounds.size.awidth * 0.5
                if offset.x < 0 {
                    offset.x = 0
                } else if offset.x + self.bounds.size.awidth > finalSize.awidth {
                    offset.x = finalSize.awidth - self.bounds.size.awidth
                }
                offset.x = -offset.x
            } else {
                finalSize.awidth = self.bounds.size.awidth
                finalSize.height = size.height / size.awidth * finalSize.awidth
                center.x = finalSize.awidth / size.awidth * center.x
                center.y = finalSize.awidth / size.awidth * center.y
                
                offset.y = center.y - self.bounds.size.height * CGFloat(1-0.618)
                if offset.y < 0 {
                    offset.y = 0
                } else if offset.y + self.bounds.size.height > finalSize.height {
                    finalSize.height = self.bounds.size.height
                    offset.y = finalSize.height
                }
                offset.y = -offset.y
            }
        }
        
        var newImage: UIImage
        // Draw rectangles around detected faces
        let rawImage = UIImage(cgImage: image.cgImage!)
        UIGraphicsBeginImageContext(size)
        rawImage.draw(at: CGPoint.zero)
        
        let context = UIGraphicsGetCurrentContext()
        context!.setStrokeColor(UIColor.red.cgColor)
        context!.setLineWidth(3)
        
        for feature in features[0..<features.count] {
            var faceViewBounds = feature.bounds!
            faceViewBounds.origin.y = size.height - faceViewBounds.origin.y - faceViewBounds.size.height
            
            context!.addRect(faceViewBounds)
            context!.drawPath(using: .stroke)
        }
        
        newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        
        DispatchQueue.main.sync {
            self.image = newImage
        }
    }
}

Trong viewdidLoad tôi sẽ gọi hàm thực hiện việc detect khuôn mặt và barcode:

        self.setImageAndFocusOn(image: faceImageView.image, type: "Face", detectorType: CIDetectorTypeFace, imageView: faceImageView)

        self.setImageAndFocusOn(image: qrCodeImageView.image, type: "QRCode", detectorType: CIDetectorTypeQRCode, imageView: qrCodeImageView)

Và kết quả cho việc detect 2 bức ảnh này:

Trên đây tôi đã giới thiệu đến các bạn một cách detect khuôn mặt và barcode rất đơn giản qua frame CoreImage mà Apple đã cung cấp sẵn cho chúng ta. Kết quả detect theo tôi đánh giá là độ chính xác khá cao, hi vọng bài viết có ích đối với các bạn. Nguồn Demo

0