Wprowadzenie
Progowanie to prosta i wydajna technika wykonywania podstawowej segmentacji obrazu i jego binaryzacji (przekształcenia go w obraz binarny), gdzie piksele są albo 0
or 1
(lub 255
jeśli używasz liczb całkowitych do ich reprezentowania).
Zazwyczaj możesz użyć progowania, aby wykonać prostą segmentację tła i pierwszego planu na obrazie, a sprowadza się to do wariantów prostej techniki dla każdego piksela:
if pixel_value > threshold:
pixel_value = MAX
else:
pixel_value = 0
Ten niezbędny proces jest znany jako Progowanie binarne. Teraz – są różne sposoby na poprawienie tego ogólnego pomysłu, w tym odwracanie operacji (przełączanie >
podpisać z <
znak), ustawienie pixel_value
do threshold
zamiast maksymalnej wartości/0 (tzw. obcinanie), zachowując pixel_value
się, jeśli jest powyżej threshold
lub jeśli jest poniżej threshold
.
Wszystko to zostało wygodnie zaimplementowane w OpenCV jako:
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TRUNC
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
… odpowiednio. Są to stosunkowo „naiwne” metody, ponieważ są dość proste, nie uwzględniają kontekstu w obrazach, mają wiedzę o tym, jakie kształty są powszechne itp. W przypadku tych właściwości musielibyśmy zastosować znacznie droższe obliczeniowo i potężne techniki.
Teraz, nawet przy „naiwnych” metodach – kilka można zastosować heurystyki w celu znalezienia dobrych progów, a należą do nich metoda Otsu i metoda Trójkątów:
cv2.THRESH_OTSU
cv2.THRESH_TRIANGLE
Uwaga: Progowanie OpenCV to podstawowa technika, która jest wrażliwa na zmiany oświetlenia i gradienty, niejednorodność kolorów itp. Najlepiej stosować ją na stosunkowo czystych obrazach, po ich rozmyciu w celu zmniejszenia szumów, bez dużej zmienności kolorów w obiektach, które chcesz segmentować.
Innym sposobem przezwyciężenia niektórych problemów z podstawowym progowaniem za pomocą jednej wartości progowej jest użycie progowanie adaptacyjne która stosuje wartość progową do każdego małego regionu obrazu, a nie globalnie.
Proste progowanie z OpenCV
Progowanie w API Pythona OpenCV odbywa się za pomocą cv2.threshold()
metoda – która przyjmuje obraz (tablica NumPy, reprezentowana przez liczby całkowite), próg, wartość maksymalną oraz metodę progowania (jak threshold
i maximum_value
są używane):
img = cv2.imread('objects.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blurred = cv2.GaussianBlur(img, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 220, 255, cv2.THRESH_BINARY)
Kod powrotu to tylko zastosowany próg:
print(f"Threshold: {ret}")
Tutaj, ponieważ próg jest 220
i użyliśmy THRESH_BINARY
metoda – każda wartość piksela powyżej 220
zostanie zwiększona do 255
, podczas gdy każda wartość piksela poniżej 220
zostanie obniżony do 0
, tworząc obraz czarno-biały, z „maską”, zakrywającą obiekty pierwszego planu.
Dlaczego 220? Wiedząc, jak wygląda obraz, możesz zgadywać w przybliżeniu, jaki próg możesz wybrać. W praktyce rzadko będziesz chciał ustawić próg ręczny, a automatyczny wybór progu zajmiemy się za chwilę.
Wykreślmy wynik! Okna OpenCV mogą być nieco skomplikowane, więc narysujemy oryginalny obraz, rozmazany obraz i wyniki za pomocą Matplotlib:
fig, ax = plt.subplots(1, 3, figsize=(12, 8))
ax[0].imshow(img)
ax[1].imshow(blurred)
ax[2].imshow(img_masked)
Metody progowe
Jak wspomniano wcześniej, istnieje wiele sposobów wykorzystania progu i maksymalnej wartości w funkcji. Na początku przyjrzeliśmy się progowi binarnemu. Stwórzmy listę metod i zastosujmy je jedna po drugiej, wykreślając wyniki:
methods = [cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV, cv2.THRESH_TRUNC, cv2.THRESH_TOZERO, cv2.THRESH_TOZERO_INV]
names = ['Binary Threshold', 'Inverse Binary Threshold', 'Truncated Threshold', 'To-Zero Threshold', 'Inverse To-Zero Threshold']
def thresh(img_path, method, index):
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blurred = cv2.GaussianBlur(img, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 220, 255, method)
fig, ax = plt.subplots(1, 3, figsize=(12, 4))
fig.suptitle(names[index], fontsize=18)
ax[0].imshow(img)
ax[1].imshow(blurred)
ax[2].imshow(img_masked)
plt.tight_layout()
for index, method in enumerate(methods):
thresh('coins.jpeg', method, index)
THRESH_BINARY
i THRESH_BINARY_INV
są odwrotne do siebie i zbinaryzują obraz pomiędzy 0
i 255
, przypisując je odpowiednio do tła i pierwszego planu i odwrotnie.
THRESH_TRUNC
binaryzuje obraz pomiędzy threshold
i 255
.
THRESH_TOZERO
i THRESH_TOZERO_INV
Binaryzować pomiędzy 0
i aktualna wartość piksela (src(x, y)
). Rzućmy okiem na powstałe obrazy:
Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!
Te metody są wystarczająco intuicyjne – ale jak możemy zautomatyzować dobrą wartość progową i co w ogóle oznacza „dobry próg”? Większość dotychczasowych wyników miała nieidealne maski ze znakami i plamkami. Dzieje się tak z powodu różnicy w odbijających powierzchniach monet – nie są one jednolicie zabarwione ze względu na różnicę w odbijaniu światła przez prążki.
Do pewnego stopnia możemy z tym walczyć, znajdując lepszy globalny próg.
Automatyczne/zoptymalizowane progowanie z OpenCV
OpenCV wykorzystuje dwie efektywne metody wyszukiwania progów globalnych – metodę Otsu i metodę Triangle.
Metoda Otsu zakłada, że to działa bimodalny obrazy. Obrazy bimodalne to obrazy, których histogramy kolorów zawierają tylko dwa szczyty (tj. mają tylko dwie różne wartości pikseli). Biorąc pod uwagę, że każdy ze szczytów należy do klasy takiej jak „tło” i „pierwszy plan” – idealny próg znajduje się dokładnie pośrodku nich.
Kredyt obrazu: https://scipy-lectures.org/
Niektóre obrazy mogą być bardziej bimodalne z rozmyciem gaussowskim, ale nie wszystkie.
Alternatywnym, często lepszym algorytmem jest algorytm trójkąta, który oblicza odległość między maksimum i minimum histogramu poziomu szarości i rysuje linię. Punkt, w którym linia ta znajduje się maksymalnie daleko od reszty histogramu, wybierany jest jako próg:
Obydwa zakładają obraz w skali szarości, więc musimy przekonwertować obraz wejściowy na szary za pomocą cv2.cvtColor()
:
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
ret, mask1 = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)
ret, mask2 = cv2.threshold(blurred, 0, 255, cv2.THRESH_TRIANGLE)
masked = cv2.bitwise_and(img, img, mask=mask1)
Przeprowadźmy obraz obiema metodami i zwizualizujmy wyniki:
methods = [cv2.THRESH_OTSU, cv2.THRESH_TRIANGLE]
names = ['Otsu Method', 'Triangle Method']
def thresh(img_path, method, index):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
ret, img_masked = cv2.threshold(blurred, 0, 255, method)
print(f"Threshold: {ret}")
fig, ax = plt.subplots(1, 3, figsize=(12, 5))
fig.suptitle(names[index], fontsize=18)
ax[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
ax[1].imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB))
ax[2].imshow(cv2.cvtColor(img_masked, cv2.COLOR_BGR2RGB))
for index, method in enumerate(methods):
thresh('coins.jpeg', method, index)
Tutaj metoda trójkąta przewyższa metodę Otsu, ponieważ obraz nie jest bimodalny:
import numpy as np
img = cv2.imread('coins.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
histogram_gray, bin_edges_gray = np.histogram(gray, bins=256, range=(0, 255))
histogram_blurred, bin_edges_blurred = np.histogram(blurred, bins=256, range=(0, 255))
fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax[0].plot(bin_edges_gray[0:-1], histogram_gray)
ax[1].plot(bin_edges_blurred[0:-1], histogram_blurred)
Jednak jest jasne, w jaki sposób metoda trójkąta była w stanie pracować z obrazem i uzyskać bardziej satysfakcjonujący wynik.
Ograniczenia progów OpenCV
Progowanie z OpenCV jest proste, łatwe i wydajne. Jednak jest to dość ograniczone. Gdy tylko wprowadzisz kolorowe elementy, niejednolite tło i zmieniające się warunki oświetleniowe – globalne progowanie jako koncepcja staje się zbyt sztywne.
Obrazy są zwykle zbyt złożone, aby wystarczyło pojedynczy próg, co można częściowo rozwiązać poprzez: progowanie adaptacyjne, gdzie stosuje się wiele lokalnych progów zamiast jednego globalnego. Choć również ograniczone, progowanie adaptacyjne jest znacznie bardziej elastyczne niż progowanie globalne.
Wnioski
W ostatnich latach segmentacja binarna (tak jak to zrobiliśmy tutaj) i segmentacja wieloetykietowa (gdzie można zakodować dowolną liczbę klas) z powodzeniem modelowano za pomocą sieci głębokiego uczenia, które są znacznie bardziej wydajne i elastyczne. Ponadto mogą kodować kontekst globalny i lokalny w obrazach, które segmentują. Minusem jest to, że do ich szkolenia potrzebne są dane, a także czas i doświadczenie.
Do prostego wyznaczania progów w locie można użyć OpenCV. Do dokładnej segmentacji na poziomie produkcyjnym warto użyć sieci neuronowych.
Idąc dalej – praktyczne uczenie głębokie dla wizji komputerowej
Twoja dociekliwość sprawia, że chcesz iść dalej? Zalecamy sprawdzenie naszego Kurs: „Praktyczne uczenie głębokie dla widzenia komputerowego z Pythonem”.
Kolejny kurs na temat wizji komputerowej?
Nie będziemy robić klasyfikacji cyfr MNIST ani mody MNIST. Swoją rolę pełnili dawno temu. Zbyt wiele zasobów edukacyjnych koncentruje się na podstawowych zestawach danych i podstawowych architekturach, zanim zaawansowane architektury czarnoskrzynkowe poniosą ciężar wydajności.
Chcemy się skupić demistyfikacja, praktyczność, zrozumienie, intuicja i realne projekty. Chcieć się nauczyć w jaki sposób możesz zrobić różnicę? Zabierzemy Cię na przejażdżkę od sposobu, w jaki nasze mózgi przetwarzają obrazy, przez pisanie klasyfikatora głębokiego uczenia się na poziomie badań naukowych w zakresie raka piersi, do sieci głębokiego uczenia się, które „halucynują”, ucząc Cię zasad i teorii poprzez praktyczną pracę, wyposażając Cię w know-how i narzędzia, aby stać się ekspertem w stosowaniu uczenia głębokiego do rozwiązywania wizji komputerowych.
Co jest w środku?
- Pierwsze zasady widzenia i jak nauczyć komputery „widzieć”
- Różne zadania i zastosowania wizji komputerowej
- Narzędzia handlu, które ułatwią Ci pracę
- Znajdowanie, tworzenie i wykorzystywanie zbiorów danych do wizji komputerowej
- Teoria i zastosowanie splotowych sieci neuronowych
- Obsługa zmiany domeny, współwystępowania i innych błędów w zbiorach danych
- Transfer uczenia się i wykorzystanie czasu szkolenia innych i zasobów obliczeniowych dla Twojej korzyści
- Budowanie i szkolenie najnowocześniejszego klasyfikatora raka piersi
- Jak zastosować zdrową dawkę sceptycyzmu do głównych idei i zrozumieć konsekwencje powszechnie stosowanych technik?
- Wizualizacja „przestrzeni koncepcyjnej” ConvNet za pomocą t-SNE i PCA
- Studia przypadków, w jaki sposób firmy wykorzystują techniki widzenia komputerowego do osiągania lepszych wyników
- Właściwa ocena modelu, wizualizacja przestrzeni utajonej i identyfikacja uwagi modelu
- Wykonywanie badań domenowych, przetwarzanie własnych zbiorów danych i tworzenie testów modelowych
- Najnowocześniejsze architektury, progresja pomysłów, co czyni je wyjątkowymi i jak je wdrażać
- KerasCV – biblioteka WIP do tworzenia najnowocześniejszych rurociągów i modeli
- Jak analizować i czytać artykuły i samodzielnie je wdrażać
- Wybór modeli w zależności od aplikacji
- Tworzenie kompleksowego procesu uczenia maszynowego
- Krajobraz i intuicja w wykrywaniu obiektów za pomocą szybszych sieci R-CNN, sieci RetinaNet, dysków SSD i YOLO
- Segmentacja instancyjna i semantyczna
- Rozpoznawanie obiektów w czasie rzeczywistym za pomocą YOLOv5
- Szkolenie wykrywaczy obiektów YOLOv5
- Praca z transformatorami przy użyciu KerasNLP (biblioteka przemysłowa WIP)
- Integracja Transformers z ConvNets w celu generowania podpisów obrazów
- DeepDream
- Optymalizacja modelu Deep Learning dla wizji komputerowej