2015-03-19 18 views
10

Jestem całkiem nowy w OpenCV i bardzo podekscytowany, aby dowiedzieć się więcej. Bawiłem się pomysłem konturowania krawędzi, kształtów.Wykrywanie krawędzi/granic OpenCV w oparciu o kolor

Doszedłem do tego kodu (działającego na urządzeniu iOS), który używa Canny. Chciałbym móc renderować to w kolorze i zakreślać każdy kształt. Czy ktoś może wskazać mi właściwy kierunek?

Dzięki!

IplImage *grayImage = cvCreateImage(cvGetSize(iplImage), IPL_DEPTH_8U, 1); 
cvCvtColor(iplImage, grayImage, CV_BGRA2GRAY); 
cvReleaseImage(&iplImage); 

IplImage* img_blur = cvCreateImage(cvGetSize(grayImage), grayImage->depth, 1); 
cvSmooth(grayImage, img_blur, CV_BLUR, 3, 0, 0, 0); 
cvReleaseImage(&grayImage); 

IplImage* img_canny = cvCreateImage(cvGetSize(img_blur), img_blur->depth, 1); 
cvCanny(img_blur, img_canny, 10, 100, 3); 
cvReleaseImage(&img_blur); 

cvNot(img_canny, img_canny); 

Przykładem mogą być te hamburgery. OpenCV wykryłoby patty i nakreśliło go. enter image description here

Obraz oryginału:

enter image description here

+2

Użyj 'findContours' aby określić kontury wszystkich kształtów: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours - Następnie użyj' drawContours', aby narysować każdy z elementów kontury w dowolnym kolorze, który chcesz: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#drawcontours. Nie mogę powiedzieć nic więcej, dopóki nie zobaczę, jak wyglądają obrazy, które próbujesz przetwarzać. – rayryeng

+0

@rayryeng dodał (a) obraz do odsyłacza. Dzięki za pomoc – Jeff

+2

Bez problemu! Czy możliwe byłoby ukazanie surowego obrazu bez czerwonego konturu? Kiedy już to dostanę, mam kilka pomysłów, które chciałbym wypróbować, a kiedy coś mam, opublikuję coś, co ci pomoże! – rayryeng

Odpowiedz

43

informacja Kolor jest często traktowane przez konwersję do HSV przestrzeni barw, które obsługuje „kolor” bezpośrednio zamiast dzielić kolor w/G składowych R/B, który sprawia, że ​​łatwiej obsługiwać te same kolory z różną jasnością itd

jeśli konwertować obraz do HSV dostaniesz to:

cv::Mat hsv; 
cv::cvtColor(input,hsv,CV_BGR2HSV); 

std::vector<cv::Mat> channels; 
cv::split(hsv, channels); 

cv::Mat H = channels[0]; 
cv::Mat S = channels[1]; 
cv::Mat V = channels[2]; 

Hue channel:

enter image description here

Nasycenie kanał: kanał

enter image description here

Wartość:

enter image description here

zwykle, kanał barwa jest pierwsza patrzeć jeśli masz int erested w segmentowaniu "koloru" (np. wszystkie czerwone obiekty). Jednym z problemów jest to, że odcień jest kolistą/kątową wartością, co oznacza, że ​​najwyższe wartości są bardzo podobne do najniższych wartości, co skutkuje jasnymi artefaktami na granicy pasztecików. Aby przezwyciężyć to dla określonej wartości, możesz przesunąć całą przestrzeń barw. Jeśli przesunięte o 50 ° dostaniesz coś takiego zamiast:

cv::Mat shiftedH = H.clone(); 
int shift = 25; // in openCV hue values go from 0 to 180 (so have to be doubled to get to 0 .. 360) because of byte range from 0 to 255 
for(int j=0; j<shiftedH.rows; ++j) 
    for(int i=0; i<shiftedH.cols; ++i) 
    { 
     shiftedH.at<unsigned char>(j,i) = (shiftedH.at<unsigned char>(j,i) + shift)%180; 
    } 

enter image description here

teraz można użyć prostego wykrywania obrotny krawędzi do krawędzi znaleźć w kanale Hue

cv::Mat cannyH; 
cv::Canny(shiftedH, cannyH, 100, 50); 

enter image description here

