使用 OpenCV(基于霍夫变换或其他功能)编写稳健(颜色和大小不变)的圆检测

2021-12-10 00:00:00 python opencv computer-vision c c++

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

I wrote the following very simple python code to find circles in an image:

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)

从以下两个例子可以看出,寻圈质量"差异很大:

As you can see from the following two examples, the 'circle finding quality' varies quite a lot:

案例 1:

案例 2:

Case1 和Case2 基本上是同一张图片,但是算法检测到的圆还是不同的.如果我向算法展示具有不同大小圆圈的图像,则圆圈检测甚至可能完全失败.这主要是由于 HIGHLOW 参数需要为每个新图片单独调整.

Case1 and Case2 are basically the same image, but still the algorithm detects different circles. If I present the algorithm an image with differently sized circles, the circle detection might even fail completely. This is mostly due to the HIGH and LOW parameters which need to be adjusted individually for each new picture.

因此我的问题是:使该算法更加稳健的各种可能性是什么?它应该是大小和颜色不变的,以便检测不同颜色和不同大小的不同圆圈.也许使用霍夫变换不是最好的做事方式?有没有更好的方法?

Therefore my question: What are the various possibilities of making this algorithm more robust? It should be size and color invariant so that different circles with different colors and in different sizes are detected. Maybe using the Hough transform is not the best way of doing things? Are there better approaches?

推荐答案

以下内容基于我作为视觉研究员的经验.从您的问题来看,您似乎对可能的算法和方法感兴趣,而只是对一段有效的代码感兴趣.首先,我为您的示例图像提供了一个快速而肮脏的 Python 脚本,并显示了一些结果以证明它可能会解决您的问题.在解决这些问题后,我会尝试回答您关于稳健检测算法的问题.

The following is based on my experience as a vision researcher. From your question you seem to be interested in possible algorithms and methods rather only a working piece of code. First I give a quick and dirty Python script for your sample images and some results are shown to prove it could possibly solve your problem. After getting these out of the way, I try to answer your questions regarding robust detection algorithms.

一些带有检测到的圆圈的示例图片(除您之外的所有图片均从 flickr.com 下载并获得 CC 许可)(无需更改/调整任何参数,正是以下代码用于提取所有图片中的圆圈):

Some sample images (all the images apart from yours are downloaded from flickr.com and are CC licensed) with the detected circles (without changing/tuning any parameters, exactly the following code is used to extract the circles in all the images):

这是代码:

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()

如您所见,它基于 MSER 斑点检测器.除了简单地映射到灰度之外,代码不会对图像进行预处理.因此,在您的图像中会遗漏那些微弱的黄色斑点.

As you can see it's based on the MSER blob detector. The code doesn't preprocess the image apart from the simple mapping into grayscale. Thus missing those faint yellow blobs in your images is expected.

简而言之:除了仅提供两个没有描述的示例图像之外,您不会告诉我们您对问题的了解.在这里,我解释了为什么我以我的拙见,在询问解决问题的有效方法之前,先了解有关问题的更多信息很重要.

回到主要问题:解决这个问题的最佳方法是什么?让我们把它看成一个搜索问题.为了简化讨论,假设我们正在寻找具有给定大小/半径的圆.因此,问题归结为找到中心.每个像素都是一个候选中心,因此,搜索空间包含所有像素.

Back to the main question: what is the best method for this problem? Let's look at this as a search problem. To simplify the discussion assume we are looking for circles with a given size/radius. Thus, the problem boils down to finding the centers. Every pixel is a candidate center, therefore, the search space contains all the pixels.

P = {p1, ..., pn} 
P: search space
p1...pn: pixels

为了解决这个搜索问题,应该定义另外两个函数:

To solve this search problem two other functions should be defined:

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

假设算法的复杂性无关紧要,可以使用穷举搜索或蛮力搜索,其中 E 获取每个像素并传递给 V.在实时应用程序中,减少搜索空间和优化计算很重要V的效率.

Assuming the complexity of the algorithm doesn't matter, the exhaustive or brute-force search can be used in which E takes every pixel and passes to V. In real-time applications it's important to reduce the search space and optimize computational efficiency of V.

我们离主要问题越来越近了.我们如何定义 V,更准确地说,候选者的哪些属性应该是度量,以及如何解决将它们分成合意和不合意的二分法问题.最常见的方法是找到一些属性,这些属性可用于基于属性的度量来定义简单的决策规则.这就是你通过反复试验所做的.您正在通过从正面和负面示例中学习来编写分类器.这是因为您使用的方法不知道您想要做什么.您必须调整/调整决策规则的参数和/或预处理数据,以便减少二分法问题方法使用的(理想候选者的)属性的变化.您可以使用机器学习算法为给定的一组示例找到最佳参数值.您可以使用从决策树到遗传编程的大量学习算法来解决这个问题.您还可以使用学习算法为多种圆检测算法找到最佳参数值,并查看哪种算法的准确度更高.这为您只需要收集样本图像的学习算法承担了主要负担.

We are getting closer to the main question. How we could define V, to be more precise what properties of the candidates should be measures and how should make solve the dichotomy problem of splitting them into desirable and undesirable. The most common approach is to find some properties which can be used to define simple decision rules based on the measurement of the properties. This is what you're doing by trial and error. You're programming a classifier by learning from positive and negative examples. This is because the methods you're using have no idea what you want to do. You have to adjust / tune the parameters of the decision rule and/or preprocess the data such that the variation in the properties (of the desirable candidates) used by the method for the dichotomy problem are reduced. You could use a machine learning algorithm to find the optimal parameter values for a given set of examples. There's a whole host of learning algorithms from decision trees to genetic programming you can use for this problem. You could also use a learning algorithm to find the optimal parameter values for several circle detection algorithms and see which one gives a better accuracy. This takes the main burden on the learning algorithm you just need to collect sample images.

另一种经常被忽视的提高稳健性的方法是利用额外的现成信息.如果您以几乎为零的额外努力知道圆圈的颜色,则可以显着提高检测器的准确度.如果您知道圆在平面上的位置并且想要检测成像的圆,您应该记住这两组位置之间的转换是由 2D 单应性描述的.并且可以仅使用四个点来估计单应性.然后,您可以提高稳健性以获得坚如磐石的方法.特定领域知识的价值常常被低估.这样看,在第一种方法中,我们尝试基于有限数量的样本来近似一些决策规则.在第二种方法中,我们知道决策规则,只需要找到一种方法在算法中有效地利用它们即可.

The other approach to improve robustness which is often overlooked is to utilize extra readily available information. If you know the color of the circles with virtually zero extra effort you could improve the accuracy of the detector significantly. If you knew the position of the circles on the plane and you wanted to detect the imaged circles, you should remember the transformation between these two sets of positions is described by a 2D homography. And the homography can be estimated using only four points. Then you could improve the robustness to have a rock solid method. The value of domain-specific knowledge is often underestimated. Look at it this way, in the first approach we try to approximate some decision rules based on a limited number of sample. In the second approach we know the decision rules and only need to find a way to effectively utilize them in an algorithm.

总而言之,有两种方法可以提高解决方案的准确性/稳健性:

To summarize, there are two approaches to improve the accuracy / robustness of the solution:

  1. 基于工具:找到一种更易于使用的算法/参数数量较少/调整算法/使用机器学习算法自动执行此过程
  2. 基于信息的:您是否使用了所有现成的信息?在问题中,您没有提及您对问题的了解.
  1. Tool-based: finding an easier to use algorithm / with fewer number of parameters / tweaking the algorithm / automating this process by using machine learning algorithms
  2. Information-based: are you using all the readily available information? In the question you don't mention what you know about the problem.

对于您共享的这两张图像,我将使用斑点检测器而不是 HT 方法.对于背景减法,我建议尝试估计背景的颜色,因为在这两个图像中,它没有变化,而圆圈的颜色不同.大部分区域都是空的.

For these two images you have shared I would use a blob detector not the HT method. For background subtraction I would suggest to try to estimate the color of the background as in the two images it is not varying while the color of the circles vary. And the most of the area is bare.

相关文章