pisanie solidnego (niezmiennego koloru i rozmiaru) wykrywania kół za pomocą opencv (na podstawie transformacji Hougha lub innych funkcji)

Napisałem następujący bardzo prosty kod Pythona, aby znaleźć okręgi na obrazku:

import cv
import numpy as np

WAITKEY_DELAY_MS = 10
STOP_KEY = 'q'

cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE);
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE);

key_pressed = False
while key_pressed != STOP_KEY:

    # grab image
    orig = cv.LoadImage('circles3.jpg')

    # create tmp images
    grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
    processed = cv.CreateImage(cv.GetSize(orig), 8, 1)


    cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

    cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

    # do some processing on the grey scale image
    cv.Erode(grey_scale, processed, None, 10)
    cv.Dilate(processed, processed, None, 10)
    cv.Canny(processed, processed, 5, 70, 3)
    cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

    storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

    # these parameters need to be adjusted for every single image
    HIGH = 50
    LOW = 140

    try: 
        # extract circles
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW)

        for i in range(0, len(np.asarray(storage))):
            print "circle #%d" %i
            Radius = int(np.asarray(storage)[i][0][2])
            x = int(np.asarray(storage)[i][0][0])
            y = int(np.asarray(storage)[i][0][1])
            center = (x, y)

            # green dot on center and red circle around
            cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

            cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

    except:
        print "nothing found"
        pass

    # show images
    cv.ShowImage("image - press 'q' to quit", orig)
    cv.ShowImage("post-process", processed)

    cv_key = cv.WaitKey(WAITKEY_DELAY_MS)
    key_pressed = chr(cv_key & 255)

Jak widać na poniższych dwóch przykładach, "jakość znajdowania kół" różni się dość dużo:

CASE1:

input1detekcja1post-processed1

CASE2:

input2detekcja2post-processed2

Case1 i Case2 są zasadniczo tym samym obrazem, ale algorytm wykrywa różne okręgi. Jeśli przedstawię algorytm obraz z różnej wielkości kół, wykrywanie kół może nawet całkowicie zawieść. Wynika to głównie z parametrów HIGH i LOW, które należy dostosować indywidualnie dla każdego nowego obrazu.

Dlatego moje pytanie: jakie są różne możliwości uczynienia tego algorytmu bardziej wytrzymałym? Powinien być niezmienny pod względem wielkości i koloru, aby wykryto różne okręgi o różnych kolorach i w różnych rozmiarach. Może użycie transformacji Hougha nie jest najlepszym sposobem na robienie rzeczy? Czy są lepsze podejść?

Author: memyself, 2012-03-25

6 answers

Poniżej przedstawiono moje doświadczenie jako badacza wizji. Z twojego pytania wynika, że interesują Cię możliwe algorytmy i metody, a nie tylko roboczy fragment kodu. Najpierw daję szybki i brudny skrypt Pythona dla przykładowych obrazów, a niektóre wyniki są pokazane, aby udowodnić, że może to rozwiązać twój problem. Po ich usunięciu staram się odpowiedzieć na twoje pytania dotyczące solidnych algorytmów wykrywania.

Szybkie Wyniki

Niektóre przykładowe zdjęcia (wszystkie zdjęcia poza twoim pobierane są z flickr.com i są licencjonowane CC) z wykrytymi kręgami (bez zmiany/strojenia żadnych parametrów, dokładnie poniższy kod jest używany do wyodrębniania okręgów we wszystkich obrazach): wykryte plamki na obrazie próbki 1wykryte plamki na obrazie próbki 2wiele kręgówplamy na obrazku flickr 1

Kod (oparty na detektorze Blob MSER)

A oto kod:

import cv2
import math
import numpy as np

d_red = cv2.cv.RGB(150, 55, 65)
l_red = cv2.cv.RGB(250, 200, 200)

orig = cv2.imread("c.jpg")
img = orig.copy()
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

detector = cv2.FeatureDetector_create('MSER')
fs = detector.detect(img2)
fs.sort(key = lambda x: -x.size)

