Python OpenCV - 从一组轮廓点中外推最大的矩形

7
我正在尝试让OpenCV在图像中检测床。我运行了常规的灰度、模糊、Canny操作,并尝试了凸包算法。然而,由于存在大量“噪音”,导致额外的轮廓和干扰物体检测。因此,我无法正确地检测床。
以下是输入图像以及Canny边缘检测结果:

Results

如您所见,已经接近完成。我已经有了床的轮廓,尽管右上角有一个间隙 - 这妨碍了我检测到一个封闭的矩形。
这是我正在运行的代码:
import cv2
import numpy as np

def contoursConvexHull(contours):
    print("contours length = ", len(contours))
    print("contours length of first item = ", len(contours[1]))
    pts = []
    for i in range(0, len(contours)):
        for j in range(0, len(contours[i])):
            pts.append(contours[i][j])

    pts = np.array(pts)

    result = cv2.convexHull(pts)

    print(len(result))
    return result

def auto_canny(image, sigma = 0.35):
    # compute the mediam of the single channel pixel intensities
    v = np.median(image)

    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) *v))
    edged = cv2.Canny(image, lower, upper)

    # return edged image
    return edged


# Get our image in color mode (1)
src = cv2.imread("bed_cv.jpg", 1)

# Convert the color from BGR to Gray
srcGray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# Use Gaussian Blur 
srcBlur = cv2.GaussianBlur(srcGray, (3, 3), 0)

# ret is the returned value, otsu is an image
##ret, otsu = cv2.threshold(srcBlur, 0, 255,
##                          cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Use canny
##srcCanny = cv2.Canny(srcBlur, ret, ret*2, 3)
srcCanny1 = auto_canny(srcBlur, 0.70)

# im is the output image
# contours is the contour list
# I forgot what hierarchy was
im, contours, hierarchy = cv2.findContours(srcCanny1,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)

##cv2.drawContours(src, contours, -1, (0, 255, 0), 3)

ConvexHullPoints = contoursConvexHull(contours)
##cv2.polylines(src, [ConvexHullPoints], True, (0, 0, 255), 3)

cv2.imshow("Source", src)
cv2.imshow("Canny1", srcCanny1)

cv2.waitKey(0)

由于床的轮廓不是封闭的,我无法适应矩形或检测具有最大面积的轮廓。

我能想到的解决方案是使用轮廓点推断出可能的最大矩形,希望填补这个小间隙,但由于矩形不完整,我不太确定如何继续。


你尝试过在边缘检测图像上进行线检测吗?我也做了同样的事情,但结果不太准确。 - Jeru Luke
我们还在考虑哪些其他用例?床会改变颜色吗?未来数据会带来什么限制? - Michał Gacka
@JeruLuke 我还没有,因为我不太确定如何处理我即将收到的代码行。 - Razgriz
@m3h0w 床的颜色没有改变。我看到未来的限制是在床周围添加物体。 - Razgriz
1个回答

13

鉴于您没有提供其他示例,我提供一个适用于此情况的算法。但请记住,您需要找到适应其他样本中光线和背景变化的方法。

由于存在大量噪声和相对较高的动态范围,建议不要使用Canny算法,而是使用自适应阈值和Find Contours(它不需要边缘作为输入),这有助于选择不同部分图像的不同阈值。

我的结果:

输入图像描述

代码:

import cv2
import numpy as np

def clahe(img, clip_limit=2.0, grid_size=(8,8)):
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=grid_size)
    return clahe.apply(img)

src = cv2.imread("bed.png")

# HSV thresholding to get rid of as much background as possible
hsv = cv2.cvtColor(src.copy(), cv2.COLOR_BGR2HSV)
lower_blue = np.array([0, 0, 120])
upper_blue = np.array([180, 38, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
result = cv2.bitwise_and(src, src, mask=mask)
b, g, r = cv2.split(result)
g = clahe(g, 5, (3, 3))

# Adaptive Thresholding to isolate the bed
img_blur = cv2.blur(g, (9, 9))
img_th = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                               cv2.THRESH_BINARY, 51, 2)

im, contours, hierarchy = cv2.findContours(img_th,
                                           cv2.RETR_CCOMP,
                                           cv2.CHAIN_APPROX_SIMPLE)

# Filter the rectangle by choosing only the big ones
# and choose the brightest rectangle as the bed
max_brightness = 0
canvas = src.copy()
for cnt in contours:
    rect = cv2.boundingRect(cnt)
    x, y, w, h = rect
    if w*h > 40000:
        mask = np.zeros(src.shape, np.uint8)
        mask[y:y+h, x:x+w] = src[y:y+h, x:x+w]
        brightness = np.sum(mask)
        if brightness > max_brightness:
            brightest_rectangle = rect
            max_brightness = brightness
        cv2.imshow("mask", mask)
        cv2.waitKey(0)

x, y, w, h = brightest_rectangle
cv2.rectangle(canvas, (x, y), (x+w, y+h), (0, 255, 0), 1)
cv2.imshow("canvas", canvas)
cv2.imwrite("result.jpg", canvas)
cv2.waitKey(0)

1
你能把 Java 中的 for 循环逻辑转换一下吗?或者说如何编写以下代码: mask[y:y+h, x:x+w] = src[y:y+h, x:x+w] brightness = np.sum(mask) - YLS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接