Đây là một bài toán không mới và cũng có khá nhiều bạn coi đây như một dự án để thực hành kĩ năng xử lý với các mô hình Deep Learning. Mình khuyên các bạn nên thực hành bài toán này vì nó sẽ giúp cho các bạn có điều kiện làm quen với nhiều loại mô hình khác nhau trong Deep Learning như Object Detection, Instance Segmentation, Optical Character Recognition. Pipeline cơ bản của nó có thể giải quyết như flow sau đây:
Cuối cùng tổng hợp và sắp xếp lại ta được một bài toán nhận diện chứng minh thư hoàn chỉnh. OK giờ chúng ta đi vào phần chính sẽ được giải quyết trong ngày hôm nay nhé
Là một kĩ thuật phổ biến trong xử lý ảnh Nó chính là quá trình biến đổi các bộ dữ liệu khác nhau về cùng một hệ tọa độ. Các hình ảnh được chụp từ điện thoại, từ cảm biến được chụp bởi các góc độ khác nhau. Có nhiều phương pháp để thực hiện điều này như sử dụng Feature Based hay Template Matching. Tuy nhiên mục đích cuối cùng vẫn là làm sao để thu được một bức ảnh ngay ngắn trong hệ tọa độ để dễ dàng xử lý nhất. Ở đây với bài toán chứng minh thư bạn sẽ cần như trong hình sau:
Một ảnh được chụp từ bất kì tư thế nào cũng sẽ được xoay lại thẳng và ngay ngắn
Vậy làm thế nào để thực hiện được điều này, chúng ta có thể nghĩ ngay đến Geometric Transformations of Images trong xử lý ảnh mà cụ thể đó là Perspective Transformation - mapping 4 tọa độ ở ảnh gốc vào 4 tọa độ ở ảnh đích. Chúng ta có thể thấy ảnh ví dụ kinh điển như sau:
Trong đó bàn cờ đã được trải phẳng trong hệ tọa độ mới giúp chúng ta dễ xử lý hơn. Vậy nếu đã chọn hướng giải quyết là Perspective Transformation rồi thì câu hỏi tiếp theo của chúng ta đó là:
Làm sao để tìm được 4 điểm trên ảnh nguồn. Cụ thể là 4 góc của chứng minh thư?
Để trao lời cho câu hỏi trên thì chúng ta có nhiều cách tuy nhiên cách mình thấy đơn giản nhất đó là xây dựng một mô hình Deep Leanring để học được vị trí của 4 góc. Đó chính là mô hình Detection Object mà đã có rất nhiều bài viết về vấn đề này các bạn có thể tham khảo trên Organization của Sun* AI Research Team nhé. Vậy để thực hiện được điều đó thì chúng ta cần phải có dữ liệu chứ phải không. Bây giờ chúng ta sẽ tìm hiểu cách để làm dữ liệu nhé.
Các bạn có thể tiến hành crawl data từ các nguồn như Google Images hoặc ảnh trên FB. Gợi ý cho các bạn có thể search các từ khóa như tìm đồ lạc, rơi giấy tờ ... hoặc vào các trang cầm đồ, cho vay tín dụng để có thể crawl được nhiều dữ liệu hơn. Sau khi chuẩn bị được một lượng nhỏ dữ liệu các bạn lưu nó vào cùng một thư mục như sau
Bước tiếp theo chúng ta cần gán nhãn cho dữ liệu nhé.
Chúng ta sử dụng tool LabelImg để tiến hành annotate dữ liệu. Trên trang chủ của nó đã có hướng dẫn sử dụng khá chi tiết rồi các bạn chỉ cần cài đặt theo thôi nhé. Mở thư mục hiện tại và tiến hành annotate nhé.
Ở đây chúng ta cần annotate 4 góc theo 4 class tương ứng như sau:
['top_left', 'top_right', 'bottom_left', 'bottom_right']
Các bạn chịu khó gán nhãn càng nhiều thì tay sẽ càng to và tất nhiên là quả ngọt cũng sẽ đến vì mô hình sẽ có độ chính xác tốt hơn nếu dữ liệu đủ đa dạng và đủ nhiều. Còn nếu bạn lười thì mình xin chia sẻ sẵn một data demo đã được gán nhãn sẵn (150 ảnh thôi) các bạn có thể tải về dùng luôn tại đây. Tất nhiên là vì tập nhỏ nên chỉ để demo thôi các bạn nhé còn muốn tốt hơn thì phải lao động thôi.
Sau khi gán nhãn xong bạn sẽ thu được một thư mục với các file XML tương ứng như sau:
Detecto là một thư viện được viết trên nền của PyTorch và rất dễ sử dụng với mục đích để các bạn lười code, tốn ít code nhất vẫn có thể training được mô hình object detect. Detecto hỗ trợ các bạn Transfer Learning với custom dataset giống như dataset chứng minh thư của chúng ta. Thư viện này có thể chạy inference trên cả image và video. Các bạn có thể thấy ảnh minh họa
Các bạn thấy hay có thể star cho tác giả tại đây . Giờ thì bắt đầu vào bước training nhé
Rất đơn giản các bạn chỉ thực hiện như sau
from detecto import core, utils, visualize
Cũng vô cùng đơn giản luôn. Các bạn chỉ cần khai báo đường dẫn thư mục của mình đồng thời khai báo các classses được sử dụng trong bước annoate trước đó.
dataset = core.Dataset('.data/sample')
model = core.Model([
'top_left', 'top_right', 'bottom_left', 'bottom_right'
])
Như vậy là bước định nghĩa và load dataset đã được thư viện Detecto giải quyết giúp chúng ta rồi. Các bạn không cần phải quan tâm đến format của file label hay các phần transform dữ liệu nữa. Giờ chúng ta sẽ đi vào training mô hình thôi
Rất đơn giản chỉ với 1 dòng code.
losses = model.fit(dataset, epochs=500, verbose=True, learning_rate=0.001)
Việc tiếp theo là ngồi đợi thôi
Epoch 1 of 500
Epoch 2 of 500
Epoch 3 of 500
Epoch 4 of 500
Epoch 5 of 500
Epoch 6 of 500
Epoch 7 of 500
Epoch 8 of 500
Epoch 9 of 500
Please waiting ...
Sau khi training các bạn lưu lại mô hình để sử dụng tiếp nhé. Rất đơn giản với 1 câu lệnh
model.save('id_card_4_corner.pth')
Nếu các bạn cũng lười training nốt thì đây là mô hình demo được save ở tại epoch thứ 30 nhé. Tất nhiên là kết quả chưa thể tốt được do mới training được 1 số epoch ngắn và tập dữ liệu nhỏ.
Sau khi lưu mô hình chúng ta tiến hành kiểm tra lại kết quả với một file khác được down từ trên mạng về (không nằm trên tập train nhé). Giả sử như file này chẳng hạn.
Chúng ta tiến hành đọc file
fname = 'data/CMT_0106321395.JPG'
image = utils.read_image(fname)
và predict thử xem kết quả của mô hình như thế nào
labels, boxes, scores = model.predict(image)
Đầu ra của mô hình sẽ trả về danh sách cách labels
, vị trí của các boxes
và confident tương ứng trong scores
. In thử các giá trị này chúng ta sẽ thấy nó trả ra khá nhiều box khác nhau
print(labels)
['bottom_left', 'bottom_right', 'top_left', 'top_right', 'bottom_left']
tương ứng với các boxes
priont(boxes)
tensor([[ 16.1044, 647.4228, 83.3507, 711.1550],
[894.2018, 636.8340, 956.9724, 701.6462],
[ 11.0550, 87.7889, 79.4745, 149.2691],
[892.7690, 80.5926, 953.7268, 139.2006],
[ 15.8271, 83.0950, 82.5982, 147.1947]])
Chúng ta vẫn có thể vẽ thử vị trí của các boxes này
import cv2
for i, bbox in enumerate(boxes):
bbox = list(map(int, bbox))
x_min, y_min, x_max, y_max = bbox
cv2.rectangle(image,(x_min,y_min),(x_max,y_max),(0,255,0),2)
cv2.putText(image, labels[i], (x_min, y_min), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0))
Thu được kết quả như sau:
Có thể thấy rằng mô hình vẫn còn bị bắt nhầm khá nhiều. Tuy vẫn ra đủ 4 loại labels. Chúng ta có các cách xử lý như sau:
non_max_suppression
Ở đây chúng ta sẽ thạm khảo cách thứ 3 còn hai cách trên thì tùy thuộc vào độ to tay của các bạn nhé. Chúng ta sẽ bàn tiếp phần này trong phần tiếp theo
» Tin mới nhất:
» Các tin khác: