Chi tiết cách đo kích thước vật thể bằng OpenCV thuần

Chào tuần mới anh Mì AI, hôm nay chúng ta sẽ sử dụng OpenCV thuần với Python để thử đo kích thước các vật thể trong ảnh nhé.

Việc đo kích thước này mình cũng nói trước luôn mấy điểm:

  • Thứ nhất, việc đo chỉ mang tính chất tham khảo, không thể nào chính xác 100% như mang thước ra đo hoặc dùng các máy đo chuyên dụng
  • Thứ hai, do ở đây mình sử dụng một ảnh bất kì làm sample, không cân chỉnh camera một cách bài bản nên có thể kích thước sẽ ảnh hưởng bởi góc nghiêng của camera.

Tuy nhiên, với mục đích học tập thì hoàn toàn chấp nhận được. Ngoài ra mình cũng đã có 1 bài đo khoảng cách tương tự các bạn cũng có thể đọc để tham khảo nhé: tại đây.

Okie con dê! Bắt đầu tìm hiểu nào!

Phần 1 – Làm sao để đo kích thước vật thể trong ảnh?

Rõ ràng với logic thông thường thì muốn đo kích thước vật thể trong ảnh ta phải biết được các yếu tố như: Khoảng cách từ camera đến vật khi chụp, Tiêu cự của camera, góc chụp…..rất phức tạp như hình dưới:

Nguồn: Tại đây

Tuy nhiên với anh em Mì ngại học toán thì mình sẽ có một cách đơn giản hơn như sau: (bạn nào thích làm phức tạp có thể nghiên cứu cách căn chỉnh camera tại đây).

  • Đầu tiên ta chọn một vật nào đó ta biết kích thước gọi là “Vật tham chiếu“. Ta nên chọn một viên bi tròn hay vật gì đó hình tròn để dễ dàng hơn trong việc tính toán. Giả sử viên bi có đường kính thật $Dr = 2cm = 20mm$
  • Tiếp theo ta đặt Vật tham chiếu đó vào ảnh cùng với các Vật muốn đo kích thước và chụp một tấm.
  • Sau đó, Giả sử ta đo được đường kính trong ảnh của Vật tham chiếu là $Dc = 100 pixel$, ta suy ra kích thước thật của 1 pixel là 20/100 = 0.2mm (goi là số $P$)
  • Bước cuối cung, ta đo kích thước trong ảnh Vật muốn đo bằng pixel, sau đó nhân với $P$ là sẽ ra kích thước thật của vật.

Các bạn đã nắm được tinh thần rồi chứ? Nếu còn vướng mắc thì các bạn cứ post lên Group trao đổi, chia sẻ: https://facebook.com/groups/miaigroup để thảo luận nhé.

Phần 2 – Triển khai bài toán

Việc đầu tiên là ta phải tìm cách đo được kích thước bằng pixel của các vật trong ảnh, đó là yếu tố tiên quyết cho bài toán này.

Giả sử ta có một tấm ảnh như sau và ở đây Vật tham chiếu là đồng xu 1 Rufiuaa của Maldives (kỷ niệm chuyến đi năm ngoái của mình kaka) và Vật cần đo là chiếc USB bên cạnh.

đo kích thước vật thể

Ta đã biết đồng xu này có kích thước thật là 2cm = 20mm. Bây giơ chúng ta sẽ cùng nhau thực hiện các bước nhé.

Tiền xử lý và tìm cạnh

Việc đầu tiên là phải thực hiện chuyển về ảnh xám và tìm cạnh để bước sau tìm contour:

# Đọc file ảnh image = cv2.imread(filename) # Chuyển thành ảnh xám gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Làm mờ ảnh gray = cv2.GaussianBlur(gray, (blur_kernel, blur_kernel), 0) # Áp dụng Canny tìm cạnh edged = cv2.Canny(gray, canny_low, canny_high) edged = cv2.dilate(edged, (d_e_kernel, d_e_kernel), iterations=1) edged = cv2.erode(edged, (d_e_kernel, d_e_kernel), iterations=1)
Code language: PHP (php)

Sau bước này ta sẽ có được bức ảnh như sau:

đo kích thước vật thể

Nhìn qua ta đã thấy có thể tìm được các contour của đồng xu và cái USB rồi. Chú ý là ở đây do nền của mình khá phức tạp nên việc tìm contour khá khó,các bạn có thể thử với nền đơn giản hơn (đơn sắc, phẳng….) cho dễ nhé.

Tìm contour và xác định kích thước bằng pixel

