5.25. Tìm kiếm các vùng màu (blob)¶
Ngưỡng hóa đã chuyển đổi khung hình đã chụp thành mặt nạ nhị phân: mỗi điểm ảnh hoặc vượt qua bài kiểm tra ngưỡng hoặc không. Điều này trả lời màu sắc nào mà ứng dụng quan tâm xuất hiện trong cảnh, nhưng không trả lời ở đâu -- mặt nạ chỉ là một tập hợp các giá trị 1 và 0. Bước tiếp theo là phát hiện vùng màu (blob): duyệt qua mặt nạ, tìm các vùng liên kết của các điểm ảnh vượt ngưỡng, và trả về từng vùng dưới dạng một đối tượng có vị trí, kích thước, hướng, và các thuộc tính khác mà ứng dụng có thể sử dụng.
find_blobs() là phương thức chính cho bước đó, và là điểm vào phổ biến nhất vào thế giới đối tượng kết quả của mô-đun image. Theo dõi một quả bóng có màu, đi theo một đường kẻ trên sàn, đếm xem một cảm biến nhiệt nhìn thấy bao nhiêu điểm sáng, quyết định xem đèn LED màu xanh có bật hay không -- cùng một lệnh gọi bao gồm tất cả các trường hợp đó. Các đầu vào thay đổi (các ngưỡng, vùng tìm kiếm, các bộ lọc áp dụng cho kết quả), nhưng mẫu lệnh gọi là như nhau.
5.25.1. Lệnh gọi cơ bản¶
find_blobs nhận một danh sách các ngưỡng và trả về một danh sách các đối tượng kết quả blob:
thresholds = [(30, 100, 15, 127, 15, 127)] # LAB threshold for red
blobs = img.find_blobs(thresholds)
for b in blobs:
img.draw_rectangle(b.rect, color=(255, 0, 0))
img.draw_cross(b.cx, b.cy, color=(255, 0, 0))
Mỗi bộ ngưỡng có cùng dạng với các ngưỡng được truyền vào binary() -- sáu giá trị (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) cho ảnh RGB565 (các giới hạn theo không gian màu LAB), hai giá trị (lo, hi) cho ảnh thang xám. Có thể cung cấp tối đa 32 ngưỡng trong một lần gọi, đó là điều làm cho find_blobs() trở nên rất linh hoạt: các beacon màu đỏ, xanh lá và xanh dương có thể được theo dõi đồng thời, mỗi loại đóng góp các blob của riêng nó vào danh sách trả về, và thuộc tính code của mỗi blob xác định ngưỡng nào đã khớp.
Các lệnh gọi draw_rectangle() và draw_cross() ở trên chú thích khung hình đã chụp cho bản xem trước trong IDE. Kết quả blob đã mang b.rect (hộp giới hạn dưới dạng bộ 4 phần tử) và b.cx / b.cy (tâm điểm nguyên), vì vậy việc vẽ kết quả phát hiện lại vào khung hình chỉ cần hai lệnh gọi phương thức.
5.25.2. Nội dung kết quả¶
Mỗi Blob là một bộ thuộc tính đóng gói tất cả những gì bộ phát hiện đo được về vùng đó. Các thuộc tính được chia thành bốn nhóm.
Nhóm hộp giới hạn và tâm điểm -- x, y, w, h, rect, cx, cy, cxf, cyf -- mô tả vị trí của blob. rect là bộ 4 phần tử (x, y, w, h) mà các phương thức vẽ yêu cầu; cx và cy là tâm điểm theo tọa độ điểm ảnh nguyên; cxf và cyf là tâm điểm theo tọa độ float dưới điểm ảnh, hữu ích khi một giai đoạn hiệu chuẩn ở trên quan tâm đến các vị trí phân số.
Các bộ mô tả hình dạng -- pixels, area, density, perimeter, roundness, elongation, compactness, rotation -- mô tả hình dạng của blob. pixels là số lượng điểm ảnh vượt ngưỡng; area là diện tích của hộp giới hạn theo trục (w * h); density là tỷ lệ giữa hai đại lượng này, tiệm cận 1.0 đối với hình chữ nhật đặc và giảm về 0.0 đối với nét xiên mỏng. roundness và compactness đều đánh giá mức độ tròn của blob, từ các góc nhìn hình học khác nhau (roundness từ các mô-men bậc hai, compactness từ tỷ lệ chu vi trên diện tích); elongation là 1.0 - roundness để tiện dụng. rotation là hướng của trục chính tính bằng radian, chính xác nhất với các blob dài và trở nên nhiễu với các blob gần tròn (một trục không xác định không có hướng xác định).
Siêu dữ liệu ngưỡng và gộp -- code, count -- xác định ngưỡng nào đã khớp và bao nhiêu blob nguồn đã được gộp vào kết quả trả về. code là một bitmap 32 bit với một bit được đặt cho mỗi ngưỡng khớp (một ngưỡng duy nhất cho code == 1; một blob đa màu được gộp có thể có nhiều bit được đặt); count là 1 trừ khi merge=True đã kết hợp nhiều lần phát hiện thành một.
Nhóm góc -- corners, min_corners -- cung cấp hình học xoay của blob. corners là bộ 4 phần tử của các điểm cực (x, y) lấy từ đường viền của blob, được sắp xếp theo chiều kim đồng hồ từ trên-trái; min_corners là bộ 4 phần tử góc của hình chữ nhật xoay có diện tích nhỏ nhất bao quanh blob. Hình chữ nhật có diện tích nhỏ nhất là khớp chặt; rect theo trục là khớp lỏng theo lưới điểm ảnh. Cả hai đều hữu ích tùy thuộc vào việc một giai đoạn tiếp theo cần hộp có hướng hay hộp thông thường.
Một blob mang hộp giới hạn theo trục (rect, x, y, w, h), tâm điểm (cx, cy hoặc dưới điểm ảnh cxf, cyf), hình chữ nhật xoay có diện tích nhỏ nhất (min_corners cộng với rotation), và các đường trục chính/phụ tùy chọn được tính bởi các hàm trợ giúp cấp mô-đun bên dưới.¶
5.25.3. Lọc tìm kiếm¶
Một khung hình đã chụp thường chứa các điểm ảnh khớp với ngưỡng vì những lý do khác ngoài đối tượng mà ứng dụng quan tâm: điểm sáng gương, đối tượng nền xa, các điểm ảnh nhiễu ảnh tình cờ nằm trong dải LAB. Các đối số từ khóa của find_blobs() là tuyến phòng thủ đầu tiên.
roi giới hạn tìm kiếm trong một vùng của khung hình, giống như mọi phương thức mô-đun image khác. Một ứng dụng biết rằng đối tượng chỉ có thể xuất hiện ở nửa dưới của trường nhìn truyền roi=(0, h//2, w, h//2) và bỏ qua mọi thứ ở phía trên; thời gian tiết kiệm được chuyển lại thành tốc độ khung hình.
area_threshold và pixels_threshold đều lọc bỏ các blob quá nhỏ để quan tâm. area_threshold loại bỏ các blob có hộp giới hạn có ít hơn số điểm ảnh đó về diện tích (tốt để lọc nhiễu rải rác); pixels_threshold loại bỏ các blob có ít hơn số điểm ảnh vượt ngưỡng đó (tốt để lọc các blob lớn nhưng thưa thớt, như mẫu chấm đã được ngưỡng hóa với một hoặc hai điểm ảnh khớp đây đó). Cả hai mặc định đều là 10; tăng chúng lên hàng trăm đối với mục tiêu nền trước vài centimet sẽ loại bỏ mọi đốm nhiễu nhỏ.
x_stride và y_stride đặt bước điểm ảnh mà máy quét thực hiện khi tìm kiếm blob để bắt đầu vạch. Stride không phải là độ phân giải vạch -- vạch luôn đi theo ranh giới blob thực tế ở mức chi tiết từng điểm ảnh -- nhưng nó kiểm soát tốc độ quét tìm thấy một điểm ảnh bắt đầu. Khi các blob được biết là lớn (mục tiêu màu có kích thước nắm tay cách camera một tầm, dễ dàng hơn một trăm điểm ảnh), x_stride=4, y_stride=4 cắt giảm thời gian quét mười sáu lần mà không mất gì đáng kể trong phát hiện. Khi các blob nhỏ (beacon LED xa, vài điểm ảnh), các stride phải giữ ở 1 để tránh bỏ qua chúng hoàn toàn. invert đảo ngược bài kiểm tra ngưỡng: khớp trở thành không khớp và quy trình trả về các blob của các điểm ảnh không vượt qua.
threshold_cb là một hàm gọi lại Python được gọi trên mỗi blob sau khi ngưỡng hóa nhưng trước khi danh sách kết quả cuối cùng được tạo. Hàm gọi lại nhận blob và trả về True để giữ lại hoặc False để loại bỏ. Đây là nơi để áp dụng các bộ lọc Python tùy ý trên các thuộc tính mà các đối số từ khóa không tiết lộ trực tiếp -- mật độ tối thiểu, phạm vi góc xoay cụ thể, tổ hợp bit code tùy chỉnh sau khi gộp. Các đối số từ khóa là bộ lọc trong mã gốc và chạy nhanh; hàm gọi lại chạy trong Python và chậm hơn nhưng không giới hạn trong những gì nó có thể biểu diễn.
5.25.4. Gộp các blob chồng lên nhau¶
merge=True xử lý hậu kỳ danh sách kết quả để kết hợp các blob có hình chữ nhật giới hạn chồng lên nhau. Cách dùng tự nhiên là phát hiện một mục tiêu mà màu sắc của nó camera nhìn thấy thành nhiều vùng đã được ngưỡng hóa vì điểm sáng gương, đường bóng tối, hoặc ánh sáng không đồng đều trên đối tượng: một quả bóng đỏ duy nhất có thể trả về ba hoặc bốn blob đỏ nhỏ mà khi ghép lại sẽ vạch ra quả bóng. Với merge=True, ba blob trở thành một blob lớn, rect bao phủ phần hợp, code là OR bitwise của các code của blob đã gộp (vì vậy một lần gộp đa màu xác định màu nào đã đóng góp), và count báo cáo bao nhiêu blob nguồn đã được kết hợp.
margin tăng hoặc thu nhỏ các hình chữ nhật giới hạn trước khi kiểm tra chồng lấp. Với margin=2, các blob có hình chữ nhật giới hạn đến trong vòng 2 điểm ảnh của nhau vẫn gộp lại; với margin=-2, chỉ các blob có hình chữ nhật giới hạn chồng lên nhau ít nhất 2 điểm ảnh mới gộp. Cách điều chỉnh tự nhiên: margin dương để xử lý các blob mà ngưỡng đã chia thành các mảnh liền kề; margin âm để giữ các đối tượng riêng biệt được nhóm chặt chẽ tách biệt.
merge_cb chạy trên mỗi cặp ứng viên trước khi việc gộp xảy ra. Hàm gọi lại nhận hai blob và trả về True để cho phép gộp hoặc False để ngăn chặn. Đây là công cụ phù hợp để kiểm tra chéo các lần gộp mà quy tắc hình học bỏ lỡ -- ví dụ: từ chối gộp hai blob có góc rotation khác nhau hơn một ngưỡng, hoặc từ chối gộp một blob nhỏ vào một blob lớn hơn nhiều nếu blob nhỏ chỉ là nhiễu.
5.25.5. Biểu đồ chiếu¶
x_hist_bins_max và y_hist_bins_max gắn thêm biểu đồ chiếu tùy chọn vào mỗi blob. Biểu đồ chiếu là số lượng điểm ảnh vượt ngưỡng theo một trục: biểu đồ trục X tổng hợp các điểm ảnh vượt ngưỡng theo cột bên trong hộp giới hạn của blob, và biểu đồ trục Y tổng hợp theo hàng. Cả hai mặc định đều là không -- các biểu đồ không được tính trừ khi cung cấp max khác không, vì nếu không chúng sẽ thêm công việc vào mỗi lần phát hiện.
Khi được tính, các biểu đồ cung cấp một tín hiệu 1 chiều rẻ mà ứng dụng có thể chạy thêm phân tích: phát hiện vị trí của một sọc dọc bên trong blob, tìm điểm gãy của một mục tiêu hai màu, đếm bao nhiêu khoảng trống xuất hiện dọc theo trục dài. Chúng được điền vào các thuộc tính x_hist_bins và y_hist_bins trên mỗi Blob.
5.25.6. Các hàm trợ giúp hình học bổ sung¶
Một số thước đo hình học bổ sung được đặt là các hàm cấp mô-đun nhận một blob và trả về phép đo được yêu cầu:
image.get_solidity()trả về độ đặc của blob -- số điểm ảnh chia cho diện tích của bao lồi. Một vùng đặc được lấp đầy gần với1.0; một blob có lõm (hình móng ngựa, bàn tay xòe ngón) giảm xuống đáng kể.image.get_convexity()trả về độ lồi -- chu vi bao lồi chia cho chu vi của blob. Một blob hoàn toàn lồi là1.0; các blob có răng cưa hoặc khía sẽ thấp hơn.image.get_major_axis_line()vàimage.get_minor_axis_line()trả về các đối tượngLinedọc theo các trục chính và phụ của blob, được suy ra từ hình chữ nhật xoay có diện tích nhỏ nhất.image.get_enclosing_circle()trả về mộtCirclebao quanh blob -- hữu ích khi một giai đoạn tiếp theo muốn một hình tròn để vẽ hoặc kiểm tra.image.get_enclosed_ellipse()trả về bộ 5 phần tử(cx, cy, rx, ry, rotation)cho một hình elip nội tiếp trong hình chữ nhật có diện tích nhỏ nhất của blob. Các giá trị đưa trực tiếp vàodraw_ellipse().
5.25.7. Tự động học ngưỡng¶
Bộ phát hiện blob chỉ tốt bằng các ngưỡng nó được chạy với, và công việc tìm ngưỡng phù hợp cho một màu mục tiêu là vấn đề riêng của nó. Hai mẫu phổ biến giảm thiểu công việc đó.
Mẫu đầu tiên là lựa chọn tương tác trong IDE: chụp một khung hình, kéo một hình chữ nhật xung quanh một ví dụ về màu mục tiêu, và để trình chỉnh sửa ngưỡng của IDE báo cáo các giới hạn LAB mà nó thấy. Các giới hạn đó được thêm vào tập lệnh như các ngưỡng của find_blobs() và bộ phát hiện đã sẵn sàng.
Mẫu thứ hai là tự động học theo chương trình: một quy trình hiệu chuẩn chạy trên camera chụp một khung hình, lấy biểu đồ tần suất của một vùng đã biết nơi mục tiêu tọa lạc (get_histogram() với roi=), và đọc phạm vi giá trị của vùng từ biểu đồ tần suất với get_percentile(). Phân vị thứ 5 đặt giới hạn thấp của mỗi kênh và phân vị thứ 95 đặt giới hạn cao, bỏ qua các điểm ảnh ngoại lệ ở cả hai đầu. Trên ảnh RGB565, một lần gọi phân vị báo cáo cả ba kênh LAB cùng một lúc, vì vậy hai lần gọi tạo ra sáu số mà find_blobs() mong đợi:
h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
lo.a_value, hi.a_value,
lo.b_value, hi.b_value)