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 centroidu – x, 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łtu – pixels, 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 scalania – code, 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ów – corners, 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.
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.3. Filtrowanie wyszukiwania¶
Przechwycona ramka zazwyczaj zawiera piksele, które pasują do progu z powodów innych niż obiekt istotny dla aplikacji: odblaski lustrzane, odległe obiekty tła, zaszumione piksele obrazu, które przypadkiem mieszczą się w zakresie LAB. Argumenty słowne kluczowe metody find_blobs() stanowią pierwszą linię obrony.
roi ogranicza wyszukiwanie do obszaru ramki, tak jak każda inna metoda modułu image. Aplikacja, która wie, że obiekt może pojawić się jedynie w dolnej połowie pola widzenia, przekazuje roi=(0, h//2, w, h//2) i ignoruje wszystko powyżej; zaoszczędzony czas wraca do szybkości klatek.
area_threshold i pixels_threshold oba filtrują plamy zbyt małe, by się nimi przejmować. area_threshold odrzuca plamy, których ramka ograniczająca ma pole mniejsze niż podana liczba pikseli (dobre do filtrowania rozproszonego szumu); pixels_threshold odrzuca plamy mające mniej niż podaną liczbę pikseli przechodzących test (dobre do filtrowania plam, które są duże, ale rzadkie, jak progowany wzór kropkowany z jednym czy dwoma pasującymi pikselami tu i tam). Obie wartości domyślne to 10; podkręcenie ich do setek dla celu na pierwszym planie o szerokości kilku centymetrów odrzuca każdą drobinkę małego szumu.
x_stride i y_stride ustawiają krok pikselowy, jaki skaner wykonuje podczas poszukiwania plamy do rozpoczęcia śledzenia. Krok to nie rozdzielczość śledzenia – śledzenie zawsze podąża za rzeczywistą granicą plamy ze szczegółowością pojedynczego piksela – lecz steruje on tym, jak szybko skan znajduje piksel początkowy. Gdy wiadomo, że plamy są duże (kolorowy cel wielkości pięści w odległości kilkudziesięciu centymetrów od kamery, z łatwością szeroki na sto pikseli), x_stride=4, y_stride=4 skraca czas skanowania szesnastokrotnie bez praktycznej straty w wykrywaniu. Gdy plamy są małe (odległy znacznik LED o szerokości kilku pikseli), kroki muszą pozostać na 1, aby ich całkowicie nie przeskoczyć. invert odwraca test progu: dopasowanie staje się niedopasowaniem, a procedura zwraca plamy pikseli nieprzechodzących testu.
threshold_cb to pythonowe wywołanie zwrotne wywoływane na każdej plamie po progowaniu, ale przed zbudowaniem ostatecznej listy wyników. Wywołanie zwrotne otrzymuje plamę i zwraca True, aby ją zachować, lub False, aby ją odrzucić. To miejsce na zastosowanie dowolnych filtrów na poziomie Pythona dla właściwości, których argumenty słowne kluczowe nie udostępniają bezpośrednio – minimalna gęstość, określony zakres rotacji, własna kombinacja bitów kodu po scaleniu. Argumenty słowne kluczowe to filtry w kodzie natywnym i działają szybko; wywołanie zwrotne działa w Pythonie i jest wolniejsze, ale nieograniczone w tym, co może wyrazić.
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 bliski1.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 ma1.0; postrzępione lub powycinane plamy mają mniej.image.get_major_axis_line()iimage.get_minor_axis_line()zwracają obiektyLinewzdłuż osi głównej i pobocznej plamy, wyprowadzone z obróconego prostokąta o minimalnym polu.image.get_enclosing_circle()zwracaCircle, 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 dodraw_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)