Rồi, bay giờ ta sẽ tìm các contour trong hình, và loại bỏ các contour nhỏ để giữ lại 2 contour chính là đồng xu và USB.

# Tìm các Contour trong ảnh cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # Sắp xếp các contour từ trái qua phải (cnts, _) = contours.sort_contours(cnts) P = None # Duyệt các contour for c in cnts: # Nếu contour quá nhỏ -> bỏ qua if cv2.contourArea(c) < area_threshold: continue
Code language: PHP (php)

Với các contour tìm được, chúng ta sẽ thực hiện tính toán kích thước của nó trong bằng, tính bằng Pixel. Ở đây chúng ta sử dụng một khái niệm MinAreaRect chứ ko phải Bounding Box nhé.

Các bạn có thể phân biệt theo hình sau (đỏ là MinAreaRect và xanh là bounding box):

đo kích thước vật thể
Nguồn: Tại đây
# Lấy minRect box = cv2.minAreaRect(c) # Lấy tọa độ các đỉnh của MinRect box = cv2.boxPoints(box) box = np.array(box, dtype="int") # Sắp xếp các điểm theo trình tự box = perspective.order_points(box) # Vẽ contour cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2) # Tinh toán 4 trung diểm của các cạnh (tl, tr, br, bl) = box (tltrX, tltrY) = midpoint(tl, tr) (blbrX, blbrY) = midpoint(bl, br) (tlblX, tlblY) = midpoint(tl, bl) (trbrX, trbrY) = midpoint(tr, br) # Tính độ dài 2 chiều dc_W = dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) dc_H = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))
Code language: PHP (php)

Sau bước này ta đã có được kích thước Vật tham chiếu (đồng xu) bằng Pixel, ta sẽ tình toán số $P$ (xem lại phần 1 nếu không nhớ $P$là gì)

$$P = Real size / Size in pixel$$

Vậy ta tính tiếp nào:

P = ref_width / dc_H

Và kích thước thật của các Vật cần đo (USB) là:

dr_W = dc_W * P dr_H = dc_H * P

Sau khi đã tính toán xong thì chúng ta chỉ cần vẽ lên ảnh cho đẹp, kẻ các khung contour là okie.

Full code của hàm này là:

def find_object_in_pix(orig, edge, area_threshold=3000): # Tìm các Contour trong ảnh cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # Sắp xếp các contour từ trái qua phải (cnts, _) = contours.sort_contours(cnts) P = None # Duyệt các contour for c in cnts: # Nếu contour quá nhỏ -> bỏ qua if cv2.contourArea(c) < area_threshold: continue # Tính toán 2 chiều bằng Pixel dc_W, dc_H, tltrX, tltrY, trbrX, trbrY = get_distance_in_pixels(orig, c) # Nếu là đồng xu if P is None: # Cập nhật số P P = ref_width / dc_H # Gán luôn kích thước thật bằng số đã biết dr_W = ref_width dr_H = ref_width else: # Nếu là các vật khác # Tính toán kích thước thật dựa vào kích thước pixel và số P dr_W = dc_W * P dr_H = dc_H * P # Ve kich thuoc len hinh cv2.putText(orig, "{:.1f} mm".format(dr_H), (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.putText(orig, "{:.1f} mm".format(dr_W), (int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) return orig
Code language: PHP (php)

Và bước cuối cùng là show ảnh đã vẽ vời các kiểu lên màn hình:

đo kích thước vật thể

Mình có chia sẻ github có code sẵn để các bạn tải về chạy thử nhé: tại đây . Chú ý cài thư viện bằng lệnh:

pip install -r setup.txt
Code language: CSS (css)

Done rồi,! Vậy là mình dã guide các bạn dùng OpenCV để đo kích thước các vật thể trong camera được rồi. Các bạn có thể phát triển, ứng dụng cho các bài toán riêng của các bạn nhé.

Chúc các bạn thành công!

Hãy join cùng cộng đồng Mì AI nhé!

Fanpage: http://facebook.com/miaiblog
Group trao đổi, chia sẻ: https://www.facebook.com/groups/miaigroup
Website: https://miai.vn
Youtube: http://bit.ly/miaiyoutube

Cảm ơn bài tham khảo cực hay của tác giả Adrian Rosebrock!

Related Post

6 Replies to “Chi tiết cách đo kích thước vật thể bằng OpenCV thuần”

      1. Em chào anh,
        Nếu vật cần đo kích thước đặt trên vật tham chiếu là tờ A4 thì xử lý như nào được ạ ?

Leave a Reply

Your email address will not be published.