微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

cv2.findContours 在黑色背景上找不到白色矩形

如何解决cv2.findContours 在黑色背景上找不到白色矩形

我是一名经验丰富的程序员,但 Python 和 OpenCV 经验有限。我正在裁剪电影帧,尝试使用电影的可见穿孔和边缘作为参考。附加的是单个帧(原始 1920 x 1080)和从该帧派生的掩码被传递给下面代码中的 findcountours。 FindContours 找到 3 个轮廓,但只有最大的(右框架边界)是正确的,其他两个(驱动器穿孔)没有被正确检测到。你们中的一位能不能告诉我我做错了什么,并指出我向善的方向?

我使用 Python 3.8 和 cv2 的关联版本。

这是使用下面的代码派生的 sample framemask

谢谢

克里斯

for file in args.files:

idx += 1

if os.path.isfile(file):

    # load the input image (whose path was supplied via command line
    # argument) and display the image to our screen
    image = cv2.imread(file)

    if image is None:
        print('Could not open or find the image: ',args["image"])
        fileinput.close()
        exit(0)

    image_clone = image.copy()
    image_height = image.shape[0]
    image_width = image.shape[1]



    """
    Threshold image
    """

    hsv = cv2.cvtColor(image.copy(),cv2.COLOR_BGR2HSV)
    #
    # # define range of white color in HSV
    # # change it according to your need !
    lower_white = np.array([0,235],dtype=np.uint8)
    upper_white = np.array([0,255],dtype=np.uint8)
    #
    # Threshold the HSV image to get only white colors
    mask = cv2.inRange(hsv,lower_white,upper_white)

    mask = cv2.copyMakeBorder(mask,10,cv2.BORDER_CONSTANT,value=[0,0])

    # I have seen some spurIoUs pixels so I filter
    
    se1 = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
    mask = cv2.morphologyEx(mask,cv2.MORPH_OPEN,se1,iterations = 5)

    # Make sure I get rid of noise
    mask = cv2.GaussianBlur(mask,5),0)

    cny = cv2.Canny(mask.copy(),255/3,255)

    mask = cv2.bitwise_xor(mask,cny)

    cv2.imwrite(os.path.splitext(file)[0] + '_mask_' + str(idx) + '.tif',mask)

    dsize = (int(mask.shape[1] * (50 / 100)),int(mask.shape[0] * (50 / 100)))
    # # # # # #
    cv2.imshow("Mask Image",cv2.resize(mask.copy(),dsize))
    cv2.waitKey(0)
    cv2.destroyWindow("Mask Image")

    contours,_ = cv2.findContours(mask,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours,key=cv2.contourArea,reverse=True)
    #
    new_image = np.zeros((mask.shape[0],mask.shape[1]),np.uint8) * 0
    #
    cv2.drawContours(new_image,[max(contours,key=cv2.contourArea)],-1,255,thickness=-1)

    dsize = (int(new_image.shape[1] * (50 / 100)),int(new_image.shape[0] * (50 / 100)))
    # # # # # # #
    cv2.imshow("Mask Image",cv2.resize(new_image.copy(),dsize))
    cv2.waitKey(0)
    cv2.destroyWindow("Mask Image")

解决方法

问题在于函数 drawContours 的参数。第二个参数包含应该绘制的 contours ,但您似乎正在过滤较小的轮廓并仅存储/绘制最大的轮廓,在这种情况下,是框架右侧的轮廓。不要过滤您的 contours。尝试像这样计算它们:

# Find the contours on the mask image:
contours,hierarchy = cv2.findContours(mask,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

# Draw the contours on the mask image:
cv2.drawContours(maskCopy,contours,-1,(0,255,0),3)

注意第三个参数是-1,这意味着绘制所有找到的轮廓。这就是你得到的:

此外,您可能希望将这些轮廓转换为 bounding rectangles,以便轻松裁剪感兴趣的区域。这是将轮廓转换为 4-vertex polygon 的方法:

contoursPoly = [None] * len(contours)
boundRect = []

# Contour to Bounding Rects:
for i,c in enumerate(contours):
   # Approximate each contour to a bonding rectangle:
   contoursPoly[i] = cv2.approxPolyDP(c,3,True)
   # Append each bounding rect to the list:
   boundRect.append(cv2.boundingRect(contours_poly[i]))

# Draw the Bounding Rects:
rectColor = (0,0)
for i in range(len(boundRect)):    
    cv2.rectangle(maskCopy,(int(boundRect[i][0]),int(boundRect[i][1])),(int(boundRect[i][0] + boundRect[i][2]),int(boundRect[i][1] + boundRect[i][3])),rectColor,5)

现在注意轮廓如何近似为矩形:

现在,您使用一些 morphology 来获得干净的二进制掩码。这实际上非常好,但形态学操作稍微扭曲了掩码的斑点,引入了位置偏移。查看覆盖在原始图像中的边界矩形:

看到位置偏移了吗?您可以尝试应用额外的 morphology 来恢复 blob 的原始位置。

第一种选择:

不要使用形态学,只考虑外轮廓。外轮廓是没有子(内)轮廓的父轮廓。这将过滤掉您可能有的任何可能的噪音:

# BGR to HSV conversion:
hsv = cv2.cvtColor(inputImage.copy(),cv2.COLOR_BGR2HSV)

# HSV Range definition:
lower_white = np.array([0,235],dtype=np.uint8)
upper_white = np.array([0,255],dtype=np.uint8)

# Get the HSV mask:
mask = cv2.inRange(hsv,lower_white,upper_white)

# Find the big contours/blobs on the filtered image:
contours,cv2.CHAIN_APPROX_SIMPLE)

contoursPoly = [None] * len(contours)
boundRect = []

# Alright,just look for the outer bounding boxes:
for i,c in enumerate(contours):
    if hierarchy[0][i][3] == -1:
        contoursPoly[i] = cv2.approxPolyDP(c,True)
        boundRect.append(cv2.boundingRect(contoursPoly[i]))

# Draw the bounding boxes on the (copied) input image:
for i in range(len(boundRect)):
    color = (0,0)
    cv2.rectangle(inputCopy,color,5)

你明白了:

第二种选择:

使用 reduce 函数将二值掩码缩减为 1 x columns 矩阵,检测轮廓并提取内部坐标以裁剪框架:

# Reduce matrix to a 1 row x n columns matrix:
reducedMask = cv2.reduce(mask,cv2.REDUCE_MAX)

reduce 函数将创建一个 1 x columns 矩阵(实际上是一个 vector),其中包含所有列的 MAX 值。二进制掩码中的 MAX 值 (255) 对应于感兴趣的斑点的位置,因此,将图像缩小到这个新掩码:

现在的想法是获取两个白色部分的坐标。这可以通过一种以上的方法来实现。现在让我们坚持寻找轮廓:

# Find the big contours/blobs on the filtered image:
contours,hierarchy = cv2.findContours(reducedMask,True)
        boundRect.append(cv2.boundingRect(contoursPoly[i]))

到目前为止获得的轮廓描述了两个白色部分。我们需要这些线的内部坐标。让我们从最小到最大 sort 绑定矩形列表。列表是一个 tuple,每个 tuple 的第一个元素是 top left(或水平)坐标,所以我们有排序列表后的第一个白色部分:

# Sort rects from smallets to largest:
sortedRectangles = sorted(boundRect,key = lambda topLeft: topLeft[0])

# Set Top Left crop coordinate:
startX = sortedRectangles[0][0] + sortedRectangles[0][2]

# Set Lower Right crop coordinate:
endX = sortedRectangles[1][0]

现在,只需使用此信息裁剪图像:

# Crop the roi:
imageSize = inputCopy.shape
imageHeight = imageSize[0]
croppedImg = inputCopy[0:0+imageHeight,startX:endX]

你会得到最终的裁剪图像:

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?