Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用OpenCV(基于Hough变换或其他特征)编写鲁棒(颜色和大小不变)圆检测_C++_Python_C_Opencv_Computer Vision - Fatal编程技术网

C++ 使用OpenCV(基于Hough变换或其他特征)编写鲁棒(颜色和大小不变)圆检测

C++ 使用OpenCV(基于Hough变换或其他特征)编写鲁棒(颜色和大小不变)圆检测,c++,python,c,opencv,computer-vision,C++,Python,C,Opencv,Computer Vision,我编写了以下非常简单的python代码来查找图像中的圆: import cv import numpy as np WAITKEY_DELAY_MS = 10 STOP_KEY = 'q' cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE); cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE); key_pressed = False while key

我编写了以下非常简单的python代码来查找图像中的圆:

import cv
import numpy as np

WAITKEY_DELAY_MS = 10
STOP_KEY = 'q'

cv.NamedWindow("image - press 'q' to quit", cv.CV_WINDOW_AUTOSIZE);
cv.NamedWindow("post-process", cv.CV_WINDOW_AUTOSIZE);

key_pressed = False
while key_pressed != STOP_KEY:

    # grab image
    orig = cv.LoadImage('circles3.jpg')

    # create tmp images
    grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
    processed = cv.CreateImage(cv.GetSize(orig), 8, 1)


    cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)

    cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)

    # do some processing on the grey scale image
    cv.Erode(grey_scale, processed, None, 10)
    cv.Dilate(processed, processed, None, 10)
    cv.Canny(processed, processed, 5, 70, 3)
    cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

    storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)

    # these parameters need to be adjusted for every single image
    HIGH = 50
    LOW = 140

    try: 
        # extract circles
        cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, HIGH, LOW)

        for i in range(0, len(np.asarray(storage))):
            print "circle #%d" %i
            Radius = int(np.asarray(storage)[i][0][2])
            x = int(np.asarray(storage)[i][0][0])
            y = int(np.asarray(storage)[i][0][1])
            center = (x, y)

            # green dot on center and red circle around
            cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

            cv.Circle(processed, center, 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
            cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

    except:
        print "nothing found"
        pass

    # show images
    cv.ShowImage("image - press 'q' to quit", orig)
    cv.ShowImage("post-process", processed)

    cv_key = cv.WaitKey(WAITKEY_DELAY_MS)
    key_pressed = chr(cv_key & 255)
从以下两个示例中可以看出,“圆查找质量”差异很大:

案例1:

案例2:

Case1和Case2基本上是相同的图像,但算法仍然检测不同的圆。如果我用不同大小的圆来呈现算法,圆检测甚至可能完全失败。这主要是由于
HIGH
LOW
参数需要针对每个新图片分别进行调整


因此,我的问题是:使该算法更健壮的各种可能性是什么?它应该是大小和颜色不变的,以便检测具有不同颜色和不同大小的不同圆。也许使用Hough变换不是最好的方法?有更好的方法吗?

如您所知,Hough变换使用“模型”在(通常)边缘检测图像中查找某些特征。在
霍夫圆
的情况下,该模型是一个完美的圆。这意味着可能不存在一个参数组合,它可以在不增加误报数量的情况下检测图片中更不稳定和椭圆形状的圆。另一方面,由于潜在的投票机制,非封闭的完美圆或带有“凹痕”的完美圆可能会持续出现。因此,根据您的预期输出,您可能希望也可能不希望使用此方法

也就是说,我看到一些东西可能会帮助您实现此功能:

  • HoughCircles
    在内部调用
    Canny
    ,所以我想你可以不调用它
  • param1
    (您称之为
    HIGH
    )通常在值
    200左右初始化。它用作对
    Canny
    的内部调用的参数:
    cv.Canny(processed,cannied,HIGH,HIGH/2)
    。您可以这样运行
    Canny
    查看设置
    HIGH
    如何影响通过Hough变换处理的图像
  • param2
    (您称之为
    LOW
    )通常围绕值
    100进行初始化。它是Hough变换累加器的投票阈值。将其设置得越高意味着假阴性越多,假阳性越多。我相信这是第一个你想摆弄的
  • 参考:


    更新re:filled circles:使用Hough变换找到圆形状后,可以通过采样边界颜色并将其与假定圆内的一个或多个点进行比较来测试它们是否已填充。或者,您可以将假定圆内的一个或多个点与给定的背景颜色进行比较。如果前一个比较成功,或者如果替代比较失败,则填充圆圈。

    查看您的代码,我注意到以下几点:

    • 灰度转换。我明白你为什么这么做,但要知道你在扔东西 把信息放在那里。正如您在“后期处理”图像中看到的,您的黄色圆圈是 与背景强度相同,只是颜色不同

    • 去除噪声后的边缘检测(erae/放大)。这是不必要的;坎尼应该处理好这件事

    • Canny边缘检测。你的“开放”圆有两条边,一条内边和一条外边。由于它们非常接近,Canny gauss滤波器可能会将它们相加。如果没有,两条边就会靠得很近。也就是说,在Canny之前,你有开放和充满的圆圈。之后,分别有0/2和1条边。由于Hough再次调用Canny,在第一种情况下,两条边可能会一起平滑(取决于初始宽度),这就是为什么核心Hough算法可以同样处理开放圆和填充圆

    因此,我的第一个建议是更改灰度贴图。不使用强度,但使用色调/饱和度/值。此外,使用微分方法-您正在寻找边。因此,计算一个
    HSV
    变换,平滑一个副本,然后取原始副本和平滑副本之间的差值。这将获得每个点的
    dH、dS、dV
    值(色调、饱和度、值的局部变化)。正方形并添加以获得一维图像,所有边缘(内部和外部)附近都有峰值


    我的第二个建议是本地正常化,但我不确定这是否必要。这个想法是,你并不特别关心你得到的边缘信号的确切值,它实际上应该是二进制的(不管是不是边缘)。因此,可以通过除以局部平均值(其中局部值的大小与边缘大小的大小相同)来规范化每个值

    好的,看看这些图像。我建议使用
    **活动轮廓**

    • 活动轮廓的好处在于,它们几乎完全适合任何给定的形状。无论是正方形还是三角形,在你的情况下,他们都是完美的候选人
    • 如果你能提取出圆的中心,那就太好了。活动轮廓始终需要一个起点,可以从该起点开始增长或收缩以适应。无需始终将中心与中心对齐。稍微偏移一下还是可以的
    • 在你的例子中,如果你让轮廓从中心向外生长,它们将停留在圆的边界上
    • 请注意,增长或收缩的活动轮廓使用,这意味着您可以设置轮廓的方向,向内或向外
    • 您可能需要使用灰度级的渐变图像。但你也可以试试颜色。如果有用的话
    • 如果你不做公关
      import cv
      import numpy as np
      
      # grab image
      orig = cv.LoadImage('circles3.jpg')
      
      # create tmp images
      grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
      processed = cv.CreateImage(cv.GetSize(orig), 8, 1)
      
      cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 3, 3)
      
      cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)
      
      # do some processing on the grey scale image
      cv.Erode(grey_scale, processed, None, 10)
      cv.Dilate(processed, processed, None, 10)
      cv.Canny(processed, processed, 5, 70, 3)
      cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)
      
      #storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
      storage = cv.CreateMemStorage(0)
      
      contours = cv.FindContours(processed, storage, cv.CV_RETR_EXTERNAL)
      # N.B. 'processed' image is modified by this!
      
      #contours = cv.ApproxPoly (contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) 
      # If you wanted to reduce the number of points...
      
      cv.DrawContours (orig, contours, cv.RGB(0,255,0), cv.RGB(255,0,0), 2, 3, cv.CV_AA, (0, 0)) 
      
      def contour_iterator(contour):
        while contour:
          yield contour
          contour = contour.h_next()
      
      for c in contour_iterator(contours):
        # Number of points must be more than or equal to 6 for cv.FitEllipse2
        if len(c) >= 6:
          # Copy the contour into an array of (x,y)s
          PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)
      
          for (i, (x, y)) in enumerate(c):
            PointArray2D32f[0, i] = (x, y)
      
          # Fits ellipse to current contour.
          (center, size, angle) = cv.FitEllipse2(PointArray2D32f)
      
          # Convert ellipse data from float to integer representation.
          center = (cv.Round(center[0]), cv.Round(center[1]))
          size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))
      
          # Draw ellipse
          cv.Ellipse(orig, center, size, angle, 0, 360, cv.RGB(255,0,0), 2,cv.CV_AA, 0)
      
      # show images
      cv.ShowImage("image - press 'q' to quit", orig)
      #cv.ShowImage("post-process", processed)
      cv.WaitKey(-1)
      
      import cv
      import numpy as np
      
      output = cv.LoadImage('case1.jpg')
      orig = cv.LoadImage('case1.jpg')
      
      # create tmp images
      rrr=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
      ggg=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
      bbb=cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
      processed = cv.CreateImage((orig.width,orig.height), cv.IPL_DEPTH_8U, 1)
      storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
      
      def channel_processing(channel):
          pass
          cv.AdaptiveThreshold(channel, channel, 255, adaptive_method=cv.CV_ADAPTIVE_THRESH_MEAN_C, thresholdType=cv.CV_THRESH_BINARY, blockSize=55, param1=7)
          #mop up the dirt
          cv.Dilate(channel, channel, None, 1)
          cv.Erode(channel, channel, None, 1)
      
      def inter_centre_distance(x1,y1,x2,y2):
          return ((x1-x2)**2 + (y1-y2)**2)**0.5
      
      def colliding_circles(circles):
          for index1, circle1 in enumerate(circles):
              for circle2 in circles[index1+1:]:
                  x1, y1, Radius1 = circle1[0]
                  x2, y2, Radius2 = circle2[0]
                  #collision or containment:
                  if inter_centre_distance(x1,y1,x2,y2) < Radius1 + Radius2:
                      return True
      
      def find_circles(processed, storage, LOW):
          try:
              cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, LOW)#, 0, 100) great to add circle constraint sizes.
          except:
              LOW += 1
              print 'try'
              find_circles(processed, storage, LOW)
          circles = np.asarray(storage)
          print 'number of circles:', len(circles)
          if colliding_circles(circles):
              LOW += 1
              storage = find_circles(processed, storage, LOW)
          print 'c', LOW
          return storage
      
      def draw_circles(storage, output):
          circles = np.asarray(storage)
          print len(circles), 'circles found'
          for circle in circles:
              Radius, x, y = int(circle[0][2]), int(circle[0][0]), int(circle[0][1])
              cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
              cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
      
      #split image into RGB components
      cv.Split(orig,rrr,ggg,bbb,None)
      #process each component
      channel_processing(rrr)
      channel_processing(ggg)
      channel_processing(bbb)
      #combine images using logical 'And' to avoid saturation
      cv.And(rrr, ggg, rrr)
      cv.And(rrr, bbb, processed)
      cv.ShowImage('before canny', processed)
      # cv.SaveImage('case3_processed.jpg',processed)
      #use canny, as HoughCircles seems to prefer ring like circles to filled ones.
      cv.Canny(processed, processed, 5, 70, 3)
      #smooth to reduce noise a bit more
      cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
      cv.ShowImage('processed', processed)
      #find circles, with parameter search
      storage = find_circles(processed, storage, 100)
      draw_circles(storage, output)
      # show images
      cv.ShowImage("original with circles", output)
      cv.SaveImage('case1.jpg',output)
      
      cv.WaitKey(0)
      
      import cv2
      import math
      import numpy as np
      
      d_red = cv2.cv.RGB(150, 55, 65)
      l_red = cv2.cv.RGB(250, 200, 200)
      
      orig = cv2.imread("c.jpg")
      img = orig.copy()
      img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
      
      detector = cv2.FeatureDetector_create('MSER')
      fs = detector.detect(img2)
      fs.sort(key = lambda x: -x.size)
      
      def supress(x):
              for f in fs:
                      distx = f.pt[0] - x.pt[0]
                      disty = f.pt[1] - x.pt[1]
                      dist = math.sqrt(distx*distx + disty*disty)
                      if (f.size > x.size) and (dist<f.size/2):
                              return True
      
      sfs = [x for x in fs if not supress(x)]
      
      for f in sfs:
              cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), d_red, 2, cv2.CV_AA)
              cv2.circle(img, (int(f.pt[0]), int(f.pt[1])), int(f.size/2), l_red, 1, cv2.CV_AA)
      
      h, w = orig.shape[:2]
      vis = np.zeros((h, w*2+5), np.uint8)
      vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
      vis[:h, :w] = orig
      vis[:h, w+5:w*2+5] = img
      
      cv2.imshow("image", vis)
      cv2.imwrite("c_o.jpg", vis)
      cv2.waitKey()
      cv2.destroyAllWindows()
      
      P = {p1, ..., pn} 
      P: search space
      p1...pn: pixels
      
      E(P) : enumerates the search space
      V(p) : checks whether the item/pixel has the desirable properties, the items passing the check are added to the output list