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.Oryginał Image:
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:
Kanał nasycenia:
Kanał wartości:
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;
}
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);
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);
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);
}
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.
Jeśli wyciągniesz z tego kontury, będzie to wyglądało tak:
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);
}
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.
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