Widać, że regiony są nieco większe od prawdziwych pasztecików, które mogą być spowodowane drobnymi odbiciami i wokół patties, ale nie jestem tego pewien.Może to tylko ze względu na artefakty kompresji JPEG;)

Jeśli zamiast korzystać z kanału nasycenia wyodrębnić krawędzie, będziesz skończyć z czymś takim:

cv::Mat cannyS; 
cv::Canny(S, cannyS, 200, 100); 

enter image description here

gdzie konturów nie są całkowicie zamknięte. Być może możesz połączyć kolor i nasycenie w ramach przetwarzania wstępnego, aby wyodrębnić krawędzie w kanale odcienia, ale tylko tam, gdzie nasycenie jest wystarczająco wysokie.

Na tym etapie masz krawędzie. Chyba, że ​​krawędzie nie są jeszcze konturami. Jeśli bezpośrednio wyodrębnić kontury krawędzi nie może być zamknięty/oddzielony itp:

// extract contours of the canny image: 
std::vector<std::vector<cv::Point> > contoursH; 
std::vector<cv::Vec4i> hierarchyH; 
cv::findContours(cannyH,contoursH, hierarchyH, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE); 

// draw the contours to a copy of the input image: 
cv::Mat outputH = input.clone(); 
for(int i = 0; i< contoursH.size(); i++) 
{ 
    cv::drawContours(outputH, contoursH, i, cv::Scalar(0,0,255), 2, 8, hierarchyH, 0); 
} 

enter image description here

można usunąć te małe kontury sprawdzając cv::contourArea(contoursH[i]) > someThreshold przed wyciągnięciem. Ale widzisz dwa patties po lewej, które mają być połączone? Tutaj jest najtrudniejsza część ... użyj heurystyki, aby "poprawić" twój wynik.

cv::dilate(cannyH, cannyH, cv::Mat()); 
cv::dilate(cannyH, cannyH, cv::Mat()); 
cv::dilate(cannyH, cannyH, cv::Mat()); 

Dilation before contour extraction will "close" the gaps between different objects but increase the object size too. 

enter image description here

jeśli wyodrębnić kontury, że będzie to wyglądać tak:

enter image description here

Jeśli zamiast wybrać tylko „wewnętrzny” kontury to jest dokładnie to, co chcesz:

cv::Mat outputH = input.clone(); 
for(int i = 0; i< contoursH.size(); i++) 
{ 
    if(cv::contourArea(contoursH[i]) < 20) continue; // ignore contours that are too small to be a patty 
    if(hierarchyH[i][3] < 0) continue; // ignore "outer" contours 

    cv::drawContours(outputH, contoursH, i, cv::Scalar(0,0,255), 2, 8, hierarchyH, 0); 
} 

enter image description here

należy pamiętać, że dylatacja i wewnętrzne elementy konturu są trochę rozmyte, więc może nie działać dla różnych obrazów, a jeśli początkowe krawędzie są lepiej ułożone wokół obramowania obiektu, może 1. nie trzeba wykonywać rozszerzania i wewnętrzna rzecz konturu i 2. jeśli jest to nadal konieczne, rozszerzenie spowoduje zmniejszenie obiektu w tym scenariuszu (który na szczęście jest wielki dla danego obrazu próbki.).

EDYCJA: Kilka ważnych informacji o HSV: kanał odcienia nadaje każdemu pikselowi kolor widma, nawet jeśli nasycenie jest bardzo niskie (= szaro/białe) lub jeśli kolor jest bardzo niski (wartość) tak często pożądane jest progowanie kanałów nasycenia i wartości w celu znalezienia określonego koloru! To może być o wiele łatwiejsze i dużo łatwiejsze w obsłudze niż dylatacja, której użyłem w moim kodzie.

+3

Ta odpowiedź jest niesamowita.- W tej chwili mam tylko dziurkowanie, ale jak nakładam kontury na obraz kolorowy? – Jeff

+1

@ 1__ Zaktualizuję za 15 minut – Micka

+1

Dokładnie to, co zamierzałem zrobić. Nie dostałem czasu ostatniej nocy. Dobra robota! Przy okazji lol na twój komentarz C api. Przy okazji piękna odpowiedź. Przypomina mi mój styl! – rayryeng