def supress(x):
        for f in fs:
                distx = f.pt[0] - x.pt[0]
                disty = f.pt[1] - x.pt[1]
                dist = math.sqrt(distx*distx + disty*disty)
                if (f.size > x.size) and (dist<f.size/2):
                        return True

sfs = [x for x in fs if not supress(x)]

for f in sfs:
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA)
        cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA)

h, w = orig.shape[:2]
vis = np.zeros((h, w*2+5), np.uint8)
vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
vis[:h, :w] = orig
vis[:h, w+5:w*2+5] = img

cv2.imshow("image", vis)
cv2.imwrite("c_o.jpg", vis)
cv2.waitKey()
cv2.destroyAllWindows()

Jak widać jest oparty na detektorze blob MSER . Kod nie przetwarza wstępnie obrazu poza prostym mapowaniem do skali szarości. W związku z tym oczekuje się, że brakuje tych słabych żółtych plam na zdjęciach.

Teoria

krótko mówiąc: nie mówisz nam, co wiesz o problemie, oprócz podania tylko dwóch przykładowych zdjęć bez ich opisu. Tutaj wyjaśniam, dlaczego moim skromnym zdaniem ważne jest, aby mieć więcej informacji na temat problemu przed zapytaniem, jakie są skuteczne metody ataku na problem.

Powrót do głównego pytania: Jaka jest najlepsza metoda na ten problem? Spójrzmy na to jest problem wyszukiwania. Aby uprościć dyskusję Załóżmy, że szukamy okręgów o danym rozmiarze/promieniu. Tak więc problem sprowadza się do znalezienia ośrodków. Każdy piksel jest centrum kandydata, dlatego przestrzeń wyszukiwania zawiera wszystkie piksele.

P = {p1, ..., pn} 
P: search space
p1...pn: pixels

Aby rozwiązać ten problem należy zdefiniować dwie inne Funkcje:

E(P) : enumerates the search space
V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list

Zakładając, że złożoność algorytmu nie ma znaczenia, można użyć wyszukiwania wyczerpującego lub brute-force, w którym E bierze każdy piksel i V. w aplikacjach czasu rzeczywistego ważne jest, aby zmniejszyć przestrzeń wyszukiwania i zoptymalizować wydajność obliczeniową V.

Zbliżamy się do głównego pytania. Jak można zdefiniować V, a dokładniej jakie właściwości kandydatów powinny być miarami i jak rozwiązać problem dychotomii polegający na podziale ich na pożądane i niepożądane. Najczęstszym podejściem jest znalezienie pewnych właściwości, które można wykorzystać do zdefiniowania prostych zasad decyzyjnych opartych na pomiar właściwości. To jest to, co robisz metodą prób i błędów. Programujesz klasyfikator ucząc się na pozytywnych i negatywnych przykładach. Dzieje się tak, ponieważ metody, których używasz, nie mają pojęcia, co chcesz zrobić. Musisz dostosować / dostroić parametry reguły decyzji i/lub wstępnie przetworzyć dane w taki sposób, aby zmniejszono zmienność właściwości (pożądanych kandydatów) stosowanych przez metodę problemu dychotomii. Możesz użyć algorytmu uczenia maszynowego, aby znajdź optymalne wartości parametrów dla danego zestawu przykładów. Istnieje cała gama algorytmów uczenia się od drzew decyzyjnych do programowania genetycznego, których możesz użyć do tego problemu. Możesz również użyć algorytmu uczenia się, aby znaleźć optymalne wartości parametrów dla kilku algorytmów wykrywania kół i sprawdzić, który z nich daje lepszą dokładność. To zajmuje główne obciążenie algorytmu uczenia się, którego potrzebujesz, aby zebrać przykładowe obrazy.

Inne podejście do poprawy odporności, które jest często pomijane jest wykorzystanie dodatkowych łatwo dostępnych informacji. Jeśli znasz kolor okręgów praktycznie bez dodatkowego wysiłku, możesz znacznie poprawić dokładność detektora. Jeśli znałeś położenie okręgów na płaszczyźnie i chciałeś wykryć odwzorowane okręgi, powinieneś pamiętać, że transformacja między tymi dwoma zestawami pozycji jest opisana homografią 2D. A homografię można oszacować używając tylko czterech punktów. Następnie można poprawić solidność, aby mieć solidną metodę. Wartość wiedzy specyficznej dla danej dziedziny jest często niedoceniana. Spójrz na to w ten sposób, w pierwszym podejściu staramy się zbliżyć niektóre zasady decyzyjne na podstawie ograniczonej liczby próbek. W drugim podejściu znamy reguły decyzyjne i musimy tylko znaleźć sposób na efektywne wykorzystanie ich w algorytmie.

