OpenCV's Sobel filter - why does it look so bad, especially compared to Gimp? -
i'm trying rebuild preprocessing have done before in gimp, using opencv. first stage sobel filter edge detection. works in gimp:
now here attempt opencv:
opencv_imgproc.sobel(/* src = */ scaled, /* dst = */ sobel, /* ddepth = */ opencv_core.cv_32f, /* dx = */ 1, /* dy = */ 1, /* ksize = */ 5, /* scale = */ 0.25, /* delta = */ 0.0, /* bordertype = */ opencv_core.border_replicate)
it looks bad, highlighting points instead of contours:
so doing wrong, or how gimp achieve such result , how can replicate in opencv?
info
image used https://www.pexels.com/photo/brown-wooden-flooring-hallway-176162/ ("free personal , commercial use").
solution tl;dr
edge detection via sobel filter requires two separate filter operations. cannot done in single step. result of 2 separate steps has combined form final result of edge detection.
info: i'm using float images (cv_32f)
simplicity.
solution in code:
// load example image std::string path = "c:\\temp\\sobeltest\\lobby2\\"; std::string filename = "pexels-photo-176162 scaled down.jpeg"; std::string fqn = path + filename; cv::mat img = cv::imread(fqn, cv_load_image_color); // value range: 0 - 255 // convert float , adapt value range (for simplicity) img.convertto(img, cv_32f, 1.f/255); // value range: 0.0 - 1.0 // build data 3x3 vertical sobel kernel float sobelkernelhorizontaldata[3][3] = { {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; // calculate normalization divisor/factor float sobelkernelnormalizationdivisor = 4.f; float sobelkernelnormalizationfactor = 1.f / sobelkernelnormalizationdivisor; // generate cv::mat vertical filter kernel cv::mat sobelkernelhorizontal = cv::mat(3,3, cv_32f, sobelkernelhorizontaldata); // value range of filter result (if used filtering): 0 - 4*255 or 0.0 - 4.0 // apply filter kernel normalization sobelkernelhorizontal *= sobelkernelnormalizationfactor; // value range of filter result (if used filtering): 0 - 255 or 0.0 - 1.0 // generate cv::mat horizontal filter kernel cv::mat sobelkernelvertical; cv::transpose(sobelkernelhorizontal, sobelkernelvertical); // apply 2 distinct sobel filtering steps cv::mat imgfilterresultvertical; cv::mat imgfilterresulthorizontal; cv::filter2d(img, imgfilterresultvertical, cv_32f, sobelkernelvertical); cv::filter2d(img, imgfilterresulthorizontal, cv_32f, sobelkernelhorizontal); // build overall filter result combining previous results cv::mat imgfilterresultmagnitude; cv::magnitude(imgfilterresultvertical, imgfilterresulthorizontal, imgfilterresultmagnitude); // write images hdd. important: convert uchar, otherwise black images std::string filenamefilterresultvertical = path + "imgfilterresultvertical" + ".jpeg"; std::string filenamefilterresulthorizontal = path + "imgfilterresulthorizontal" + ".jpeg"; std::string filenamefilterresultmagnitude = path + "imgfilterresultmagnitude" + ".jpeg"; cv::mat imgfilterresultverticaluchar; cv::mat imgfilterresulthorizontaluchar; cv::mat imgfilterresultmagnitudeuchar; imgfilterresultvertical.convertto(imgfilterresultverticaluchar, cv_8uc3, 255); imgfilterresulthorizontal.convertto(imgfilterresulthorizontaluchar, cv_8uc3, 255); imgfilterresultmagnitude.convertto(imgfilterresultmagnitudeuchar, cv_8uc3, 255); cv::imwrite(filenamefilterresultvertical, imgfilterresultverticaluchar); cv::imwrite(filenamefilterresulthorizontal, imgfilterresulthorizontaluchar); cv::imwrite(filenamefilterresultmagnitude, imgfilterresultmagnitudeuchar); // show images cv::imshow("img", img); cv::imshow("imgfilterresultvertical", imgfilterresultvertical); cv::imshow("imgfilterresulthorizontal", imgfilterresulthorizontal); cv::imshow("imgfilterresultmagnitude", imgfilterresultmagnitude); cv::waitkey();
note code equivalent this:
cv::sobel(img, imgfilterresultvertical, cv_32f, 1, 0, 3, sobelkernelnormalizationfactor); cv::sobel(img, imgfilterresulthorizontal, cv_32f, 0, 1, 3, sobelkernelnormalizationfactor); cv::magnitude(imgfilterresultvertical, imgfilterresulthorizontal, imgfilterresultmagnitude);
result images
source image, vertical filter result, horizontal filter result, combined filter result (magnitude)
short infos on opencv's data types , value ranges
- working float images (image type
cv_32f
) useful , simpler. however, working float images slower since 4 times data used (compared uchar). if want correctness high performance, have use uchar images , pass correct divisors (parameter "alpha") opencv functions. however, more error prone , happen values overflow without realizing it. - 8-bit images (uchar, cv_8uc) have value range of 0 - 255. 32-bit float images (cv_32f) have value range of 0.0 - 1.0 (values larger 1.0 displayed same 1.0). using 32-bit images easier since overflow less happen (however bad scaling, e.g. values above 1.0 can happen).
calculating kernel normalization divisor
the normalization divisor kernels can calculated following fomula:
f = max(abs(sumnegative), abs(sumpositive))
where sumnegative sum of negative values in kernel , sumpositive sum of positive values in kernel.
warning: not equal float normalizationdivisor = cv::sum(cv::abs(kernel))(0)
, have write custom function this.
additional tips
- edge detection resolution dependent edge thickness dependent. if edges want detect rather thick, can use larger sobel filter kernel sizes (see sobel filter kernel of large size , not use accepted answer. instead use adam bowen's answer (most likely) correct one). of course, can scale down image , use default 3x3 sobel filter detect thick edges.
- using larger filter kernels results in different normalization divisors / factors.
- the sobel filter rough approximation regarding neighborhood distances. scharr filter represents improvement on sobel filter "improves rotational invariance" [http://johncostella.com/edgedetect/ ]
- to save colored float images, have convert (and scale) them uchar using convertto
edge detection on color images
it makes no sense apply edge detection filters on color images. having image display color channel (b, g, r) contributes how edge detection , "encoding" result colored pixel specific , uncommon procedure. of course if goal make image "cool", go ahead. in case rules won't apply anyway.
Comments
Post a Comment