二进制阈值图像-> 应用精明的边缘检测-> findContour(),这会改善轮廓检测吗?

Posted

技术标签:

【中文标题】二进制阈值图像-> 应用精明的边缘检测-> findContour(),这会改善轮廓检测吗?【英文标题】:binary thresholded image-> apply canny edge detection -> findContour(), does this improve Contour detection? 【发布时间】:2019-09-22 18:43:17 【问题描述】:

我正在尝试检测黄色物体。我在 HSV 配色方案中执行颜色分割,使用 cvInRange 对黄色范围进行阈值,它返回一个二进制阈值掩码,检测到的区域以白色显示,而其他颜色被忽略并涂黑。我认为获得边缘不仅会减少 findContour() 的计算,而且会使改变边缘平面更加明显。因此,不要这样做:

    binary thresholded image -> findContour()

我做到了:

    binary thresholded image -> Canny() -> findContour() instead.

请参阅下面的代码 + 显示的图像帧输出的附加图片。

public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) 

     InputFrame = inputFrame.rgba();

     Core.transpose(InputFrame,mat1); //transpose mat1(src) to mat2(dst), sorta like a Clone!
     Imgproc.resize(mat1,mat2,InputFrame.size(),0,0,0);    // params:(Mat src, Mat dst, Size dsize, fx, fy, interpolation)   Extract the dimensions of the new Screen Orientation, obtain the new orientation's surface width & height.  Try to resize to fit to screen.
     Core.flip(mat2,InputFrame,-1);   // mat3 now get updated, no longer is the Origi inputFrame.rgba BUT RATHER the transposed, resized, flipped version of inputFrame.rgba().

     int rowWidth = InputFrame.rows();
     int colWidth = InputFrame.cols();

     Imgproc.cvtColor(InputFrame,InputFrame,Imgproc.COLOR_RGBA2RGB);
     Imgproc.cvtColor(InputFrame,InputFrame,Imgproc.COLOR_RGB2HSV);


 //============= binary threshold image to Yellow mask ============
     Lower_Yellow = new Scalar(21,150,150);    //HSV color scale  H to adjust color, S to control color variation, V is indicator of amt of light required to be shine on object to be seen.
     Upper_Yellow = new Scalar(31,255,360);    //HSV color scale

     Core.inRange(InputFrame,Lower_Yellow, Upper_Yellow, maskForYellow);


 //============== Apply Morphology to remove noise ===================
     final Size kernelSize = new Size(5, 5);  //must be odd num size & greater than 1.
     final Point anchor = new Point(-1, -1);   //default (-1,-1) means that the anchor is at the center of the structuring element.
     final int iterations = 1;   //number of times dilation is applied.  https://docs.opencv.org/3.4/d4/d76/tutorial_js_morphological_ops.html

     Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, kernelSize);

     Imgproc.morphologyEx(maskForYellow, yellowMaskMorphed, Imgproc.MORPH_CLOSE, kernel, anchor, iterations);   //dilate first to remove then erode.  White regions becomes more pronounced, erode away black regions


 //=========== Apply Canny to obtain edge detection ==============
     Mat mIntermediateMat = new Mat();
     Imgproc.GaussianBlur(yellowMaskMorphed,mIntermediateMat,new Size(9,9),0,0);   //better result than kernel size (3,3, maybe cos reference area wider, bigger, can decide better whether inrange / out of range.
     Imgproc.Canny(mIntermediateMat, mIntermediateMat, 5, 120);   //try adjust threshold   //https://***.com/questions/25125670/best-value-for-threshold-in-canny


 //============ apply findContour()==================
     List<MatOfPoint> contours = new ArrayList<>();
     Mat hierarchy = new Mat();
     Imgproc.findContours(mIntermediateMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));   


 //===========  Use contourArea to find LargestBlob contour ===============
     double maxArea1 = 0;
     int maxAreaIndex1 = 0;
     //MatOfPoint max_contours = new MatOfPoint();
     Rect r = null;
     ArrayList<Rect> rect_array = new ArrayList<Rect>();

     for(int i=0; i < contours.size(); i++) 
         //if(Imgproc.contourArea(contours.get(i)) > 300)    //Size of Mat contour @ that particular point in ArrayList of Points.
         double contourArea1 = Imgproc.contourArea(contours.get(i));    
        //Size of Mat contour @ that particular point in ArrayList of Points.
             if (maxArea1 < contourArea1)
                 maxArea1 = contourArea1;
                 maxAreaIndex1 = i;
             
             //maxArea1 = Imgproc.contourArea(contours.get(i));  //assigned but nvr used
             //max_contours = contours.get(i);
             r = Imgproc.boundingRect(contours.get(maxAreaIndex1));    
             rect_array.add(r);  //will only have 1 r in the array eventually, cos we will only take the one w largestContourArea.
     


     Imgproc.cvtColor(InputFrame, InputFrame, Imgproc.COLOR_HSV2RGB);


 //============ plot largest blob contour ================
     if (rect_array.size() > 0)    //if got more than 1 rect found in rect_array, draw them out!

         Iterator<Rect> it2 = rect_array.iterator();    //only got 1 though, this method much faster than drawContour, wont lag. =D
         while (it2.hasNext()) 
             Rect obj = it2.next();
             //if
             Imgproc.rectangle(InputFrame, obj.br(), obj.tl(),
                 new Scalar(0, 255, 0), 1);
         

     

原始黄色对象1

HSV 色彩空间中的对象2

cvInrRange 变为黄色后 - 返回二进制阈值掩码 3

应用 Canny 边缘检测后返回的边缘4

【问题讨论】:

【参考方案1】:

我已经尝试了这两种方法,发现在阈值图像上应用 Canny() 有助于使检测更快、更稳定,因此我将这部分保留在我的代码中。我的猜测是,在我们应用 Canny() 之后可能需要计算的点更少,它还有助于使边缘更明显,因此在 findContour() 中计算变得更容易和更快。

【讨论】:

以上是关于二进制阈值图像-> 应用精明的边缘检测-> findContour(),这会改善轮廓检测吗?的主要内容,如果未能解决你的问题,请参考以下文章

自适应 Canny 边缘检测

使用精明边缘检测的图像裁剪[重复]

图像分割基于阙值+边缘检测+区域法图像分割matlab源码含GUI

Matlab边缘检测问题

图像边缘检测基于matlab自适应阈值的八方向和四方向sobel图像边缘检测含Matlab源码 2058期

图像边缘检测基于matlab自适应阈值的八方向和四方向sobel图像边缘检测含Matlab源码 2058期