Podsumowanie

Podsumowując, istnieją dwa podejścia do poprawy dokładności / solidności rozwiązanie:

  1. W 2008 roku firma Microsoft ogłosiła, że w 2009 roku firma Microsoft wprowadziła do swojej oferty nowy algorytm, który został stworzony z myślą o klientach, którzy chcą korzystać z algorytmów uczenia maszynowego.]}
  2. oparte na informacjach : czy wykorzystujesz wszystkie łatwo dostępne informacje? W pytaniu nie wspominasz, co wiesz o problemie.

Dla tych dwóch obrazów, które udostępniłeś, użyłbym detektora blob, A Nie metody HT. Dla tła odejmowanie proponuję spróbować oszacować kolor tła, ponieważ na dwóch obrazach nie zmienia się, podczas gdy kolor okręgów się zmienia. A większość obszaru jest naga.

 33
Author: fireant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-17 18:15:14

To jest wielki problem modelowania. Mam następujące zalecenia/ pomysły:

  1. Podziel obraz na RGB, a następnie przetworz.
  2. obróbka wstępna.
  3. dynamiczne wyszukiwanie parametrów.
  4. Dodaj ograniczenia.
  5. upewnij się, co próbujesz wykryć.

Bardziej szczegółowo:

1: jak zauważono w innych odpowiedziach, konwersja na skalę szarości odrzuca zbyt wiele informacji - wszelkie okręgi o podobnej jasności do tła będzie stracony. Znacznie lepiej rozważyć kanały kolorów w izolacji lub w innej przestrzeni kolorów. Można to zrobić na dwa sposoby: wykonać HoughCircles na każdym wstępnie przetworzonym kanale w izolacji, następnie połączyć wyniki lub przetworzyć kanały, następnie połączyć je, a następnie uruchomić HoughCircles. W mojej próbie poniżej wypróbowałem drugą metodę, dzielenie na kanały RGB, przetwarzanie, a następnie łączenie. Uważaj na nadmierne nasycenie obrazu podczas łączenia, używam cv.And, aby uniknąć tego problemu (w tym stage my circles to zawsze czarne pierścienie/krążki na białym tle).

2: obróbka wstępna jest dość trudna i często najlepiej się nią bawić. Skorzystałem z AdaptiveThreshold, która jest naprawdę potężną metodą splotu, która może poprawić krawędzie obrazu poprzez threst pikseli w oparciu o ich lokalną średnią (podobne procesy występują również we wczesnej ścieżce systemu wizualnego ssaków). Jest to również przydatne, ponieważ zmniejsza hałas. Użyłem dilate/erode z tylko jednym przejściem. A ja zachowałeś inne parametry. Wydaje się, że użycie Canny przed HoughCircles bardzo pomaga w znalezieniu "wypełnionych okręgów" , więc prawdopodobnie najlepiej trzymać je w środku. To wstępne przetwarzanie jest dość ciężkie i może prowadzić do fałszywych alarmów z nieco bardziej "blobby circles", ale w naszym przypadku jest to być może pożądane?

3: jak zauważyłeś parametr HoughCircles param2 (Twój parametr LOW) musi być dopasowany do każdego obrazu, aby uzyskać optymalne rozwiązanie, w rzeczywistości z docs :

Im jest mniejszy, tym więcej fałszywych okręgów można wykryć.

Trouble is the sweet spot is gonna be different for every image. Myślę, że najlepszym podejściem jest ustawienie warunku i przeszukanie różnych wartości param2, aż warunek ten zostanie spełniony. Twoje obrazy pokazują Nie nakładające się okręgi, a gdy param2 jest zbyt niska, zazwyczaj otrzymujemy mnóstwo nakładających się okręgów. Proponuję więc poszukać:

