【问题标题】:How to detect a precise rectangle in a photo opencv如何在照片opencv中检测精确的矩形
【发布时间】:2021-06-05 04:04:12
【问题描述】:

我使用这段代码来检测照片上的矩形,起初它运行良好,直到我意识到我会在中间有一个也是正方形的对象:

问题:

如何正确检测第一张结果图片上的 4 个角,而不检测正方形中间物体的角。非常感谢。

代码:

import numpy as np
import cv2


img = cv2.imread('Photos/lastBoard.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 100, 200)

corners = cv2.goodFeaturesToTrack(gray, 25, 0.01, 50)

corner_list = []
for corner in corners:
    x, y = corner.ravel()

    if(y < 700 and (50 < x < 800 )):
        corner_list.append([int(x), int(y)])
        cv2.circle(img, (x, y), 5, (36, 255, 12), -1)

cv2.imshow("yo", img)


cv2.waitKey(0)

【问题讨论】:

    标签: python opencv


    【解决方案1】:

    伙计,你没有使用我们在你的last question 中介绍的技术和处理,这让我心碎。您已经有很多可以重复使用的功能。您尝试分割的矩形有一个独特的颜色(一种绿色),并且有一个定义的区域纵横比!看看你桌子上所有的东西,它们比长方形还小!另外,矩形几乎是正方形!这意味着它的纵横比接近1.0。如果你以某种方式分割矩形,近似它的角应该是相对容易的。

    这是很有价值的信息,因为它可以让您跟踪您的行动计划。我看到你正在使用cv2.goodFeaturesToTrack 来检测所有东西的角落。没关系,但可以简化。我提出了一个与上次非常相似的行动计划:

    1. 尝试使用颜色分割矩形,让我们计算一个 基于HSV的掩码
    2. 让我们使用区域过滤器和一些形态学来清除面具的噪音
    3. 找到contours - 我们正在寻找最大的绿色contour,即矩形。
    4. 感兴趣的轮廓已定义特征。使用areaaspect ratio 过滤垃圾轮廓。
    5. 获得感兴趣的轮廓/斑点后,近似其角。

    让我们看看代码:

    # 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(这与我们上次看到的功能完全相同)然后使用closingdilate 后跟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)
    

    这是过滤后的蒙版,噪点基本没了:

    现在,让我们像上次一样根据areaaspect 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
    

    【讨论】:

    • 我应该考虑一下哈哈,我有一个问题回到上次的冰球问题,解决方案似乎很大程度上取决于灯光的状况(颜色)。你认为 cv2.matchTemplate 是一个更强大的冰球颜色问题的解决方案吗?谢谢:D
    • 当您需要匹配形状时,@codejourney 模板匹配通常很有帮助。也许您可以使用它,但这在很大程度上取决于您尝试匹配的形状(在这种情况下,圆圈 - 以及如何从分段蒙版中获得这些圆圈)及其大小(形状必须大致相同) .您可能需要包括一种更强大的分割技术来处理光线变化。也许您可以标准化整个帧的光照条件尝试对比度均衡或伽马校正。我建议研究该主题。祝你好运,伙计。
    • 如果可能的话,最后一个问题很酷哈哈,如果我想要正方形的内角,这个代码也可以吗? (我不确定要调整什么)我尝试了区域但没有运气谢谢
    • @codejourney 您需要添加更多代码来获取内角。在这种情况下,矩形没有完全分割——由于闪电噪声,顶部有一个间隙,所以你得到一个轮廓而不是两个(即内轮廓和外轮廓),所以解决方案并不简单。获得内角的一种(肮脏)方法可能是裁剪分段的二进制掩码(使用您已经获得的坐标),反转掩码并再次寻找轮廓 - 现在这将为您提供内轮廓。然后,像以前一样获取边界矩形和近似角。
    猜你喜欢
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    相关资源
    最近更新 更多