Wykrywanie krawędzi / krawędzi OpenCV na podstawie koloru

Jestem całkiem nowy w OpenCV i bardzo podekscytowany, aby dowiedzieć się więcej. Bawiłam się pomysłem zarysowania krawędzi, kształtów.

Natknąłem się na ten kod (działający na urządzeniu z systemem iOS), który używa Canny. Chciałbym móc renderować to w kolorze i zakreślić 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);
A przykładem mogą być te burgery. OpenCV wykryłby patty i zarysował go.Tutaj wpisz opis obrazka

Oryginał Image:

Tutaj wpisz opis obrazka

Author: Paul Sanders, 2015-03-19

1 answers

Informacje o kolorach są często obsługiwane przez konwersję do przestrzeni kolorów HSV, która obsługuje" kolor " bezpośrednio zamiast dzielenia koloru na komponenty R / G/B, co ułatwia obsługę tych samych kolorów o różnej jasności itp.

Jeśli przekonwertujesz swój obraz do HSV otrzymasz 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];

Kanał barwy:

Tutaj wpisz opis obrazka

Kanał nasycenia:

Tutaj wpisz opis obrazka

Kanał wartości:

Tutaj wpisz opis obrazka

Zazwyczaj kanał hue jest pierwszym, który patrzy na jeśli jesteś zainteresowany segmentacją "kolor" (np. wszystkie czerwone obiekty). Jednym z problemów jest to, że barwa jest wartością kołową/kątową, co oznacza, że najwyższe wartości są bardzo podobne do najniższych, co skutkuje jasnymi artefaktami na granicy pasztetów. Aby przezwyciężyć to dla określonej wartości, możesz przesunąć całą przestrzeń odcienia. Jeśli przesuniesz się o 50° dostaniesz coś takiego zamiast tego:

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;
    }

Tutaj wpisz opis obrazka

Teraz możesz użyć prostego wykrywania krawędzi, aby znaleźć krawędzie w kanał barwy:

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

Tutaj wpisz opis obrazka

Widać, że regiony są trochę większe niż prawdziwe paszteciki, może to być spowodowane małymi refleksami na ziemi wokół pasztecików, ale nie jestem tego pewien. Może to przez artefakty kompresji jpeg;)

Jeśli zamiast tego użyjesz kanału nasycenia, aby wyodrębnić krawędzie, skończysz z czymś takim:

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

Tutaj wpisz opis obrazka

Gdzie kontury nie są całkowicie zamknięte. Może ty możesz połącz odcień i nasycenie w ramach wstępnego przetwarzania, aby wyodrębnić krawędzie w kanale odcienia, ale tylko tam, gdzie Nasycenie jest wystarczająco wysokie.

Na tym etapie masz krawędzie. Pamiętaj, że krawędzie nie są jeszcze konturami. Jeśli bezpośrednio wyodrębnisz kontury z krawędzi, mogą one nie być zamknięte/oddzielone 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);
 }

Tutaj wpisz opis obrazka

Możesz usunąć te małe kontury, sprawdzając cv::contourArea(contoursH[i]) > someThreshold przed rysowaniem. Ale widzisz te dwa paszteciki po lewej stronie? Nadchodzi najtrudniejsza część... użyj kilku heurystyka, aby "poprawić" 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.

Tutaj wpisz opis obrazka

Jeśli wyciągniesz z tego kontury, będzie to wyglądało tak:

Tutaj wpisz opis obrazka

Jeśli zamiast tego wybierzesz tylko" wewnętrzne " kontury, to jest dokładnie to, co lubisz:

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);
 }

Tutaj wpisz opis obrazka

Należy pamiętać, że dylatacja i kontur wewnętrzny są nieco rozmyte, więc może nie działać dla różnych obrazów i jeśli początkowe krawędzie są lepiej umieszczone wokół granicy obiektu, może to być 1. nie jest konieczne, aby zrobić rozszerzenie i Inner contour thing I 2. jeśli nadal jest to konieczne, rozszerzenie spowoduje, że obiekt będzie mniejszy w tym scenariuszu (co na szczęście jest Świetne dla danego przykładowego obrazu.).

EDIT: kilka ważnych informacji o HSV: kanał barwy nadaje każdemu pikselowi kolor widma, nawet jeśli Nasycenie jest bardzo niskie (=szary/biały) lub jeśli kolor jest bardzo niski (wartość), więc często pożądane jest próg nasycenia i wartości kanałów, aby znaleźć jakiś konkretny kolor! To może być znacznie łatwiejsze i o wiele więcej niż Dylatacja, której użyłem w kodzie.

 85
Author: Micka,
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-15 10:28:33