Dzisiaj dowiemy się, jak wykrywać linie i okręgi na obrazie za pomocą techniki zwanej transformatą Hougha.
Co to jest przestrzeń Hougha?
Zanim zaczniemy stosować transformatę Hougha do obrazów, musimy zrozumieć, czym jest przestrzeń Hougha, a dowiemy się tego na przykładzie.
Przestrzeń parametryczna
Przy pracy z obrazami możemy sobie wyobrazić, że obraz jest macierzą 2d nad pewnymi współrzędnymi x i y, pod którymi linia mogłaby być opisana jako y = mx + b
Ale w przestrzeni parametrycznej, którą nazwiemy przestrzenią Hougha, mogę przedstawić tę samą linię jako m
vs b
, więc charakterystyka linii w przestrzeni obrazu będzie pojedynczym punktem w pozycji m-b
w przestrzeni Hougha.
Mamy jednak problem, z y = mx + b
, nie możemy reprezentować pionowej linii, ponieważ jej nachylenie jest nieskończone. Więc potrzebujemy lepszego sposobu parametryzacji, współrzędne biegunowe (rho i theta).
Przestrzeń Hougha
- rho: opisuje odległość linii od początku
- theta: opisuje kąt oddalenia od poziomu
Jedną z bardzo ważnych obserwacji jest to, co dzieje się, gdy weźmiemy wiele punktów wokół linii i przekształcimy w naszą przestrzeń Hougha.
Pojedyncza kropka w przestrzeni obrazu przekłada się na krzywą w przestrzeni Hougha, z tą różnicą, że punkty na linii w przestrzeni obrazu będą reprezentowane przez wiele krzywych z jednym punktem styczności.
I to będzie nasz cel, znalezienie punktów, w których grupa krzywych przecina się.
Co to jest transformata Hougha?
Transformata Hougha jest metodą ekstrakcji cech do wykrywania prostych kształtów, takich jak koła, linie, itp. w obrazie.
„Prosta” cecha jest uzyskiwana przez reprezentację kształtu w kategoriach parametrów. Prosty” kształt będzie reprezentowany tylko przez kilka parametrów, na przykład linia może być reprezentowana przez jej nachylenie i punkt przecięcia lub okrąg, który może być reprezentowany przez x, y i promień.
W naszym przykładzie linii, transformata Hough będzie odpowiedzialna za przetwarzanie kropek na obrazie i obliczanie wartości w przestrzeni Hough.
Algorytm dokonywania transformacji, a następnie znajdowania przecinających się krzywych jest nieco skomplikowany, a zatem wykracza poza zakres tego postu. Przyjrzymy się jednak implementacji tego algorytmu, która jest częścią biblioteki OpenCV.
Wykrywanie linii za pomocą OpenCV
W OpenCV wykrywanie linii za pomocą transformaty Hougha jest zaimplementowane w funkcjach HoughLines
i HoughLinesP
(Probabilistic Hough Transform). My skupimy się na tej drugiej.
Funkcja ta oczekuje następujących parametrów:
-
image
: 8-bitowy, jednokanałowy binarny obraz źródłowy. Obraz ten może być modyfikowany przez funkcję. -
lines
: Wyjściowy wektor linii. Każda linia jest reprezentowana przez 4-elementowy wektor (x_1, y_1, x_2, y_2) , gdzie (x_1,y_1) i (x_2, y_2) są punktami końcowymi każdego wykrytego segmentu linii. -
rho
: Rozdzielczość odległościowa akumulatora w pikselach. -
theta
: Rozdzielczość kątowa akumulatora w radianach. -
threshold
: Parametr progowy akumulatora. Zwracane są tylko te linie, które uzyskają wystarczającą liczbę głosów -
minLineLength
: Minimalna długość linii. Odcinki linii krótsze od niej są odrzucane. -
maxLineGap
: Maksymalna dozwolona przerwa między punktami na tej samej linii, aby je połączyć.
Zbyt skomplikowane? łatwiej będzie na przykładzie:
# Read image img = cv2.imread('lanes.jpg', cv2.IMREAD_COLOR)# Convert the image to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the edges in the image using canny detectoredges = cv2.Canny(gray, 50, 200)# Detect points that form a linelines = cv2.HoughLinesP(edges, 1, np.pi/180, max_slider, minLineLength=10, maxLineGap=250)# Draw lines on the imagefor line in lines: x1, y1, x2, y2 = line cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 3)# Show resultcv2.imshow("Result Image", img)
A oto wynik:
Bardzo ważne jest, abyśmy faktycznie użyli obrazu z tylko krawędziami jako parametru dla transformaty Hougha, w przeciwnym razie algorytm nie będzie działał zgodnie z założeniami.
Wykrywanie okręgów za pomocą OpenCV
Proces przebiega mniej więcej tak samo jak w przypadku linii, z tą różnicą, że tym razem użyjemy innej funkcji z biblioteki OpenCV. Użyjemy teraz funkcji HoughCircles
, która przyjmuje następujące parametry:
-
image
: 8-bitowy, jednokanałowy obraz wejściowy w skali szarości. -
circles
: Wektor wyjściowy znalezionych okręgów. Każdy wektor jest zakodowany jako 3-elementowy wektor zmiennoprzecinkowy (x, y, promień). -
circle_storage
: W funkcji C jest to pamięć, która będzie zawierać wyjściową sekwencję znalezionych okręgów. -
method
: Metoda detekcji, której należy użyć. Obecnie jedyną zaimplementowaną metodą jest CV_HOUGH_GRADIENT , która jest w zasadzie 21HT -
dp
: Odwrotny stosunek rozdzielczości akumulatora do rozdzielczości obrazu. Na przykład, jeśli dp=1 , to akumulator ma taką samą rozdzielczość jak obraz wejściowy. Jeśli dp=2 , to akumulator ma o połowę mniejszą szerokość i wysokość. -
minDist
: Minimalna odległość między środkami wykrytych okręgów. Jeśli parametr jest zbyt mały, oprócz jednego prawdziwego okręgu może zostać fałszywie wykrytych wiele sąsiednich. Jeśli jest zbyt duży, niektóre okręgi mogą zostać pominięte. -
param1
: Pierwszy parametr specyficzny dla metody. W przypadku CV_HOUGH_GRADIENT jest to wyższy próg z dwóch przekazywanych do detektora krawędzi Canny() (niższy jest dwa razy mniejszy). -
param2
: Drugi parametr specyficzny dla metody. W przypadku CV_HOUGH_GRADIENT , jest to próg akumulacji dla środków okręgów na etapie detekcji. Im jest on mniejszy, tym więcej fałszywych okręgów może zostać wykrytych. Kręgi odpowiadające większym wartościom akumulatora będą zwracane w pierwszej kolejności. -
minRadius
: Minimalny promień okręgu. -
maxRadius
: Maksymalny promień okręgu.
Pamiętaj, że parametry muszą być inne, ponieważ nie możemy opisać okręgu za pomocą tej samej parametryzacji, której użyliśmy dla linii, a zamiast tego musimy użyć równania takiego jak (x - x0)^^2 + (y - y0)^^2 = r^^2
.
I do kodu:
# Read image as gray-scaleimg = cv2.imread('circles.png', cv2.IMREAD_COLOR)# Convert to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Blur the image to reduce noiseimg_blur = cv2.medianBlur(gray, 5)# Apply hough transform on the imagecircles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, img.shape/64, param1=200, param2=10, minRadius=5, maxRadius=30)# Draw detected circlesif circles is not None: circles = np.uint16(np.around(circles)) for i in circles: # Draw outer circle cv2.circle(img, (i, i), i, (0, 255, 0), 2) # Draw inner circle cv2.circle(img, (i, i), 2, (0, 0, 255), 3)
Zauważ, że w porównaniu z poprzednim przykładem, nie stosujemy tutaj żadnej funkcji wykrywania krawędzi. Dzieje się tak dlatego, że funkcja HoughCircles
ma wbudowaną funkcję wykrywania Canny.
I wynik:
Wniosek
Transformacja Hougha jest doskonałą techniką wykrywania prostych kształtów na obrazach i ma wiele zastosowań, począwszy od zastosowań medycznych, takich jak analiza rentgenowska, tomograficzna i rezonansu magnetycznego, a skończywszy na samochodach samojezdnych. Jeśli chcesz dowiedzieć się więcej o przestrzeni Hougha, zalecam, abyś faktycznie uruchomił kod, wypróbował różne konfiguracje samodzielnie i sprawdził dokumentację OpenCV, aby uzyskać dodatkowe informacje.
Dzięki za przeczytanie!