Maksimum liczba nie nakładających się i nie zawartych okręgów

Tak więc nazywamy HoughCircles o różnych wartościach param2 aż to zostanie spełnione. Robię to w moim przykładzie poniżej, po prostu zwiększając param2 aż osiągnie założenie progu. Byłoby to o wiele szybsze (i dość łatwe do zrobienia), jeśli wykonasz wyszukiwanie binarne, aby znaleźć, kiedy to zostanie spełnione, ale musisz być ostrożny z obsługą wyjątków, ponieważ opencv często rzuca błąd dla niewinnie wyglądających wartości param2 (przynajmniej na moim instalacji). Innym warunkiem, który byłby bardzo przydatne, aby dopasować się do byłaby liczba kręgów.

4: czy są jeszcze jakieś ograniczenia, które możemy dodać do modelu? Im więcej rzeczy możemy powiedzieć naszemu modelowi, tym łatwe zadanie możemy wykonać, aby wykryć okręgi. Na przykład, czy wiemy:

  • liczba okręgów. - nawet górna lub dolna granica jest pomocna.
  • możliwe Kolory okręgów, tła lub "nie okręgów".
  • ich rozmiary.
  • gdzie mogą być na obrazie.

5: niektóre plamy na twoich zdjęciach mogą być tylko luźno nazywane kręgami! Rozważ dwa "niekolistne plamy" na drugim obrazku, mój kod nie może ich znaleźć (dobrze!), ale... jeśli "photoshop" je tak, że są bardziej okrągłe, mój kod może je znaleźć... Może jeśli chcesz wykryć rzeczy, które nie są kręgami, inne podejście, takie jak {15]} może być lepsze.

Problemy

Robiąc ciężkie wstępne przetwarzanie AdaptiveThresholding i "Canny" może powodować duże zniekształcenia dla funkcji obrazu, co może prowadzić do fałszywego wykrywania okręgu lub nieprawidłowego raportowania promienia. Na przykład duży twardy dysk po przetworzeniu może pojawić się pierścień, więc HughesCircles może znaleźć pierścień wewnętrzny. Ponadto nawet docs zauważają, że:

...Zwykle funkcja dobrze wykrywa centra okręgów, jednak może nie znaleźć poprawnych promieni.

Jeśli potrzebujesz dokładniejszych promieni wykrywanie, proponuję następujące podejście (nie zaimplementowane):

    Na oryginalnym obrazie, promień-ślad od zgłoszonego środka okręgu, w rozszerzającym się krzyżu (4 promienie: góra/dół/lewo/prawo) {21]} Wykonaj to osobno w każdym kanale RGB]}
  • połącz te informacje dla każdego kanału dla każdego promienia w rozsądny sposób(tj. w razie potrzeby Odwróć, przesuń, skaluj itp.)
  • weź średnią dla pierwszych kilku pikseli na każdym promieniu, użyj tego, aby wykryć, gdzie znaczące odchylenie na występuje ray.
  • Te 4 punkty są szacunkami punktów na obwodzie.
  • użyj tych czterech szacunków, aby określić dokładniejszy Promień i położenie środka (!).
  • Można to uogólnić za pomocą rozszerzającego się pierścienia zamiast czterech promieni.

Wyniki

Kod na końcu robi całkiem dobre dość dużo czasu, te przykłady zostały wykonane z kodu, jak pokazano:

Wykrywa wszystkie okręgi w pierwszym image: Tutaj wpisz opis obrazka

Jak wygląda wstępnie przetworzony obraz przed nałożeniem filtra canny (bardzo widoczne są różne kolorowe okręgi):Tutaj wpisz opis obrazka

Wykrywa wszystkie oprócz dwóch (plamy) na drugim obrazku: Tutaj wpisz opis obrazka

Zmieniony drugi obraz (plamy są koliste, a duży owal stał się bardziej okrągły, co poprawia wykrywanie), wszystkie wykryte: Tutaj wpisz opis obrazka

Całkiem dobrze sprawdza się w wykrywaniu ośrodków w tym obrazie Kandinsky 'ego (nie mogę znaleźć koncentrycznych pierścieni ze względu na stan graniczny). Tutaj wpisz opis obrazka

Kod:

import cv
import numpy as np

output = cv.LoadImage('case1.jpg')
orig = cv.LoadImage('case1.jpg')

# create tmp images
rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

def channel_processing(channel):
    pass
    cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
    #mop up the dirt
    cv.Dilate(channel, channel, None, 1)
    cv.Erode(channel, channel, None, 1)

def inter_centre_distance(x1,y1,x2,y2):
    return ((x1-x2)**2 + (y1-y2)**2)**0.5

def colliding_circles(circles):
    for index1, circle1 in enumerate(circles):
        for circle2 in circles[index1+1:]:
            x1, y1, Radius1 = circle1[0]
            x2, y2, Radius2 = circle2[0]
            #collision or containment:
            if inter_centre_distance(x1,y1,x2,y2) < Radius1 + Radius2:
                return True

def find_circles(processed, storage, LOW):
    try:
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
    except:
        LOW += 1
        print 'try'
        find_circles(processed, storage, LOW)
    circles = np.asarray(storage)
    print 'number of circles:', len(circles)
    if colliding_circles(circles):
        LOW += 1
        storage = find_circles(processed, storage, LOW)
    print 'c', LOW
    return storage

def draw_circles(storage, output):
    circles = np.asarray(storage)
    print len(circles), 'circles found'
    for circle in circles:
        Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
        cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
        cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

#split image into RGB components
cv.Split(orig,rrr,ggg,bbb,None)
#process each component
channel_processing(rrr)
channel_processing(ggg)
channel_processing(bbb)
#combine images using logical 'And' to avoid saturation
cv.And(rrr, ggg, rrr)
cv.And(rrr, bbb, processed)
cv.ShowImage('before canny', processed)
# cv.SaveImage('case3_processed.jpg',processed)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.ShowImage('processed', processed)
#find circles, with parameter search
storage = find_circles(processed, storage, 100)
draw_circles(storage, output)
# show images
cv.ShowImage("original with circles", output)
cv.SaveImage('case1.jpg',output)

cv.WaitKey(0)
 28
Author: fraxel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 17:12:41

Ah, tak... stare niezmienniki koloru/rozmiaru dla problemu kółek (aka transformacja Hougha jest zbyt specyficzna i mało wytrzymała)...

W przeszłości opierałem się bardziej na analizie strukturalnej i kształtowej funkcji OpenCV. Z folderu "samples" można uzyskać bardzo dobry obraz tego, co jest możliwe - szczególnie fitellipse.py i squares.py.

Dla wyjaśnienia, przedstawiam hybrydową wersję tych przykładów i na podstawie oryginalnego źródła. Wykryte kontury są w zielone i elipsy dopasowane w kolorze czerwonym.

Tutaj wpisz opis obrazka

Jeszcze nie do końca:

  • etapy wstępnego przetwarzania wymagają odrobiny poprawek, aby wykryć słabsze okręgi.
  • możesz przetestować kontur dalej, aby określić, czy jest to okrąg, czy nie...
Powodzenia!
import cv
import numpy as np

# grab image
orig = cv.LoadImage('circles3.jpg')

# create tmp images
grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)

cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

# do some processing on the grey scale image
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

#storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
storage = cv.CreateMemStorage(0)

contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL)
# N.B. 'processed' image is modified by this!

#contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) 
# If you wanted to reduce the number of points...

cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0)) 

def contour_iterator(contour):
  while contour:
    yield contour
    contour = contour.h_next()

for c in contour_iterator(contours):
  # Number of points must be more than or equal to 6 for cv.FitEllipse2
  if len(c) >= 6:
    # Copy the contour into an array of (x,y)s
    PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)

    for (i, (x, y)) in enumerate(c):
      PointArray2D32f[0, i] = (x, y)

    # Fits ellipse to current contour.
    (center, size, angle) = cv.FitEllipse2(PointArray2D32f)

    # Convert ellipse data from float to integer representation.
    center = (cv.Round(center[0]), cv.Round(center[1]))
    size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))

    # Draw ellipse
    cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0)

# show images
cv.ShowImage("image - press 'q' to quit", orig)
#cv.ShowImage("post-process", processed)
cv.WaitKey(-1)

EDIT:

Po prostu aktualizacja, aby powiedzieć, że uważam, że głównym tematem wszystkich tych odpowiedzi jest to, że istnieje wiele dalszych założeń i ograniczeń, które może być stosowany do tego, co chcesz uznać za circular . Moja własna odpowiedź nie robi na to pretensji - ani w niskopoziomowym wstępnym przetwarzaniu, ani w wysokopoziomowym geometrycznym dopasowaniu. Fakt, że wiele okręgów nie jest tak naprawdę okrągłych ze względu na sposób ich rysowania lub nie-afiniczne / projekcyjne transformacje obrazu, a także inne właściwości w ich renderowaniu/przechwytywaniu ( kolor, szum, oświetlenie, grubość krawędzi) - wszystko to powoduje dowolną liczbę możliwych okręgów kandydujących w obrębie jednego obrazu.

Istnieją znacznie bardziej wyrafinowane techniki. Ale będą Cię kosztować. Osobiście podoba mi się pomysł @fraxel na użycie progu addaptive. Jest szybki, niezawodny i w miarę wytrzymały. Następnie można dalej testować końcowe kontury (np. użyć momentów Hu) lub kształtki za pomocą prostego testu proporcji osi elipsy-np. if ((min(size)/max(size))>0.7).

Jak zawsze w wizji komputerowej istnieje napięcie między pragmatyzmem, zasadą i parsomonią. As I am fond mówienie ludziom, którzy uważają, że CV jest łatwe, nie jest - w rzeczywistości jest to problem AI kompletny . Najlepsze, na co często możesz liczyć poza tym, to coś, co działa przez większość czasu.

 11
Author: tiluki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-17 10:26:24

Przeglądając Twój kod, zauważyłem, co następuje:

  • Konwersja w skali szarości. Rozumiem, dlaczego to robisz, ale zdaj sobie sprawę, że rzucasz tam są informacje. Jak widać na obrazach "post-proces", Twoje żółte kółka są ta sama intensywność co tło, tylko w innym kolorze.

  • Wykrywanie krawędzi po usunięciu szumów (erae/dilate). To nie powinno być konieczne; Canny powinien się tym zająć.

  • Sprytne wykrywanie krawędzi. Twoje" otwarte " okręgi mają dwie krawędzie, wewnętrzną i zewnętrzną krawędź. Ponieważ są dość blisko, sprytny filtr Gaussa może dodać je razem. Jeśli nie, będziesz miał dwa krawędzie blisko siebie. Czyli przed Canny, masz otwarte i wypełnione koła. Następnie masz odpowiednio 0/2 i 1 krawędź. Ponieważ Hough ponownie wywołuje Canny, w pierwszym przypadku dwie krawędzie mogą być wygładzone razem( w zależności od początkowej szerokości), dlatego algorytm Hough może traktować otwarte i wypełnione okręgi to samo.

Więc, moja pierwsza rekomendacja to zmiana mapowania skali szarości. Nie używaj intensywności, ale używaj barwy/nasycenia / wartości. Ponadto, użyj podejścia różnicowego - szukasz krawędzi. Więc Oblicz transformację HSV, wygładź kopię, a następnie weź różnicę między oryginalną i wygładzoną kopią. Dzięki temu uzyskasz dH, dS, dV wartości (lokalne wahania barwy, nasycenia, wartości) dla każdego punktu. Kwadrat i dodać, aby uzyskać jednowymiarowy obraz, ze szczytami przy wszystkich krawędziach (wewnętrzna i zewnętrzne).

Moja druga rekomendacja to lokalna normalizacja, ale nie jestem pewien, czy to w ogóle konieczne. Chodzi o to, że nie dbasz szczególnie o dokładną wartość sygnału krawędzi, który otrzymałeś, to naprawdę powinno być binarne i tak(krawędź lub nie). W związku z tym można normalizować każdą wartość, dzieląc przez średnią lokalną (gdzie lokalna jest rzędu wielkości rozmiaru krawędzi).
 8
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-26 13:37:47

