Ham*_*msi 2 opencv computer-vision python-3.x canny-operator
我是 OpenCV 新手,问题是我需要获取所有轮廓点。在 findContours 方法中设置 cv2.RETR_TREE 模式很容易。问题是这样会返回多余的坐标。因此,例如,在这个多边形中,我不想得到这样的轮廓点:
因此,根据第一张图像,绿色是使用 RETR_TREE 模式检测到的轮廓,而点 1-2、3-5、4-6... 是多余的,因为它们彼此非常接近。我需要将这些冗余点组合成一个,并将其附加到 customContours 数组中。目前,我只有第一张图片的代码,设置点之间的距离和点坐标:
def getContours(img, minArea=20000, cThr=[100, 100]):
font = cv2.FONT_HERSHEY_COMPLEX
imgColor = img
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
imgCanny = cv2.Canny(imgBlur, cThr[0], cThr[1])
kernel = np.ones((5, 5))
imgDial = cv2.dilate(imgCanny, kernel, iterations=3)
imgThre = cv2.erode(imgDial, kernel, iterations=2)
cv2.imshow('threshold', imgThre)
contours, hierachy = cv2.findContours(imgThre, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
customContours = []
for cnt in contours:
area = cv2.contourArea(cnt)
if area > minArea:
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.009*peri, True)
bbox = cv2.boundingRect(approx)
customContours.append([len(approx), area, approx, bbox, cnt])
print('points: ', len(approx))
n = approx.ravel()
i = 0
for j in n:
if i % 2 == 0:
x = n[i]
y = n[i + 1]
string = str(x)+" " + str(y)
cv2.putText(imgColor, str(i//2+1) + ': ' + string, (x, y), font, 2, (0, 0, 0), 2)
i = i + 1
customContours = sorted(customContours, key=lambda x: x[1], reverse=True)
for cnt in customContours:
cv2.drawContours(imgColor, [cnt[2]], 0, (0, 0, 255), 5)
return imgColor, customContours
Run Code Online (Sandbox Code Playgroud)
您能帮我了解第二张图片的真正要点吗?
(编辑 01/07/21 )
我想要一个通用的解决方案,因为图像可能更复杂,如下图:
注意:请注意,中间的箭头(点 17 和 18)没有封闭区域,因此不是要研究的多边形。那么,那个地区就没有兴趣获得他的积分了。另外,请注意,点的顺序并不重要,但如果条目是孔图像,则它应该知道有 4 个多边形,因此对于每个多边形点,从 0 开始,然后是 1,依此类推。
这是我的方法。它主要是基于形态学的。它涉及使用特殊内核对图像进行卷积。该卷积识别三角形的端点以及中线所在的交点。这将产生一个点蒙版,其中包含与您要查找的点相匹配的像素。之后,我们可以应用一点形态学来连接可能的重复点。剩下的就是获取这些点的坐标列表以进行进一步处理。
这些是步骤:
这是代码:
# Imports:
import numpy as np
import cv2
# image path
path = "D://opencvImages//"
fileName = "triangle.png"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Prepare a deep copy for results:
inputImageCopy = inputImage.copy()
# Convert BGR to Grayscale
grayImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Threshold via Otsu:
_, binaryImage = cv2.threshold(grayImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
Run Code Online (Sandbox Code Playgroud)
第一位计算二值图像。非常简单。我使用这张图片作为基础,它只是您发布的内容的清理版本,没有注释。这是生成的二值图像:
现在,要执行卷积,我们必须首先获得图像“骨架”。骨架是二值图像的一个版本,其中线条已标准化为宽度为1 pixel。这很有用,因为我们可以将图像与3 x 3内核进行卷积并寻找特定的像素模式。让我们使用 OpenCV 的扩展图像处理模块来计算骨架:
# Get image skeleton:
skeleton = cv2.ximgproc.thinning(binaryImage, None, 1)
Run Code Online (Sandbox Code Playgroud)
这是获得的图像:
我们现在可以应用卷积。该方法基于Mark Setchell在这篇文章中提供的信息。这篇文章主要展示了查找形状端点的方法,但我将其扩展为还可以识别线相交点,例如三角形的中间部分。主要思想是,卷积产生一个非常具体的值,其中在输入图像中找到黑白像素的图案。请参阅该帖子了解此想法背后的理论,但在这里,我们正在寻找两个值:110和40。第一个发生在找到终点时。第二个是当发现一条线相交时。让我们设置卷积:
# Threshold the image so that white pixels get a value of 0 and
# black pixels a value of 10:
_, binaryImage = cv2.threshold(skeleton, 128, 10, cv2.THRESH_BINARY)
# Set the convolution kernel:
h = np.array([[1, 1, 1],
[1, 10, 1],
[1, 1, 1]])
# Convolve the image with the kernel:
imgFiltered = cv2.filter2D(binaryImage, -1, h)
# Create list of thresholds:
thresh = [110, 40]
Run Code Online (Sandbox Code Playgroud)
第一部分已经完成。我们将分两个单独的步骤检测端点和交叉点。每一步都会产生一个部分结果,我们可以通过OR这两个结果得到最终的掩码:
# Prepare the final mask of points:
(height, width) = binaryImage.shape
pointsMask = np.zeros((height, width, 1), np.uint8)
# Perform convolution and create points mask:
for t in range(len(thresh)):
# Get current threshold:
currentThresh = thresh[t]
# Locate the threshold in the filtered image:
tempMat = np.where(imgFiltered == currentThresh, 255, 0)
# Convert and shape the image to a uint8 height x width x channels
# numpy array:
tempMat = tempMat.astype(np.uint8)
tempMat = tempMat.reshape(height,width,1)
# Accumulate mask:
pointsMask = cv2.bitwise_or(pointsMask, tempMat)
Run Code Online (Sandbox Code Playgroud)
这是点的最终掩码:
请注意,白色像素是与我们的目标图案匹配的位置。这些就是我们正在寻找的点。由于形状不是完美的三角形,因此某些点可能会重复。我们可以通过应用形态膨胀来“合并”相邻的斑点:
# Set kernel (structuring element) size:
kernelSize = 7
# Set operation iterations:
opIterations = 3
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform Dilate:
morphoImage = cv2.morphologyEx(pointsMask, cv2.MORPH_DILATE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101)
Run Code Online (Sandbox Code Playgroud)
这是结果:
非常好,我们现在有了大的像素簇(或斑点)。要获取它们的坐标,一种可能的方法是获取这些轮廓的边界矩形并计算它们的质心:
# Look for the outer contours (no children):
contours, _ = cv2.findContours(morphoImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Store the points here:
pointsList = []
# Loop through the contours:
for i, c in enumerate(contours):
# Get the contours bounding rectangle:
boundRect = cv2.boundingRect(c)
# Get the centroid of the rectangle:
cx = int(boundRect[0] + 0.5 * boundRect[2])
cy = int(boundRect[1] + 0.5 * boundRect[3])
# Store centroid into list:
pointsList.append( (cx,cy) )
# Set centroid circle and text:
color = (0, 0, 255)
cv2.circle(inputImageCopy, (cx, cy), 3, color, -1)
font = cv2.FONT_HERSHEY_COMPLEX
string = str(cx) + ", " + str(cy)
cv2.putText(inputImageCopy, str(i) + ':' + string, (cx, cy), font, 0.5, (255, 0, 0), 1)
# Show image:
cv2.imshow("Circles", inputImageCopy)
cv2.waitKey(0)
Run Code Online (Sandbox Code Playgroud)
这些是位于原始输入中的点:
另请注意,我已将它们的坐标存储在pointsList列表中:
# Print the list of points:
print(pointsList)
Run Code Online (Sandbox Code Playgroud)
这将质心打印为元组(centroidX, centroidY):
[(717, 971), (22, 960), (183, 587), (568, 586), (388, 98)]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1352 次 |
| 最近记录: |