Trong bài viết này, chúng ta sẽ sử dụng các framework machine learning mới được giới thiệu của iOS là Vision và CoreML để xây dựng một app nhận diện đối tượng thời gian thực qua camera live view.

Hình ảnh thu được sẽ được framework Vision phân tích, xử lý và dựa vào trained model có sẵn để đưa ra phán đoán về tên của đồ vật đó.

I. Introduction to Vision and CoreML

1. Vision

Vision là một framework được Apple giới thiệu từ phiên bản iOS 11. Với Vision, chúng ta có thể áp dụng các kỹ thuật phân tích và xử lý hình ảnh để nhận diện khuôn mặt, phát hiện các đặc trưng ảnh, phân loại ảnh và video.

Một số ứng dụng của Vision có thể kể đến như:

  • Nhận diện, nhận dạng khuôn mặt (Face Detection and Recognition).
  • Phân tích ảnh sử dụng học máy (Machine Learning Image Analysis).
  • Nhận diện mã vạch (Barcode Detection).
  • Nhận diện chữ viết (Text Detection).
  • Nhận diện và theo dõi đối tượng (Object Detection and Tracking).

2. CoreML

Giống với Vision, CoreML (Core Marchine Learning) được lần đầu giới thiệu ở WWDC 2017 cùng với iOS 11. CoreML cho phép tích hợp các model học máy được huấn luyện (trained machine learning model) vào ứng dụng iOS.

Một trained model là kết quả của việc áp dụng một thuật toán học máy vào một tập dữ liệu huấn luyện (training data). Từ đó, nó sẽ đưa ra các dự đoán (prediction) với một dữ liệu đầu vào mới. Ví dụ một model được train với các dữ liệu về nhiệt độ, độ ẩm, dự báo thời tiết của các ngày trong năm, sẽ có thể đưa ra dự đoán về tình hình nắng mưa dựa trên một dữ liệu đầu vào (nhiệt độ, độ ẩm) mới.

Ngoài ra, CoreML đóng vai trò làm nền tảng cho các framework iOS khác. Ví dụ CoreML hỗ trợ Vision phân tích ảnh, hỗ trợ framework Foundation việc xử lý ngôn ngữ tự nhiên, hỗ trợ GameplayKit việc đánh giá cây quyết định (learned decision trees).

II. Practice

1. Configure AVCaptureSession

Mở Xcode lên, tạo một Single View App mới, trong file ViewController.swift, import AVKit – framework thực hiện các xử lý cơ bản liên quan đến audio/video trong iOS.

Đầu tiên, chúng ta cần phải configure một AVCaptureSession object để bắt đầu capture hình ảnh từ camera. Thêm đoạn code sau vào func viewDidLoad().

override func viewDidLoad() {
    super.viewDidLoad()
    // Tạo AVCaptureDevice cho media type video (camera)
    guard let camera = AVCaptureDevice.default(for: .video), 
          // Tạo input source object cho device camera
          let cameraInput = try? AVCaptureDeviceInput(device: camera) else { 
        return
    }
    // Tạo object của class AVCaptureSession. Object này quản lý việc capture và điều phối luồng dữ liệu từ input các thiết bị tới các output
    // Ở đây chúng ta đang cần capture hình ảnh từ input của camera điện thoại
    let captureSession = AVCaptureSession()
    // Set chất lượng của output: low, medium, high, photo...
    captureSession.sessionPreset = .photo 
    // Thêm input source vào session
    captureSession.addInput(cameraInput) 
    // Run session và bắt đầu captue video
    captureSession.startRunning() 
}

Vì app sử dụng nguồn dữ liệu hình ảnh từ camera nên chúng ta cần xin user quyền truy cập camera phone bằng cách thêm key NSCameraUsageDescription vào Info.plist.

<key>NSCameraUsageDescription</key>
<string>Needs permission to access your camera phone</string>

2. Add camera preview layer

Sau khi configure được capture session và input, chúng ta cần hiển thị hình ảnh live view camera lên view.

Trong func viewDidLoad(), tiếp tục thêm đoạn code sau:

    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = view.frame
    view.layer.addSublayer(previewLayer)

Chạy app, nhấn OK khi app xin quyền truy cập camera, chúng ta sẽ thấy live view của camera được hiển thị.

3. Add AVCaptureSession output

Thêm đoạn code sau vào func viewDidLoad()

    let dataOutput = AVCaptureVideoDataOutput()
    dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "myQueue"))
    captureSession.addOutput(dataOutput)

Thêm extension conform delegate của AVCaptureVideoDataOutput (AVCaptureVideoDataOutputSampleBufferDelegate).

Mỗi khi camera capture được một frame ảnh, func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)sẽ được gọi.

Trong extension ViewController, implement function sau để xử lý mỗi frame thu được đó.

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
                       from connection: AVCaptureConnection) {
        // Xử lý các frame ảnh thu được từ camera
    }
    
}

4. Analysis captured output

Đầu tiên hãy import thêm framework Vision vào ViewController.

Tiếp theo vào trang https://developer.apple.com/machine-learning/ download một CoreML model để làm tập dữ liệu training. Ví dụ model ResNet50 chứa 1000 category training data về cây cối, động vật, thực phẩm, phương tiện, con người…

Kéo thả file model vừa download vào project, chọn Copy items if needed.

Thêm một UILabel tùy ý vào view và đặt tênresultLabel. Label này sẽ dùng để hiển thị kết quả xử lý machine learning.

Hoàn thiện func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection).

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
                       from connection: AVCaptureConnection) {
        // Xử lý các frame ảnh thu được từ camera
        // Các frame ảnh dưới dạng CMSampleBuffer sẽ được convert sang CVPixelBuffer
        guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
              // Tạo core model từ training model Resnet50
              let model = try? VNCoreMLModel(for: Resnet50().model) else {
            return
        }
        // Tạo request xử lý machine learning, sau khi xử lý xong sẽ gọi closure
        let request = VNCoreMLRequest(model: model) { [weak self] (request, error) in
            // Check nếu có lỗi xảy ra
            if error != nil {
                DispatchQueue.main.async {
                    self?.resultLabel.text = "Error"
                }
                return
            }
            // Sau khi request được xử lý sẽ có một tập các kết quả dự đoán kèm với độ chính xác
            // Lấy ra kết quả đầu tiên, có độ chính xác cao nhất
            guard let firstResult = request.results?.first as? VNClassificationObservation else {
                return
            }
            // Hiển thị tên object và độ chính xác của dự đoán trên main thread
            DispatchQueue.main.async {
                self?.resultLabel.text = "(firstResult.identifier) - (firstResult.confidence)"
            }
        }
        // Thực hiện request xử lý với đầu vào là buffer các pixel
        try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request])
    }
    
}

Cơ bản app đã hoàn thành, chạy app và thử đưa camera đến các đối tượng cần nhận diện. Kết quả:

Nguồn tham thảo: