5.25. Wyszukiwanie plam (blobs)

Progowanie zamieniło przechwyconą ramkę w binarną maskę: każdy piksel albo przechodzi test progu, albo nie. To odpowiada na pytanie które kolory istotne dla aplikacji pojawiają się w scenie, ale nie gdzie – maska to po prostu morze jedynek i zer. Następnym krokiem jest wykrywanie plam: przejście po masce, znalezienie spójnych obszarów pikseli przechodzących test i zwrócenie każdego z nich jako obiektu z pozycją, rozmiarem, orientacją oraz innymi właściwościami, na których aplikacja może działać.

find_blobs() to podstawowa metoda wykonująca ten krok i najczęstszy punkt wejścia do świata obiektów wynikowych modułu image. Śledzenie kolorowej piłki, podążanie za linią namalowaną na podłodze, zliczanie jasnych punktów widzianych przez sensor termiczny, decydowanie, czy niebieska dioda LED jest włączona czy wyłączona – to samo wywołanie obejmuje je wszystkie. Dane wejściowe się zmieniają (progi, przeszukiwany obszar, filtry stosowane do wyniku), ale wzorzec wywołania pozostaje taki sam.

5.25.1. Podstawowe wywołanie

find_blobs przyjmuje listę progów i zwraca listę obiektów wynikowych plam:

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))

Każda krotka progu ma tę samą postać co progi przekazywane do binary() – sześć wpisów (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) dla obrazu RGB565 (granice są w przestrzeni LAB), dwa wpisy (lo, hi) dla obrazu w skali szarości. W pojedynczym wywołaniu można podać do 32 progów, co czyni find_blobs() tak elastyczną: czerwone, zielone i niebieskie znaczniki mogą być śledzone jednocześnie, każdy wnosząc własne plamy do zwracanej listy, a właściwość code każdej plamy identyfikuje, który próg dopasowała.

Wywołania draw_rectangle() i draw_cross() powyżej oznaczają przechwyconą ramkę na potrzeby podglądu w IDE. Wynik plamy zawiera już b.rect (ramkę ograniczającą jako 4-elementową krotkę) oraz b.cx / b.cy (całkowitoliczbowy centroid), więc narysowanie wykrycia z powrotem na ramce to dwa wywołania metod.

5.25.2. Co zawiera wynik

Każda Blob to krotka atrybutów, która łączy w sobie wszystko, co detektor zmierzył o danym obszarze. Właściwości dzielą się na cztery grupy.

Grupa ramki ograniczającej i centroidux, y, w, h, rect, cx, cy, cxf, cyf – opisuje pozycję plamy. rect to 4-elementowa krotka (x, y, w, h), której oczekują metody rysujące; cx i cy to centroid w całkowitoliczbowych współrzędnych pikselowych; cxf i cyf to centroid w zmiennoprzecinkowych współrzędnych subpikselowych, przydatny, gdy wcześniejsza kalibracja uwzględnia pozycje ułamkowe.

Deskryptory kształtupixels, area, density, perimeter, roundness, elongation, compactness, rotation – opisują, jak wygląda plama. pixels to liczba pikseli przechodzących test; area to pole ramki ograniczającej wyrównanej do osi (w * h); density to stosunek obu wartości, który zbliża się do 1.0 dla pełnego prostokąta i spada w kierunku 0.0 dla cienkiej ukośnej kreski. roundness i compactness oceniają, jak okrągła jest plama, z różnych geometrycznych punktów widzenia (roundness na podstawie momentów drugiego rzędu, compactness na podstawie stosunku obwodu do pola); elongation to dla wygody 1.0 - roundness. rotation to orientacja osi głównej w radianach, która jest najdokładniejsza dla wydłużonych plam, a staje się zaszumiona dla plam niemal okrągłych (niejednoznaczna oś nie ma dobrze określonego kierunku).

Metadane progu i scalaniacode, count – identyfikują, który próg został dopasowany i ile plam źródłowych scalono w zwracaną. code to 32-bitowa mapa bitowa z jednym bitem ustawionym dla każdego dopasowanego progu (pojedynczy próg daje code == 1; scalona wielokolorowa plama może mieć ustawionych kilka bitów); count wynosi 1, chyba że merge=True połączyło kilka wykryć w jedno.

Grupa narożnikówcorners, min_corners – podaje obróconą geometrię plamy. corners to 4-elementowa krotka skrajnych punktów (x, y) wyciągniętych z konturu plamy, posortowanych zgodnie z ruchem wskazówek zegara od lewego górnego rogu; min_corners to 4-elementowa krotka narożników obróconego prostokąta o minimalnym polu, który obejmuje plamę. Prostokąt o minimalnym polu to ciasne dopasowanie; wyrównany do osi rect to luźne dopasowanie zgodne z siatką pikseli. Oba są przydatne w zależności od tego, czy dalszy etap potrzebuje zorientowanego prostokąta, czy zwykłego.

Wykrywanie plamy zilustrowane na tle binarnej maski progowej. Lewy panel przedstawia przechyloną owalną maskę pikseli przechodzących test. Prawy panel pokazuje tę samą maskę z oznaczoną wokół niej ramką ograniczającą wyrównaną do osi, centroidem zaznaczonym krzyżykiem pośrodku, przerywanym obróconym prostokątem o minimalnym polu przylegającym do owalu pod jego rzeczywistym kątem oraz linią osi głównej przechodzącą przez centroid wskazującą wzdłuż długiego kierunku owalu.

