如何在不影响内部轮廓的情况下从图像中去除外部空心轮廓

时间:2019-10-18 21:23:03

标签: python image opencv image-processing computer-vision

这是problem在不同方法下的延续。

我想摆脱盒子及其线条外的所有东西,并保持盒子内部的一切不变。或至少只有盒子。

盒子:

img

做一些魔术后,我希望它看起来像什么

img

这是它的裁剪来源:

img

如果您在黑色背景上查看第一张图像,您会注意到在框形字段之外还有一个白色带。可以通过获取其边界框的统计信息并在其中添加填充来将其裁剪为更大或更小。

这是天真的,也是我的第一个方法。这里的问题是:

  • 在不影响内部字符的情况下,不能过多考虑不同的行宽。假设您输入-9作为填充。在线宽约为9像素的盒子上,它几乎可以很好地工作,但是里面的任何东西都超过9像素。这会导致一些剩余像素,最终影响应用程序的准确性。
  • 另一方面,线宽明显小于9的任何带框字段可能也会破坏其中的字符。

我的第二次尝试是removing the contour,戴上面具。但是,结果没有达到预期。

提取轮廓的代码非常长,因此假设stats包含从contours返回的contours, _ = cv2.findContours

# loop in each contour in stats
for i in range(len(stats)):
    # get the stats of the bounding rectangle 
    x, y, w, h = cv2.boundingRect(stats[i])

    # draw the field contour into the mask
    cv2.drawContours(mask, [stats[i]], -1, 0, -1)

    # remove the contour from the original image
    section = cv2.bitwise_and(section, section, mask=mask)

    # crop the boxed field
    field = crop_by_origin(x, y, w, h, padding=p)

这就是我得到的:

img

我不明白为什么它不起作用?也许是因为该站点中的示例位于黑色背景上?也许不适用于“透明”轮廓?这有可能吗?

如何解决此问题?这里还有其他可能的解决方案吗?

更新

我用@nathancy的答案尝试了另一张图片,这就是结果:

img

结果:

img

我尝试使用水平线内核,但是没有按预期进行,有没有办法使它更具动态性?

1 个答案:

答案 0 :(得分:2)

此问题可以分为两个单独的步骤。首先,我们要隔离矩形,这可以使用轮廓逼近+滤波完成。然后,使用从removing horizontal lines in image中的上一个答案借用的实现,删除水平和垂直线。这是一种整体方法:

  • 将图像转换为灰度和高斯模糊
  • 大津获取二进制图像的阈值
  • 查找轮廓并使用轮廓逼近法找到矩形框

    • 如果轮廓通过此滤镜和最小阈值区域,请使用Numpy切片将ROI复制粘贴到空白的白色蒙版上
  • 删除水平线

  • 删除垂直线
  • 按位与掩码与输入图像以获取结果

大津的阈值->检测到的盒子->绘制到蒙版->上的盒子倒了

删除水平线->删除垂直线->按位取-并得到结果

import cv2
import numpy as np

image = cv2.imread('1.png')
mask = np.ones(image.shape, dtype=np.uint8) * 255
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.05 * peri, True)
    if len(approx) == 4 and area > 500:
        x,y,w,h = cv2.boundingRect(approx)
        mask[y:y+h, x:x+w] = image[y:y+h, x:x+w]

# Remove horizontal lines
mask = cv2.cvtColor(255 - mask, cv2.COLOR_BGR2GRAY)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (50,2))
detect_horizontal = cv2.morphologyEx(mask, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(mask, [c], -1, (0,0,0), 6)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,45))
detect_vertical = cv2.morphologyEx(mask, cv2.MORPH_OPEN, vertical_kernel, iterations=1)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(mask, [c], -1, (0,0,0), 6)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=mask)
result[mask==0] = (255,255,255)

cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()