Face detection với Core Image trong iOS
Bài đăng này đã không được cập nhật trong 3 năm
I. Giới thiệu
Trong những năm gần đây, nhận diện khuôn mặt là một vấn đề được tìm hiểu rất nhiều trong ngành khoa học máy tính. Đã có rất nhiều nghiên cứu, những bài báo khoa học được đăng trên các trang công nghệ uy tín liên quan đến việc phân tích và nhận dạng khuôn mặt.
Trên iOS, việc nhận dạng khuôn mặt đã được Apple tìm hiểu từ khá sớm, các API cho việc nhận diện khuôn mặt cũng đã được Apple viết và giới thiệu từ khi iOS 5 ra đời. Các API này là một phần của thư viện Core Image, cung cấp cho lập trình viên khả năng nhận dạng khuôn mặt thông qua các API đơn giản, rất tiện và không phải mất thời gian, công sức nghiên cứu các thuật toán về nhận diện khuôn mặt.
Trong bài viết này, tôi xin giới thiệu đến các bạn một tutorial đơn giản để nhận diện khuôn mặt bằng các face detection API trong CoreImage framework
II. Demo project
Đầu tiên, chúng ta mở xCode, tạo iOS project, đặt tên project FaceDetection, chọn ngôn ngữ lập trình là Swift, devices Universal, bỏ tích các ô chọn và tạo project
Tiếp theo, chúng ta cần dữ liệu là ảnh mặt người để nhận diện, các bạn có thể lấy ảnh bất kỳ mình muốn, hoặc download ảnh giống tôi tại đây
Tiếp theo, chúng ta đổi tên ảnh thành "mark-zuckerberg" và ném vào Assets.xcassets của project. Lưu ý rằng tôi chỉ test ảnh trên simulator dùng device @2x, do vậy các bạn cần chú ý sử dụng các kích thước @1x và @3x trong project của mình.
Như vậy là bước chuẩn bị project của chúng ta đã xong. Bây giờ chúng ta sẽ bắt tay vào việc code. Đầu tiên, chúng ta mở file ViewController.swift và thêm code như sau:
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView = UIImageView(image: UIImage(named: "mark-zuckerberg"))
view.addSubview(imageView)
}
Trong đoạn code trên, chúng ta khai báo property imageView, và trong hàm viewDidLoad(), chúng ta khởi tạo imageView với ảnh chúng ta đã download, và add imageView thành subview của ViewController.
Các bạn lưu ý, khi chúng ta khởi tạo một instance của UIImageView mà không khai báo frame thì instance này sẽ mặc định có origin là (0, 0) và độ dài (width, height) lần lượt là width và height của ảnh truyền vào. Ví dụ với ảnh của tôi ở đây có kích thước 547 x 547 và scale @2x. chúng ta sẽ có frame của imageView là (0, 0, 273.5, 273.5)
chạy thử project, chúng ta có kết quả như ảnh sau:
Bước tiếp theo, chúng ta sẽ viết code để nhận diện khuôn mặt cho ảnh trên, và hiện kết quả trên màn hình. Chúng ta thêm đoạn code sau:
func faceDetection(imageView: UIImageView) {
// 1
let image = CIImage(CGImage: (imageView.image?.CGImage)!)
// 2
let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
// 3
let features = detector.featuresInImage(image) as! [CIFaceFeature]
// 4
let scale = UIScreen.mainScreen().scale
// 5
for faceFeature in features {
// 6
let faceFeatureBoundScaled = CGRect(x: faceFeature.bounds.origin.x/scale, y: faceFeature.bounds.origin.y/scale, width: faceFeature.bounds.size.width/scale, height: faceFeature.bounds.size.height/scale)
// 7
let faceView = UIView(frame: CGRect(x: faceFeatureBoundScaled.origin.x, y: imageView.image!.size.height - faceFeatureBoundScaled.size.height - faceFeatureBoundScaled.origin.y, width: faceFeatureBoundScaled.size.width, height: faceFeatureBoundScaled.size.height))
faceView.layer.borderWidth = 1
faceView.layer.borderColor = UIColor.redColor().CGColor
imageView.addSubview(faceView)
}
}
Bên trên, chúng ta viết function faceDetection với biến đầu vào là dạng UIImageView. Trong function:
- khởi tạo image với kiểu CIImage từ UIImage của UIImageView truyền vào function. CIImage là image của CoreImage framework, chúng ta cần chuyển từ UIImage sang CIImage để sử dụng trong các API của CoreImage
- khởi tạo detector kiểu CIDetector, đây chính là thứ chúng ta cần để nhận dạng khuôn mặt. Tất nhiên CIDetector cũng là class của CoreImage framework.
- Nhận dạng khuôn mặt. Vâng, các bạn không hề đọc sai đâu, chính xác đây là hàm nhận dạng khuôn mặt. thật không thể tin được, Chỉ cần gọi hàm duy nhất featuresInImage( với instance detector, chúng ta đã thực hiện được việc nhận dạng khuôn mặt. Hàm này trả về cho chúng ta 1 array dạng [CIFeature], và trong đoạn code trên, tôi đã ép kiểu về dạng [CIFaceFeature] để sử dụng bên dưới
- Chúng ta lấy scale của UIScreen. tôi test trên device dùng ảnh @2x, do vậy scale ở đây có giá trị bằng 2
- Với kết quả nhận được từ việc nhận diện khuôn mặt, chúng ta tạo vòng for qua các element của features
- Chúng ta lấy frame của face với scale của device. Tôi sẽ lấy ví dụ cho các bạn dễ hình dung, giả sử chúng ta có ảnh 600x600, khi đưa vào trong project với scale @2x, chúng ta sẽ có UIImage với kích thước 300x300. Giả sử ảnh của chúng ta nhận được kết quả 200x200 cho size của face(faceFeature.bounds.size), thì chúng ta cần chia 2 để ra kết quả ứng với UIImage(100x100)
- Khởi tạo 1 UIView có khung màu đỏ và add vào UIImageView của chúng ta. Các bạn lưu ý trục tọa độ của UIImage và CIImage ngược nhau theo trục y (hình bên dưới), nên origin theo trục y của UIView cần chuyển lại trục
Vậy là chúng ta đã có hàm để nhận diện khuôn mặt, để chạy hàm bên trên chúng ta thêm code sau:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
faceDetection(imageView)
}
Chạy thử project, chúng ta được kết quả như sau:
Vậy là chúng ta đã có thể nhận diện được vị trí của khuôn mặt chỉ bằng duy nhất một hàm của CoreImage framework.
Ngoài nhận dạng vị trí khuôn mặt, class CIFaceFeature còn cung cấp cho chúng ta các property khác để xác định vị trí mắt trái-phải, mồm, xác định mặt cười, mắt nhắm-mở,... Để xác định vị trí mắt, chúng ta thêm code như sau vào hàm faceDetection(
func faceDetection(imageView: UIImageView) {
// 1
let image = CIImage(CGImage: (imageView.image?.CGImage)!)
// 2
let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
// 3
let features = detector.featuresInImage(image) as! [CIFaceFeature]
// 4
let scale = UIScreen.mainScreen().scale
// 5
for faceFeature in features {
// 6
let faceFeatureBoundScaled = CGRect(x: faceFeature.bounds.origin.x/scale, y: faceFeature.bounds.origin.y/scale, width: faceFeature.bounds.size.width/scale, height: faceFeature.bounds.size.height/scale)
// 7
let faceView = UIView(frame: CGRect(x: faceFeatureBoundScaled.origin.x, y: imageView.image!.size.height - faceFeatureBoundScaled.size.height - faceFeatureBoundScaled.origin.y, width: faceFeatureBoundScaled.size.width, height: faceFeatureBoundScaled.size.height))
faceView.layer.borderWidth = 1
faceView.layer.borderColor = UIColor.redColor().CGColor
imageView.addSubview(faceView)
// continue code
let faceWidth = faceFeature.bounds.size.width/scale
// 8
if faceFeature.hasLeftEyePosition {
// 9
let leftEye = UIView(frame: CGRect(origin: CGPointZero, size: CGSize(width: faceWidth*0.2, height: faceWidth*0.2)))
leftEye.backgroundColor = UIColor.greenColor()
// 10
leftEye.center = CGPoint(x: faceFeature.leftEyePosition.x/scale, y: imageView.frame.size.height - faceFeature.leftEyePosition.y/2)
imageView.addSubview(leftEye)
}
}
}
Trong đoạn code trên: 8. Check face feature có vị trí mắt trái hay không để thực hiện đoạn code thêm view vào vị trí mắt trái 9. Tạo UIView leftEye với size bằng 0.2 của chiều rộng face 10. Gán lại vị trí của leftEye theo vị trí của leftEyePosition. tương tự như faceView bên trên, chúng ta cũng cần đổi lại position theo trục y cho leftEye view
Build và chạy thử project, chúng ta sẽ được kết quả như hình sau:
Ngoài vị trí mắt trái, các bạn hoàn toàn có thể nhận dạng được các vị trí mắt phải và mồm của khuôn mặt. Việc code cũng hoàn toàn tương tự nên tôi xin không viết thêm code và kết thúc demo này tại đây.
III. Kết luận
Trên đây, tôi đã giới thiệu đến các bạn tính năng nhận diện khuôn mặt sử dụng CoreImage framework trên iOS. Đây là một tính năng rất hay, được Apple cung cấp API rất dễ sử dụng cho các lập trình viên để họ có thể tạo ra các app tuyệt vời trên AppStore. ngoài ra, tôi cũng đã giới thiệu với các bạn một demo nhỏ để giúp các bạn mới làm quen với tính năng này dễ tiếp cận hơn, trong quá trình viết bài còn nhiều sơ sót, xin các bạn lượng thứ. Cuối cùng, tôi xin cảm ơn các bạn đã theo dõi bài viết này.
Have a nice day!!!
All rights reserved