伙计,你没有使用我们在你的last question 中介绍的技术和处理,这让我心碎。您已经有很多可以重复使用的功能。您尝试分割的矩形有一个独特的颜色(一种绿色),并且有一个定义的区域和纵横比!看看你桌子上所有的东西,它们比长方形还小!另外,矩形几乎是正方形!这意味着它的纵横比接近1.0。如果你以某种方式分割矩形,近似它的角应该是相对容易的。
这是很有价值的信息,因为它可以让您跟踪您的行动计划。我看到你正在使用cv2.goodFeaturesToTrack 来检测所有东西的角落。没关系,但可以简化。我提出了一个与上次非常相似的行动计划:
- 尝试使用颜色分割矩形,让我们计算一个
基于
HSV的掩码
- 让我们使用区域过滤器和一些形态学来清除面具的噪音
- 找到
contours - 我们正在寻找最大的绿色contour,即矩形。
-
感兴趣的轮廓已定义特征。使用
area 和aspect ratio 过滤垃圾轮廓。
- 获得感兴趣的轮廓/斑点后,近似其角。
让我们看看代码:
# imports:
import numpy as np
import cv2
# image path
path = "D://opencvImages//"
fileName = "table1.jpg"
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
inputCopy = inputImage.copy()
# The HSV mask values:
lowerValues = np.array([58, 151, 25])
upperValues = np.array([86, 255, 75])
# Convert the image to HSV:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
# Create the HSV mask
mask = cv2.inRange(hsvImage, lowerValues, upperValues)
第一步旨在创建HSV 掩码。与上次非常相似,我已经定义了HSV 感兴趣的范围,并应用了与以前完全相同的东西。稍后您可以(并且应该)探索更多奇特的技术,但让我们暂时坚持我们所知道的有效方法,因为该项目肯定很快就会到期。结果如下:
你看到面具已经很漂亮了吗?只有绿色圆球和矩形在thresholding 中幸存下来。矩形不完整并不重要,因为我们要用bounding rectangle 来近似它的轮廓!好吧,让我们把这个坏男孩清理得更好一点。使用filterArea(这与我们上次看到的功能完全相同)然后使用closing(dilate 后跟erode)只是为了得到一个漂亮的面具:
# Run a minimum area filter:
minArea = 50
mask = areaFilter(minArea, mask)
# Pre-process mask:
kernelSize = 3
structuringElement = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
iterations = 2
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, structuringElement, None, None, iterations, cv2.BORDER_REFLECT101)
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, structuringElement, None, None, iterations, cv2.BORDER_REFLECT101)
这是过滤后的蒙版,噪点基本没了:
现在,让我们像上次一样根据area 和aspect ratio 找到contours 和过滤。然而,参数是不同的,因为我们的目标不是弹拨,而是矩形:
# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# Store the poly approximation and bound
contoursPoly = [None] * len(contours)
# Store the corners of the square here:
detectedCorners = []
# Look for the outer bounding boxes:
for _, c in enumerate(contours):
# Approximate the contour to a polygon:
contoursPoly = cv2.approxPolyDP(c, 3, True)
# Convert the polygon to a bounding rectangle:
boundRect = cv2.boundingRect(contoursPoly)
# Get the bounding rect's data:
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
# Calculate the rect's area:
rectArea = rectWidth * rectHeight
# Calculate the aspect ratio:
aspectRatio = rectWidth / rectHeight
delta = abs(1.0 - aspectRatio)
# Set the min threshold values to identify the
# blob of interest:
minArea = 2500
epsilon = 0.2
好的,到目前为止一切顺利,我希望。如您所见,我将轮廓近似为 4 顶点多边形,然后计算其bounding rectangle。这个近似值应该非常适合我们感兴趣的 blob。现在,应用轮廓过滤器并使用边界矩形数据来近似角。我一个一个地近似每个角落,并将它们存储在
detectedCorners 数组。然后,我们可以绘制它们。在这里,仍然在 for 循环内:
# Is this bounding rectangle we
# are looking for?
if rectArea > minArea and delta < epsilon:
# Compute the corners/vertices:
# Corner 1 (top left)
corner1 = (rectX, rectY)
detectedCorners.append(corner1)
# Corner 2 (top right)
corner2 = (rectX + rectWidth, rectY)
detectedCorners.append(corner2)
# Corner 3 (bottom left)
corner3 = (rectX, rectY + rectHeight)
detectedCorners.append(corner3)
# Corner 4 (bottom right)
corner4 = (rectX + rectWidth, rectY + rectHeight)
detectedCorners.append(corner4)
# Draw the corner points:
for p in detectedCorners:
color = (0, 0, 255)
cv2.circle(inputCopy, (p[0], p[1]), 5, color, -1)
cv2.imshow("Square Corners", inputCopy)
cv2.waitKey(0)
这是两张图片的结果。近似的角是红点:
下面是areaFilter函数的定义和实现:
def areaFilter(minArea, inputImage):
# Perform an area filter on the binary blobs:
componentsNumber, labeledImage, componentStats, componentCentroids = \
cv2.connectedComponentsWithStats(inputImage, connectivity=4)
# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]
# Filter the labeled pixels based on the remaining labels,
# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')
return filteredImage