Transformata Hougha używa "modelu", aby znaleźć pewne cechy NA (zazwyczaj) obrazie wykrywanym krawędziami, jak zapewne wiesz. W przypadku HoughCircles model ten jest okręgiem idealnym. Oznacza to, że prawdopodobnie nie istnieje kombinacja parametrów, które sprawią, że wykryje bardziej nieregularne i eliptyczne okręgi na zdjęciu bez zwiększania liczby fałszywych alarmów. Z drugiej strony, ze względu na podstawowy mechanizm głosowania, niezamknięty krąg doskonały lub krąg doskonały z "dent" może się konsekwentnie pojawiać. Więc w zależności od oczekiwanego wyniku Możesz lub nie chcesz użyć tej metody.

To powiedziawszy, jest kilka rzeczy, które widzę, które mogą pomóc ci w drodze z tą funkcją: {16]}

  1. HoughCircles dzwoni wewnętrznie, więc chyba możesz to zostawić.
  2. param1 (które wywołujesz HIGH) jest zazwyczaj inicjalizowane wokół wartości 200. Jest używany jako parametr do wewnętrznego wywołania do Canny: cv.Canny(processed, cannied, HIGH, HIGH/2). To może pomóc uruchom Canny sam w ten sposób, aby zobaczyć, jak ustawienie HIGH wpływa na obraz obrabiany przez transformację Hougha.
  3. param2 (które wywołujesz LOW) jest zazwyczaj inicjowane wokół wartości 100. Jest to próg wyborczy dla akumulatorów transformatora Hougha. Ustawienie go wyżej oznacza więcej fałszywych negatywów, niższe więcej fałszywych pozytywów. Myślę, że to pierwszy raz, z którym chcesz się bawić.

Ref: http://docs.opencv.org/3.0-beta/modules/imgproc/doc/feature_detection.html#houghcircles

Aktualizacja re: wypełnione okręgi: Po znalezieniu kształtów okręgów z transformatą Hougha możesz sprawdzić, czy są wypełnione przez pobranie koloru granicy i porównanie go do jednego lub więcej punktów wewnątrz przypuszczalnego okręgu. Alternatywnie można porównać jeden lub więcej punktów wewnątrz rzekomego okręgu do danego koloru tła. Okrąg jest wypełniony, jeśli poprzednie porównanie sukces, lub w przypadku alternatywnego porównania, jeśli się nie powiedzie.

 6
Author: Eric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-09 09:22:51

Ok patrząc na zdjęcia. Proponuję użyć **Active Contours**

  • Aktywne Kontury Zaletą aktywnych konturów jest to, że prawie idealnie pasują do każdego kształtu. Czy to kwadraty lub trójkąta i w Twoim przypadku są idealnymi kandydatami.
  • Jeśli jesteś w stanie wyodrębnić środek okręgów, to świetnie. Aktywne kontury zawsze potrzebują punktu, od którego mogą rosnąć lub kurczyć się, aby dopasować. Nie jest konieczne, aby centra były zawsze wyrównane do centrum. Trochę przesunięcia nadal będzie ok.
  • i w Twoim przypadku, jeśli pozwolisz konturom rosnąć od środka na zewnątrz, powinny one spoczywać na granicach okręgu.
  • Należy pamiętać, że aktywne kontury, które rosną lub kurczą się, wykorzystują energię balonu , co oznacza, że można ustawić kierunek konturów, do wewnątrz lub na zewnątrz.
  • prawdopodobnie będziesz musiał użyć gradientowego obrazu w skali szarości. Ale nadal możesz spróbować również w Kolorze. Jeśli to zadziała!
  • i jeśli nie dostarczysz centra, wrzucają dużo aktywnych konturów, sprawiają, że rosną/kurczą się. Kontury, które się osadzają, są przechowywane, niespokojne są wyrzucane. To podejście brutalnej siły. Będzie intensywny procesor. Ale będzie wymagał bardziej starannej pracy, aby upewnić się, że zostawisz prawidłowe kontury i wyrzucisz te złe.
Mam nadzieję, że w ten sposób rozwiążesz problem.
 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-11 13:35:13