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

时间:2017-03-10 14:50:52

标签: python python-2.7 opencv

我正试图让OpenCV在图像中检测到一张床。我正在运行通常的灰度,模糊,Canny,我尝试过Convex Hull。然而,由于存在相当多的“噪音”,这会产生额外的轮廓并且会弄乱物体检测。因此,我无法正确检测到床。

这是输入图像以及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)

由于床的轮廓没有关闭,我不能适合矩形,也不能检测最大面积的轮廓。

我能想到的解决方案是使用轮廓点推断最大可能的矩形,以期弥合这个小间隙,但由于矩形不完整,我不太清楚如何继续。

1 个答案:

答案 0 :(得分:6)

由于您还没有提供任何其他示例,我提供了一个处理此案例的算法。但请记住,您必须找到适应它的方法,然而其他样品的光线和背景会发生变化。

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

我的结果:

enter image description here

代码:

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)