Pada artikel kali ini kita akan mempelajari bagaimana cara mendeteksi objek pada gambar di iOS.
Kita akan membuat aplikasi sederhana untuk menghitung jumlah uang koin yang ada di gambar secara real-time serta menampilkan Bounding Box dari setiap koin yang terdeteksi.
Object Detection adalah proses untuk mendeteksi keberadaan suatu objek dalam sebuah gambar. Object Detection biasanya memanfaatkan teknologi Machine Learning dalam proses pembuatannya. Salah satu contoh penerapan Object Detection adalah pada mobil Self Driving.
Di iOS sendiri kita bisa menggunakan Framework Vision untuk melakukan Object Detection. Vision merupakan salah satu Framework dari Apple yang dapat kita gunakan untuk melakukan berbagai task terkait dengan Computer Vision, misalnya seperti mendeteksi objek, mendeteksi wajah, membaca barcode, dll.
Sebelumnya saya telah menulis artikel mengenai Image Classification pada iOS, kamu bisa membacanya di sini: https://blog.alfin.dev/article/image-classification-pada-ios.
Lalu apa bedanya dengan Object Detection? Jadi, kalau Image Classification itu akan mengembalikan hasil berupa 1 label saja yang mendeskripsikan suatu gambar. Sedangkan Object Detection bisa mengembalikan hasil lebih dari 1 label pada suatu gambar, dan pada Object Detection, kita juga akan mendapatkan Bounding Box dari setiap objek yang terdeteksi.
Dalam Machine Learning ada satu komponen penting bernama Model, Model adalah sebuah file yang telah dilatih untuk mengenali pola tertentu berdasarkan data yang kita berikan dan bisa membuat prediksi terhadap data baru.
Untuk membuat Model, kita akan menggunakan aplikasi Create ML bawaan Xcode, namun sebelum itu kita harus mengumpulkan dataset atau data-data yang akan kita gunakan dalam melatih Model kita.
Silakan ambil beberapa foto uang koin. Untuk jumlahnya sendiri dari Apple disarankan minimal 30 foto per kategori. Semakin banyak Datasets yang kita punya tentunya akan semakin akurat Model kita dalam memprediksi data baru.
Lalu kita bagi Datasets kita menjadi 2 yaitu untuk training, dan untuk testing. Jumlah data testing disarankan sekitar 20% dari jumlah data training.
Berikut adalah beberapa tips dalam membuat Datasets agar model kita lebih akurat dalam memprediksi data baru.
Minimal 30 foto per kategori.
Ukuran foto setidaknya 299 x 299 pixel.
Jumlah data per kategori usahakan tidak terlalu jauh, misalnya kategori "Kucing" ada 100 foto, maka kategori yang lain sebaiknya ada sekitar 100 foto juga.
Ambil foto dari berbagai angle atau sudut pengambilan gambar.
Ambil foto dari pencahayaan yang berbeda-beda.
Ambil foto dari berbagai background.
Sebelum kita melatih Model kita dengan Datasets yang baru saja kita kumpulkan, kita perlu untuk membuat sebuah file Annotation dalam format json.
File Annotation adalah sebuah file yang mendeskripsikan kategori serta bounding box objek yang ingin kita deteksi. Format file Annotation itu seperti ini:
Annotations.json1[2 {3 "imagefilename": "coin.png",4 "annotation": [5 {6 "label": "Coin",7 "coordinates": {8 "y": 156.062,9 "x": 195.122,10 "height": 148.872,11 "width": 148.0312 }13 }14 ]15 }16]
Nah agar lebih mudah dalam membuat file Annotation, kita bisa menggunakan aplikasi "AnnotationCreater", kamu bisa mengunduhnya di sini: https://apps.apple.com/id/app/annotationcreater/id1623375683?mt=12
Cara menggunakannya pun sangat mudah, kita tinggal import foto-foto kita, kemudian buat kategori baru, lalu gambar bounding box di mana objek yang ingin kita deteksi berada. Terakhir kita tinggal export dengan cara klik icon "Export" pada bagian kanan atas.
Silakan buka aplikasi Create ML dengan cara klik "Xcode -> Open Developer Tool -> Create ML"
Lalu buat dokumen baru, pilih template "Object Detection", dan beri nama "CoinDetector".
setelah itu klik icon "+" untuk memasukkan data training dari folder "Annotation Export"
kita juga memberikan beberapa parameter seperti:
Algorithm
Algoritma yang digunakan dalam training model. Pada CreateML ada 2 pilihan algoritma:
Full Network
Kita melatih model kita dari awal. Cocok jika kita punya banyak data (200+) dan ingin support minimal iOS 12.
Transfer Learning
Kita menggunakan "object feature print" dari Vision. Cocok jika kita hanya punya sedikit data dan ingin ukuran dari model kita lebih kecil. Namun algoritma ini hanya mendukung iOS 14 ke atas.
Iterations
Berapa kali Datasets kita ingin dilatih.
Batch Size
Jumlah data yang dilatih dalam 1 Iteration. Semakin banyak Batch Size, jumlah memori yang digunakan juga akan semakin banyak.
Grid Size (khusus Full Network)
Jadi cara algoritma Full Network (YOLOv2) dalam mendeteksi objek adalah dengan membagi gambar kita menjadi beberapa Cell (algoritma ini akan mendeteksi 1 objek saja dalam 1 Cell). Nah, kita bisa mengatur ukuran Cell tersebut melalui Grid Size.
kemudian kita klik "Train" untuk melatih model kita. Lalu tunggu sampai proses training selesai, training model pada Object Detection memang agak lebih lama daripada Image Classification.
Setelah itu kita bisa mengevaluasi apakah model kita sudah cukup akurat untuk memprediksi suatu data atau belum.
Kita juga bisa melakukan preview dengan cara drag image kita ke tab "Preview".
Untuk meng-export model, silakan ke tab "Output" dan klik icon "Get" lalu pilih lokasi di mana model kita akan disimpan.
Sekarang kita mulai membuat project iOS nya. Silakan buka Xcode dan buat project baru dengan cara klik "File -> New -> Project", pilih interface "Storyboard" dan "Swift" sebagai bahasanya.
Setelah itu tarik file model yang telah kita buat (berekstensi .mlmodel) ke dalam project kita.
Tampilan dari aplikasi yang akan kita buat cukup sederhana, yaitu:
Preview View (UIView): Untuk menampilkan preview dari kamera.
Total Label (UILabel): Untuk menampilkan label dari jumlah koin yang terdeteksi.
Jadi, silakan buat UI nya di Storyboard, dan jangan lupa untuk menghubungkan outlet-nya ke class ViewController
ViewController.swift1class ViewController: UIViewController {23 @IBOutlet weak var previewView: UIView!4 @IBOutlet weak var totalLabel: UILabel!56 override func viewDidLoad() {7 super.viewDidLoad()8 }9}
Kita akan membuat 2 CALayer, yang pertama previewLayer untuk menampilkan Preview dari kamera, kemudian yang kedua adalah detectionLayer untuk menggambar Bounding Box dari setiap koin yang terdeteksi.
Lalu kita juga perlu menambahkan sebuah method bernama updateLayerGeometry untuk memperbaiki posisi dan ukuran dari detectionLayer.
ViewController.swift1private var previewLayer: AVCaptureVideoPreviewLayer!2private var detectionLayer: CALayer!34override func viewDidLoad() {5 super.viewDidLoad()67 setupLayers()8 updateLayerGeometry()9}1011private func setupLayers() {12 // Configure the preview layer13 previewLayer = AVCaptureVideoPreviewLayer(session: session)14 previewLayer.videoGravity = .resizeAspectFill15 previewLayer.frame = self.previewView.layer.bounds16 self.previewView.layer.addSublayer(previewLayer)1718 // Configure the detection Layer19 detectionLayer = CALayer()20 detectionLayer.bounds = CGRect(21 x: 0.0,22 y: 0.0,23 width: cameraSize.width,24 height: cameraSize.height25 )26 detectionLayer.position = CGPoint(27 x: self.previewView.layer.bounds.midX,28 y: self.previewView.layer.bounds.midY29 )30 self.previewView.layer.addSublayer(detectionLayer)31}3233private func updateLayerGeometry() {34 let bounds = self.previewView.layer.bounds3536 let xScale: CGFloat = bounds.size.width / cameraSize.height37 let yScale: CGFloat = bounds.size.height / cameraSize.width3839 var scale = fmax(xScale, yScale)40 if scale.isInfinite {41 scale = 1.042 }4344 CATransaction.begin()45 CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)4647 // rotate the layer into screen orientation, scale and mirror48 detectionLayer.setAffineTransform(49 CGAffineTransform(50 rotationAngle: CGFloat(.pi / 2.0)51 )52 .scaledBy(x: scale, y: -scale)53 )5455 // center the layer56 detectionLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)5758 CATransaction.commit()59}
Tambahkan kode berikut pada file ViewController.swift
ViewController.swift1import UIKit3import Vision4import CoreML67class ViewController: UIViewController {89 @IBOutlet weak var previewView: UIView!10 @IBOutlet weak var totalLabel: UILabel!1113 private var request: VNCoreMLRequest!14 private var previewLayer: AVCaptureVideoPreviewLayer!15 private var detectionLayer: CALayer!1617 override func viewDidLoad() {18 super.viewDidLoad()1921 setupVision()2223 setupLayers()24 updateLayerGeometry()25 }2628 private func setupVision() {29 guard let coinDetector = try? CoinDetector(configuration: MLModelConfiguration()) else {30 fatalError("Failed to create an object detector model instance.")31 }3233 guard let model = try? VNCoreMLModel(for: coinDetector.model) else {34 fatalError("Failed to create a `VNCoreMLModel` instance.")35 }3637 let request = VNCoreMLRequest(38 model: model,39 completionHandler: visionRequestHandler40 )41 request.imageCropAndScaleOption = .scaleFit42 self.request = request43 }45}
Pada method setupVision, kita membuat Vision Request untuk melakukan Object Detection menggunakan Model kita, Request ini nantinya akan dijalankan pada setiap frame sehingga akan terlihat real-time.
Kemudian kita handling hasil dari proses Object Detection, kita akan menampilkan Bounding Box dari setiap koin yang terdeteksi serta meng-update Label kita sesuai dengan jumlah koin yang terdeteksi.
ViewController.swift1private func visionRequestHandler(_ request: VNRequest, error: Error?) {2 if let error = error {3 print("Vision image detection error: \(error.localizedDescription)")4 return5 }67 if request.results == nil {8 print("Vision request had no results.")9 return10 }1112 guard let observations = request.results as? [VNRecognizedObjectObservation] else {13 print("VNRequest produced the wrong result type: \(type(of: request.results)).")14 return15 }1617 DispatchQueue.main.async {18 CATransaction.begin()19 CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)2021 self.detectionLayer.sublayers = nil // remove all the old recognized objects22 for observation in observations {23 let objectBounds = VNImageRectForNormalizedRect(24 observation.boundingBox,25 Int(self.cameraSize.width),26 Int(self.cameraSize.height)27 )2829 let shapeLayer = self.createRoundedRectLayer(objectBounds)30 self.detectionLayer.addSublayer(shapeLayer)31 }32 self.updateLayerGeometry()33 CATransaction.commit()3435 // Set Total Label36 let total = observations.count37 self.totalLabel.text = "\(total) Coins"38 }39}4041private func createRoundedRectLayer(_ bounds: CGRect) -> CALayer {42 let shapeLayer = CALayer()43 shapeLayer.bounds = bounds44 shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)45 shapeLayer.backgroundColor = UIColor.yellow.withAlphaComponent(0.15).cgColor46 shapeLayer.cornerRadius = 847 shapeLayer.borderColor = UIColor.yellow.cgColor48 shapeLayer.borderWidth = 1.549 return shapeLayer50}
Sekarang, kita akan mengintegrasikan Vision dengan AVFoundation agar bisa mendeteksi objek secara real-time.
Pada file bagian paling atas, silakan import AVFoundation:
1import AVFoundation
Lalu setelah deklarasi properti detectionLayer, tambahkan beberapa properti berikut:
1private var request: VNCoreMLRequest!23private var previewLayer: AVCaptureVideoPreviewLayer!4private var detectionLayer: CALayer!57private let session = AVCaptureSession()8private var cameraSize: CGSize!9private let videoOutputQueue = DispatchQueue(label: "video-output-queue", qos: .userInitiated)
Kemudian tambahkan function berikut untuk Setup Capture Session:
1private func setupCaptureSession() {2 session.beginConfiguration()34 // Add the video input to the capture session5 let camera = AVCaptureDevice.default(6 .builtInWideAngleCamera,7 for: .video,8 position: .back9 )!1011 // Connect the camera to the capture session input12 let cameraInput = try! AVCaptureDeviceInput(device: camera)13 session.addInput(cameraInput)1415 session.sessionPreset = .vga640x4801617 // Create the video data output18 let videoOutput = AVCaptureVideoDataOutput()19 videoOutput.alwaysDiscardsLateVideoFrames = true20 videoOutput.videoSettings = [21 String(kCVPixelBufferPixelFormatTypeKey): Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)22 ]23 videoOutput.setSampleBufferDelegate(self, queue: videoOutputQueue)2425 // Add the video output to the capture session26 session.addOutput(videoOutput)2728 // Set camera size29 let dimension = CMVideoFormatDescriptionGetDimensions(camera.activeFormat.formatDescription)30 cameraSize = CGSize(31 width: CGFloat(dimension.width),32 height: CGFloat(dimension.height)33 )3435 session.commitConfiguration()36}
Pada kode di atas, kita mendelegasikan SampleBufferDelegate ke self agar kita bisa mengakses tiap frame yang dicapture oleh AVFoundation. Nah, kita akan mendeteksi objek terhadap frame-frame tersebut.
Silakan tambahkan kode berikut di bagian paling bawah:
1extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {2 func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {3 guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }45 do {6 let handler = VNImageRequestHandler(cvPixelBuffer: imageBuffer)7 try handler.perform([request])8 } catch {9 print(error.localizedDescription)10 }11 }12}
Terakhir, kita akan menjalankan Capture Session. Ubah isi dari viewDidLoad menjadi seperti ini:
1override func viewDidLoad() {2 super.viewDidLoad()34 setupVision()6 setupCaptureSession()78 setupLayers()9 updateLayerGeometry()1012 DispatchQueue.global(qos: .background).async {13 self.session.startRunning()14 }16}
Kalau kita jalankan aplikasinya, maka hasilnya akan seperti ini 🎉
Sekarang kita telah mengetahui bagaimana cara membuat aplikasi Object Detection menggunakan Create ML dan Vision Framework serta mengintegrasikannya dengan AVFoundation agar kita bisa mendeteksi objek secara real-time, selanjutnya mungkin kamu bisa kembangkan lagi project ini untuk mendeteksi koin sesuai nominalnya apakah Rp100, Rp500, Rp1000, atau kamu bisa juga menambahkan suara menggunakan AVSpeechSynthesizer ketika koin terdeteksi, dll 😄
Oke mungkin itu saja yang bisa saya bagikan kali ini, kalau kamu merasa artikel ini bermanfaat silakan Like & Share artikel ini ke teman-teman kamu atau jika kamu punya pertanyaan, tulis aja di kolom komentar, Thank you! 😁 🙏
Source code lengkapnya bisa kamu lihat melalui link berikut: https://github.com/alfinsyahruddin/CoinDetector
Always open to new ideas. 🕊️
Articles that you might want to read.
Tutorial Image Classification menggunakan Create ML dan Vision Framework.
Buat aplikasi AI pertama-mu dalam 10 menit ⚡️
Menulis kode yang mudah untuk di-test pada Swift.