如何找到这些有时重叠的圆圈的中心 [英] How to find the centre of these sometimes-overlapping circles

查看:380
本文介绍了如何找到这些有时重叠的圆圈的中心的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为我正在研究的项目的一部分,我需要使用OpenCV和Python在图像中找到一些blob的中心点。
我遇到了一些麻烦,真的很感激任何帮助或见解:)



我目前的方法是:获取轮廓图像,在那些上面叠加椭圆,使用斑点检测器找到每个这些的中心。
这个效果相当不错,但偶尔我需要忽略无关的blob,有时blob会相互碰触。



这是一个例子当它进展顺利时:
良好的源图像:

提取轮廓后

检测到斑点:



当它变得很糟糕时(你可以看到它在三个blob上错误地叠加了一个椭圆,并检测到一个我不想要的):
Bad源图片:

提取轮廓后:

检测到blob:



这是代码I目前使用。我不确定任何其他选项。

  def process_and_detect(img_path):
img = cv2.imread(path )
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(imgray,50,150,0)
im2,等高线,等级= cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
drawn_img = np.zeros(img.shape,np.uint8)
min_area = 50
min_ellipses = [] $ b轮廓中的cnt为$ b:如果cv2.contourArea(cnt)> = min_area,则为

ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(drawn_img,ellipse,(0,255, 0), - 1)
plot_img(drawn_img,size = 12)

#更改阈值
params = cv2.SimpleBlobDetector_Params()
params.filterByColor = True
params.blobColor = 255
params.filterByCircularity = True
params.minCircularity = 0.75
params.filterByArea = True
params.minArea = 150
#Set起来了检测器
detector = cv2.SimpleBlobDetector_create(params)

#检测blob。
keypoints = detector.detect(drawn_img)关键点k的

x = round(k.pt [0])
y = round(k.pt [1])
line_length = 20
cv2.line(img,(x-line_length,y),(x + line_length,y),(255,0,0),2)
cv2.line( img,(x,y-line_length),(x,y + line_length),(255,0,0),2)
plot_img(img,size = 12)

非常感谢你阅读这篇文章,我真诚地希望有人可以帮助我,或指出我正确的方向。谢谢!

解决方案

Blob探测器



目前,您的实施是多余的。来自



更多参与



这个相同的管道可用于自动循环阈值,因此您不必猜测和硬编码这些值。由于斑点的大小看起来大致相同,因此您可以循环直到所有轮廓具有大致相同的区域。你可以这样做,例如通过查找中位轮廓大小,定义您允许的上限和下限中位数的某个百分比,并检查检测到的所有轮廓是否适合这些边界。



这是我的意思是动画 gif 。请注意,一旦轮廓分离, gif 就会停止:





然后你可以简单地找到那些分开的轮廓的质心。这是代码:

  def process_and_detect(img_path):

img = cv2.imread(img_path)
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

for thresh_val in range(0,255):

#threshold和detect contours
thresh = cv2.threshold(imgray,thresh_val,255,cv2.THRESH_BINARY)[1]
contours = cv2.findContours(thresh,
cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)[1]

#按区域过滤轮廓
min_area = 50
filtered_contours = [c表示轮廓中的c
如果cv2.contourArea(c)> = min_area]
area_contours = [cv2.contourArea(c)for c in filtered_contours]

#可接受的偏离中位数轮廓区域
median_area = np.median(area_contours)
dev = 0.3
lowerb = median_area - dev * median_area
upperb = median_area + de v * median_area

#当所有轮廓都偏离中位数区域
if((area_contours>低于)& (area_contours< upperb))。all():
break

#draw中心位置blob
line_length = 8
cross_color =(255,0,0 )
for c in filtered_contours:
M = cv2.moments(c)
x = int(M ['m10'] / M ['m00'])
y = int( M ['m01'] / M ['m00'])
cv2.line(img,(x-line_length,y),(x + line_length,y),cross_color,2)
cv2。 line(img,(x,y-line_length),(x,y + line_length),cross_color,2)



请注意,我在这里使用范围(0,255)循环显示所有可能的阈值) 0,1,...,254 但实际上你可以从高处开始并一次跳过几个值,比如说,范围(50,200,5)获得 50,55,...,195 当然会要快得多。


As part of a project I'm working on, I need to find the centre-point of some "blobs" in an image using OpenCV with Python. I'm having a bit of trouble with it, and would truly appreciate any help or insight :)

My current method is to: get the contours of the images, overlay ellipses on those, use the blob detector to find the centre of each of these. This works fairly well, but occasionally I have extraneous blobs that I need to ignore, and sometimes the blobs are touching each-other.

Here's an example of when it goes well: Good source image: After extracting contours: With the blobs detected:

And when it goes poorly (you can see that it's incorrectly overlayed an ellipse over three blobs, and detected one that I don't want): Bad source image: After extracting contours: With the blobs detected:

This is the code I currently use. I'm unsure of any other option.

def process_and_detect(img_path):
    img = cv2.imread(path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, thresh = cv2.threshold(imgray, 50, 150, 0)
    im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    drawn_img = np.zeros(img.shape, np.uint8)
    min_area = 50
    min_ellipses = []
    for cnt in contours:
        if cv2.contourArea(cnt) >= min_area:
            ellipse = cv2.fitEllipse(cnt)
            cv2.ellipse(drawn_img,ellipse,(0,255,0),-1)
    plot_img(drawn_img, size=12)

    # Change thresholds
    params = cv2.SimpleBlobDetector_Params()
    params.filterByColor = True
    params.blobColor = 255
    params.filterByCircularity = True
    params.minCircularity = 0.75
    params.filterByArea = True
    params.minArea = 150
    # Set up the detector
    detector = cv2.SimpleBlobDetector_create(params)

    # Detect blobs.
    keypoints = detector.detect(drawn_img)
    for k in keypoints:
        x = round(k.pt[0])
        y = round(k.pt[1])
        line_length = 20
        cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2)
        cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2)
    plot_img(img, size=12)

Thank you so much for reading this far, I sincerely hope someone can help me out, or point me in the right direction. Thanks!

解决方案

Blob detector

Currently, your implementation is redundant. From the SimpleBlobDetector() docs:

The class implements a simple algorithm for extracting blobs from an image:

  1. Convert the source image to binary images by applying thresholding with several thresholds from minThreshold (inclusive) to maxThreshold (exclusive) with distance thresholdStep between neighboring thresholds.
  2. Extract connected components from every binary image by findContours() and calculate their centers.
  3. Group centers from several binary images by their coordinates. Close centers form one group that corresponds to one blob, which is controlled by the minDistBetweenBlobs parameter.
  4. From the groups, estimate final centers of blobs and their radiuses and return as locations and sizes of keypoints.

So you're implementing part of the steps already, which might give some unexpected behavior. You could try playing with the parameters to see if you can figure out some that work for you (try creating trackbars to play with the parameters and get live results of your algorithm with different blob detector parameters).

Modifying your pipeline

However, you've already got most of your own pipeline written, so you can easily remove the blob detector and implement your own algorithm. If you simply drop your threshold a bit, you can easily get clearly marked circles, and then blob detection is as simple as contour detection. If you have a separate contour for each blob, then you can calculate the centroid of the contour with moments(). For example:

def process_and_detect(img_path):

    img = cv2.imread(img_path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, thresh = cv2.threshold(imgray, 100, 255, cv2.THRESH_BINARY)

    contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[1]
    line_length = 20
    for c in contours:
        if cv2.contourArea(c) >= min_area:
            M = cv2.moments(c)
            x = int(M['m10']/M['m00'])
            y = int(M['m01']/M['m00']) 
            cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2)
            cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2)

Getting more involved

This same pipeline can be used to automatically loop through threshold values so you don't have to guess and hardcode those values. Since the blobs all seem roughly the same size, you can loop through until all contours have roughly the same area. You could do this for e.g. by finding the median contour size, defining some percentage of that median size above and below that you'll allow, and checking if all the contours detected fit in those bounds.

Here's an animated gif of what I mean. Notice that the gif stops once the contours are separated:

Then you can simply find the centroids of those separated contours. Here's the code:

def process_and_detect(img_path):

    img = cv2.imread(img_path)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    for thresh_val in range(0, 255):

        # threshold and detect contours
        thresh = cv2.threshold(imgray, thresh_val, 255, cv2.THRESH_BINARY)[1]
        contours = cv2.findContours(thresh,
                                    cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[1]

        # filter contours by area
        min_area = 50
        filtered_contours = [c for c in contours
                             if cv2.contourArea(c) >= min_area]
        area_contours = [cv2.contourArea(c) for c in filtered_contours]

        # acceptable deviation from median contour area
        median_area = np.median(area_contours)
        dev = 0.3
        lowerb = median_area - dev*median_area
        upperb = median_area + dev*median_area

        # break when all contours are within deviation from median area
        if ((area_contours > lowerb) & (area_contours < upperb)).all():
            break

    # draw center location of blobs
    line_length = 8
    cross_color = (255, 0, 0)
    for c in filtered_contours:
        M = cv2.moments(c)
        x = int(M['m10']/M['m00'])
        y = int(M['m01']/M['m00'])
        cv2.line(img, (x-line_length, y), (x+line_length, y), cross_color, 2)
        cv2.line(img, (x, y-line_length), (x, y+line_length), cross_color, 2)

Note that here I looped through all possible threshold values with range(0, 255) to give 0, 1, ..., 254 but really you could start higher and skip through a few values at a time with, say, range(50, 200, 5) to get 50, 55, ..., 195 which would of course be much faster.

这篇关于如何找到这些有时重叠的圆圈的中心的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