Plama niesie ze sobą ramkę ograniczającą wyrównaną do osi (rect, x, y, w, h), centroid (cx, cy lub subpikselowe cxf, cyf), obrócony prostokąt o minimalnym polu (min_corners wraz z rotation) oraz opcjonalne linie osi głównej / pobocznej obliczane przez poniższe funkcje pomocnicze na poziomie modułu.

5.25.4. Scalanie nakładających się plam

merge=True przetwarza listę wyników po fakcie, aby połączyć plamy, których prostokąty ograniczające się nakładają. Naturalnym zastosowaniem jest wykrywanie celu, którego kolor kamera widzi jako wiele progowanych obszarów z powodu odblasków lustrzanych, linii cienia lub niespójnego oświetlenia na obiekcie: pojedyncza czerwona piłka może wrócić jako trzy lub cztery małe czerwone plamy, które razem wzięte odwzorowują piłkę. Przy merge=True te trzy plamy stają się jedną dużą plamą, rect obejmuje sumę, code jest bitowym OR kodów scalonych plam (więc wielokolorowe scalenie identyfikuje, które kolory się przyczyniły), a count raportuje, ile plam źródłowych połączono.

margin powiększa lub pomniejsza prostokąty ograniczające przed testem nakładania. Przy margin=2 plamy, których prostokąty ograniczające znajdują się w odległości 2 pikseli od siebie, wciąż się scalają; przy margin=-2 scalają się tylko plamy, których prostokąty ograniczające nakładają się o co najmniej 2 piksele. Naturalne strojenie: dodatni margines do obsługi plam, które próg rozbił na sąsiadujące fragmenty; ujemny margines, aby utrzymać ściśle zgrupowane odrębne obiekty oddzielnie.

merge_cb działa na każdej kandydującej parze przed wykonaniem scalenia. Wywołanie zwrotne otrzymuje dwie plamy i zwraca True, aby zezwolić na scalenie, lub False, aby je uniemożliwić. To właściwe narzędzie do weryfikacji scaleń, które reguła geometryczna pomija – na przykład odmowa scalenia dwóch plam, których kąty rotation różnią się o więcej niż próg, lub odmowa scalenia małej plamy z dużo większą, jeśli ta mała to tylko szum.

5.25.5. Histogramy rzutowania

x_hist_bins_max i y_hist_bins_max dołączają opcjonalne histogramy rzutowania do każdej plamy. Histogram rzutowania to liczba pikseli przechodzących test wzdłuż jednej osi: histogram osi X sumuje piksele przechodzące test na kolumnę wewnątrz ramki ograniczającej plamy, a histogram osi Y sumuje je na wiersz. Oba domyślnie wynoszą zero – histogramy nie są obliczane, dopóki nie zostanie podana niezerowa wartość max, ponieważ w przeciwnym razie dodawałyby pracę do każdego wykrycia.

Gdy są obliczone, histogramy dostarczają taniego sygnału 1-D, na którym aplikacja może przeprowadzić dalszą analizę: wykrycie pozycji pionowego paska wewnątrz plamy, znalezienie punktu podziału dwukolorowego celu, zliczanie luk pojawiających się wzdłuż osi głównej. Są one wypełniane jako właściwości x_hist_bins i y_hist_bins na każdej Blob.

5.25.6. Dodatkowe funkcje pomocnicze geometrii

Garść kolejnych miar geometrycznych istnieje jako funkcje na poziomie modułu, które przyjmują plamę i zwracają żądany pomiar:

  • image.get_solidity() zwraca solidność plamy – liczbę pikseli podzieloną przez pole otoczki wypukłej. Pełny, wypełniony obszar jest bliski 1.0; plama z wklęsłościami (podkowa, dłoń z rozstawionymi palcami) spada znacznie poniżej.

  • image.get_convexity() zwraca wypukłość – obwód otoczki wypukłej podzielony przez obwód plamy. Idealnie wypukła plama ma 1.0; postrzępione lub powycinane plamy mają mniej.

  • image.get_major_axis_line() i image.get_minor_axis_line() zwracają obiekty Line wzdłuż osi głównej i pobocznej plamy, wyprowadzone z obróconego prostokąta o minimalnym polu.

  • image.get_enclosing_circle() zwraca Circle, który obejmuje plamę – przydatne, gdy dalszy etap chce mieć okrąg do narysowania lub do testowania.

  • image.get_enclosed_ellipse() zwraca 5-elementową krotkę (cx, cy, rx, ry, rotation) dla elipsy wpisanej w prostokąt plamy o minimalnym polu. Wartości trafiają bezpośrednio do draw_ellipse().

5.25.7. Automatyczne uczenie progu

Detektor plam jest tak dobry, jak progi, z którymi jest uruchamiany, a praca nad znalezieniem właściwego progu dla koloru celu jest osobnym problemem. Dwa powszechne wzorce zmniejszają tę pracę.

Pierwszy to interaktywny wybór w IDE: przechwyć ramkę, przeciągnij prostokąt wokół przykładu koloru celu i pozwól, aby edytor progów w IDE zaraportował granice LAB, które widzi. Te granice trafiają do skryptu jako progi find_blobs() i detektor jest gotowy.

Drugi to programowe automatyczne uczenie: procedura kalibracyjna działająca na kamerze przechwytuje ramkę, pobiera histogram znanego wycinka, gdzie znajduje się cel (get_histogram() z roi=), i odczytuje zakres wartości wycinka z histogramu za pomocą get_percentile(). 5. percentyl ustawia dolną granicę każdego kanału, a 95. jego górną granicę, ignorując odstające piksele na obu końcach. Na obrazie RGB565 jedno wywołanie percentyla raportuje wszystkie trzy kanały LAB naraz, więc dwa wywołania dają sześć liczb, których oczekuje find_blobs():

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)