如何检测圆形腐蚀/扩张

2024-05-14 00:42:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我想检测一条线上的圆形腐蚀和扩张。对于膨胀,我尝试递归地腐蚀图像,每次递归时,我检查宽度/高度纵横比。如果比值小于4,我假设它的轮廓是圆的,对于每一个这样的轮廓,我根据力矩和面积计算圆心和半径。这是检测圆形扩张的函数:

def detect_circular_dilations(img, contours):
    contours_current, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    if len(contours_current) == 0:
        return get_circles_from_contours(contours)
    for c in contours_current:
        x, y, w, h = cv2.boundingRect(c)
        if w > h:
            aspect_ratio = float(w) / h
        else:
            aspect_ratio = float(h) / w
        if aspect_ratio < 4 and w < 20 and h < 20 and w > 5 and h > 5:
            contours.append(c)
    return detect_circular_dilations(cv2.erode(img, None, iterations=1), contours)

我想检测的一个圆形扩张的例子如下:

Circular Dilation

我还没有解决的另一个问题是检测圆形腐蚀。循环侵蚀的例子如下:

Circular Erosion

这里我用红色矩形标记了我想检测的圆形腐蚀。可能有一些较小的圆形图案(在左边)不应该被视为实际的圆形侵蚀。在

有人知道什么是检测这种圆形物体的最佳方法吗?对于循环扩张,我将非常感谢任何意见/建议,以便潜在地使检测更加可靠。在

谢谢你!在


Tags: andimgreturnif圆形currentfloatcv2
2条回答

我将尝试用cv2.Canny()找到这条线的两条边并搜索轮廓。如果你的轮廓线是你的两条轮廓线,那么你的轮廓线就是你的轮廓线。然后,可以计算一条边上每个点到另一条边的最小距离。然后,你可以计算距离的中值,如果一个点的距离大于或小于中值(+-公差),那么这条线的扩张或腐蚀就会增加到一个列表中。如果需要,您可以通过列出列表来分类噪音,如果这些点不是连续的(在x轴上),则可以删除这些点。在

下面是一个简单的例子:

import cv2
import numpy as np
from scipy import spatial

def detect_dilation(median, mindist, tolerance):
    count = 0
    for i in mindist:
        if i > median + tolerance:
            dilate.append((reshape_e1[count][0], reshape_e1[count][1]))
        elif i < median - tolerance:
            erode.append((reshape_e1[count][0], reshape_e1[count][1]))
        else:
            pass
        count+=1

def other_axis(dilate, cnt):
    temp = []
    for i in dilate:
        temp.append(i[0])
    for i in cnt:
        if i[0] in temp:
            dilate.append((i[0],i[1]))

img = cv2.imread('1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,100,200)
_, contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
contours.sort(key= lambda cnt :cv2.boundingRect(cnt)[3])
edge_1 = contours[0]
edge_2 = contours[1]
reshape_e1 = np.reshape(edge_1, (-1,2))
reshape_e2 =np.reshape(edge_2, (-1,2))
tree = spatial.cKDTree(reshape_e2)
mindist, minid = tree.query(reshape_e1)
median = np.median(mindist)
dilate = []
erode = []
detect_dilation(median,mindist,5)
other_axis(dilate, reshape_e2)
other_axis(erode, reshape_e2)

dilate = np.array(dilate).reshape((-1,1,2)).astype(np.int32)
erode = np.array(erode).reshape((-1,1,2)).astype(np.int32)
x,y,w,h = cv2.boundingRect(dilate)
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
x,y,w,h = cv2.boundingRect(erode)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)

cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

enter image description here

编辑:

如果图片中有一条线断了(这意味着更多的轮廓线),你必须将每个轮廓线视为一条单独的线。您可以通过在cv2.boundingRect()的帮助下创建一个感兴趣的区域来实现这一点。但是当我尝试用新上传的图片时,这个过程不是很健壮,因为你必须改变容差来获得期望的结果。因为我不知道其他图像是什么样子,你可能需要一个更好的方法来获得平均距离和公差系数。不管怎样,这里都是我描述的一个例子(15代表容忍度):

^{pr2}$

结果:

enter image description here

像这样的问题通常用距离变换和中轴变换来解决。这些在某种程度上是相关的,因为中轴沿着距离变换的脊线运行。总体思路是:

  1. 计算图像的距离变换(对于每个前景像素,返回到最近的背景像素的距离;有些库以另一种方式实现此操作,在这种情况下,需要计算反转图像的距离变换)。

  2. 计算中轴(或骨架)。

  3. 沿中轴的距离变换值是相关值,我们忽略所有其他像素。这里我们看到了线的局部半径。

  4. 局部极大值是扩张的质心。使用一个阈值来确定哪些是重要的膨胀,哪些不是(一个嘈杂的轮廓会导致许多局部极大值)。

  5. 局部极小是腐蚀的质心。

例如,我使用下面的MATLAB代码得到以下输出。在

detected dilations

这是我使用的代码。它使用MATLAB和DIPimage 3,只是作为原理的快速证明。使用您喜欢使用的任何图像处理库,都可以很容易地将其转换为Python。在

% Read in image and remove the red markup:
img = readim('https://i.stack.imgur.com/bNOTn.jpg');
img = img{3}>100;
img = closing(img,5);

% This is the algorithm described above:
img = fillholes(img);               % Get rid of holes
radius = dt(img);                   % Distance transform
m = bskeleton(img);                 % Medial axis
radius(~m) = 0;                     % Ignore all pixels outside the medial axis
detection = dilation(radius,25)==radius & radius>25; % Local maxima with radius > 25
pos = findcoord(detection);         % Coordinates of detections
radius = double(radius(detection)); % Radii of detections

% This is just to make the markup:
detection = newim(img,'bin');
for ii=1:numel(radius)
   detection = drawshape(detection,2*radius(ii),pos(ii,:),'disk');
end
overlay(img,detection)

相关问题 更多 >