Opencv将联系的圈子分成单个

时间:2016-01-07 08:45:37

标签: python opencv

我有一个要处理的图像。我需要检测图像中的所有圆圈。这就是它。 org image

这是我的代码。

import cv2
import cv2.cv as cv
img = cv2.imread(imgpath)
cv2.imshow("imgorg",img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",gray)
ret,thresh = cv2.threshold(gray, 199, 255, cv.CV_THRESH_BINARY_INV)
cv2.imshow("thresh",thresh)
cv2.waitKey(0)
cv2.destrotAllWindows()

然后,我得到了这样的图像。 enter image description here

我尝试使用侵蚀和扩张将它们分成单个。但它不起作用。我的问题是如何将这些接触的圆分成单个,所以我可以检测它们。

根据@ Micka的想法,我尝试按照以下方式处理图像,这是我的代码。

import cv2
import cv2.cv as cv
import numpy as np

def findcircles(img,contours):
    minArea = 300;
    minCircleRatio = 0.5;
    for  contour  in contours:

        area = cv2.contourArea(contour)
        if area < minArea: 
            continue

        (x,y),radius = cv2.minEnclosingCircle(contour)
        center = (int(x),int(y))
        radius = int(radius)
        circleArea = radius*radius*cv.CV_PI;

        if area/circleArea < minCircleRatio:
             continue;
        cv2.circle(img, center, radius, (0, 255, 0), 2)
        cv2.imshow("imggg",img)

img = cv2.imread("a.png")
cv2.imshow("org",img)

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,threshold = cv2.threshold(gray, 199, 255,cv. CV_THRESH_BINARY_INV)
cv2.imshow("threshold",threshold)

blur = cv2.medianBlur(gray,5)
cv2.imshow("blur",blur)

laplacian=cv2.Laplacian(blur,-1,ksize = 5,delta = -50)
cv2.imshow("laplacian",laplacian)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(7,7))
dilation = cv2.dilate(laplacian,kernel,iterations = 1)
cv2.imshow("dilation", dilation)

result= cv2.subtract(threshold,dilation) 
cv2.imshow("result",result)

contours, hierarchy = cv2.findContours(result,cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)
findcircles(gray,contours)

但是我没有像@ Micka那样得到同样的效果。我不知道哪一步是错的。

2 个答案:

答案 0 :(得分:5)

改编@jochen的想法我来到了这个:

  1. 提取完整的圆形面具(我称之为fullForeground
  2. enter image description here

    1. 从彩色图像中,计算灰度,模糊(中间模糊大小7),并提取边缘,例如使用cv::Laplacian 该拉普拉斯阈值化&gt; 50给出:
    2. cv::Laplacian(blurred, lap, 0, 5); // no delta lapMask = lap > 50; // thresholding to values > 50

      enter image description here

      这一次扩张一次给出:

      cv::dilate(lapMask, dilatedThresholdedLaplacian, cv::Mat()); // dilate the edge mask once
      

      enter image description here

      现在减法fullForeground - dilatedThresholdedLaplacian(与此类蒙版的and_not运算符相同)给出:

      enter image description here

      从这里你可以计算轮廓。对于每个轮廓,您可以计算面积并将其与封闭圆的面积进行比较,得出此代码和结果:

      std::vector<std::vector<cv::Point> > contours;
      cv::findContours(separated.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
      
      double minArea = 500;
      double minCircleRatio = 0.5;
      for(unsigned int i=0; i<contours.size(); ++i)
      {
          double cArea = cv::contourArea(contours[i]);
          if(cArea < minArea) continue;
      
          //filteredContours.push_back(contours[i]);
          //cv::drawContours(input, contours, i, cv::Scalar(0,255,0), 1);
          cv::Point2f center;
          float radius;
          cv::minEnclosingCircle(contours[i], center, radius);
      
          double circleArea = radius*radius*CV_PI;
      
          if(cArea/circleArea < minCircleRatio) continue;
      
          cv::circle(input, center, radius, cv::Scalar(0,0,255),2);
      }
      

      enter image description here

      这是显示覆盖范围的另一张图片:

      enter image description here

      希望这会有所帮助

答案 1 :(得分:2)

我认为第一个错误是thesh的价值。 在您的示例中,命令cv2.threshold将所有白色区域转换为黑色,将其他所有区域转换为白色。我建议使用thesh的较小值,以便将所有黑色像素转换为白色,并将所有白色或“彩色”像素(在圆圈内)转换为黑色或反之亦然。 thesh的值应该比最亮的黑色像素大一点 有关详细信息,请参阅opencv docu for threshold 然后我会在阈值图像中放置opencv find all contours并过滤它们以获得“有效”圆圈,例如按尺寸和形状。
如果那不够,你可以从图像的其余部分分割内圆:首先计算threasholdImageA,所有白色区域都是黑色的。然后计算threasholdImageB,所有黑色区域都是黑色。然后将threasholdImageA和threasholdImageB(例如,与numpy.logical_and)结合起来,得到二进制图像,其中内圈为白色,其余为黑色。当然,必须明智地选择阈值以获得特定结果。 这样也可以分割内部直接接触背景的圆圈。