使用Open CV遮罩水平和垂直线

时间:2018-07-17 11:52:02

标签: python python-3.x opencv computer-vision

我正在尝试删除此图像中的水平和垂直线条,以使文本区域更加鲜明。

enter image description here

我正在使用下面的代码,该代码遵循guide

image = cv2.imread('image.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(
                    blurred, 255,
                    cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV,
                    25,
                    15
                )
# Create the images that will use to extract the horizontal and vertical lines
horizontal = np.copy(thresh)
vertical = np.copy(thresh)

# Specify size on horizontal axis
cols = horizontal.shape[1]
horizontal_size = math.ceil(cols / 20)

# Create structure element for extracting horizontal lines through morphology operations
horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))

# Apply morphology operations
horizontal = cv2.erode(horizontal, horizontalStructure)
horizontal = cv2.dilate(horizontal, horizontalStructure)

# Show extracted horizontal lines
cv2.imwrite("horizontal.jpg", horizontal)

# Specify size on vertical axis
rows = vertical.shape[0]
verticalsize = math.ceil(rows / 20)

# Create structure element for extracting vertical lines through morphology operations
verticalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (1, verticalsize))

# Apply morphology operations
vertical = cv2.erode(vertical, verticalStructure)
vertical = cv2.dilate(vertical, verticalStructure)

此后,我知道我需要隔离线条并用白线遮盖原始图像,但是我不确定如何继续。

有人有什么建议吗?

5 个答案:

答案 0 :(得分:3)

Jeru's answer已经为您提供了想要的东西。但是我想添加一个替代方法,它可能比您目前所拥有的更普遍。

您正在将彩色图像转换为灰度值,然后应用自适应阈值以尝试找到线条。您可以对此进行过滤,以仅获得较长的水平和垂直线,然后使用该蒙版在这些位置将原始图像绘制为白色。

在这里,我们查找所有线条,并将其从图像中删除,从而使用周围的颜色绘画它们。此过程完全不涉及阈值处理,所有形态学操作都应用于彩色图像的通道。

理想情况下,我们将使用颜色形态,但是这种形态的实现很少。数学形态学基于最大和最小运算,并且颜色三元组(即向量)的最大或最小定义不明确。

因此,我们改为将以下过程分别应用于三个颜色通道。这应该会产生对这个应用程序足够好的结果:

  1. 提取红色通道:拍摄input RGB图像,然后提取第一个通道。这是灰度图像。我们将此图像称为channel

  2. 应用大礼帽过滤器来检测薄结构::对channel施加带有小结构元素(SE)的闭合与{{1 }}(闭合是一个扩张,其后是具有相同SE的侵蚀,您也可以使用它来查找线)。我们将此输出称为channelthin。此步骤类似于您的本地阈值,但未应用实际阈值。所得强度表明线的黑暗程度。背景。如果将thin = closing(channel)-channel添加到thin,则将填写这些精简结构。 SE的大小在这里决定了什么被认为是“薄”。

  3. 过滤掉短行,只保留长行:将水平长SE的开口应用于channel,将垂直长SE的开口应用于thin,并取两个结果中的最大值。我们将其称为thin。请注意,这与生成lineshorizontal所用的过程相同。我们没有按照Jeru的建议将它们加在一起,而是采用了最大值。这样一来,输出强度仍与vertical中的对比度匹配。 (按照数学形态学的说法,开口的最大值是开口)。 SE的长度在这里决定了足以成为一条线的长度。

  4. 填充原始图像通道中的行:现在,只需将channel添加到lines。将结果写入输出图像的第一个通道。

  5. 与其他两个渠道重复相同的过程。

使用PyDIP这是一个非常简单的脚本:

channel

编辑:

当将第一个SE的大小增加到大于5时,如果大小足够大,可以同时去除示例图像中间的较粗的灰色条,则会导致包含反向文本“ POWERLIFTING”的块的一部分留在import PyDIP as dip input = dip.ImageReadTIFF('/home/cris/tmp/T4tbM.tif') output = input.Copy() for ii in range(0,3): channel = output.TensorElement(ii) thin = dip.Closing(channel, dip.SE(5, 'rectangular')) - channel vertical = dip.Opening(thin, dip.SE([100,1], 'rectangular')) horizontal = dip.Opening(thin, dip.SE([1,100], 'rectangular')) lines = dip.Supremum(vertical,horizontal) channel += lines # overwrites output image 中。

同样要过滤掉这些部分,我们可以如下更改thin的定义:

thin

也就是说,我们执行notthin = dip.Closing(channel, dip.SE(11, 'rectangular'), ["add max"])) notthin = dip.MorphologicalReconstruction(notthin, channel, 1, "erosion") thin = notthin - channel 而不是thin=closing(channel)-channel。重建只是扩展选定的(不是薄的)结构,以便在选择结构的一部分的地方,现在选择整个结构。 thin=reconstruct(closing(channel))-channel中唯一存在的是未连接到较厚结构的线。

我还添加了thin作为边界条件-这导致闭合以白色扩大图像外部的区域,因此将图像边缘的线视为线。

output

答案 1 :(得分:2)

要在此处进行详细说明,请执行以下操作:

  • 首先,添加生成的图像verticalhorizontal。这将为您提供包含水平线和垂直线的图像。由于两个图像都是uint8类型(无符号的8位整数),将它们添加在一起将不会有问题:

res = vertical + horizontal

    最后,用原始的3通道图像遮盖上面获得的结果图像。可以使用cv2.bitwise_and

fin = cv2.bitwise_and(image, image, mask = cv2.bitwise_not(res))

答案 2 :(得分:1)

您想要这样的东西吗?

image = cv2.imread('image.jpg', cv2.IMREAD_UNCHANGED);

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

ret,binary = cv2.threshold(gray, 170, 255, cv2.THRESH_BINARY)#|cv2.THRESH_OTSU)

V = cv2.Sobel(binary, cv2.CV_8U, dx=1, dy=0)
H = cv2.Sobel(binary, cv2.CV_8U, dx=0, dy=1)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
V = cv2.morphologyEx(V, cv2.MORPH_DILATE, kernel, iterations = 2)
H = cv2.morphologyEx(H, cv2.MORPH_DILATE, kernel, iterations = 2)

rows,cols = image.shape[:2]

mask = np.zeros(image.shape[:2], dtype=np.uint8)

contours = cv2.findContours(V, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
for cnt in contours:
    (x,y,w,h) = cv2.boundingRect(cnt)
    # manipulate these values to change accuracy
    if h > rows/2 and w < 10:
        cv2.drawContours(mask, [cnt], -1, 255,-1)

contours = cv2.findContours(H, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
for cnt in contours:
    (x,y,w,h) = cv2.boundingRect(cnt)
    # manipulate these values to change accuracy
    if w > cols/2 and h < 10:
        cv2.drawContours(mask, [cnt], -1, 255,-1)

mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel, iterations = 2)
image[mask == 255] = (255,255,255)

result

答案 3 :(得分:1)

因此,我通过使用Juke建议的一部分找到了解决方案。最终,我将需要继续使用二进制模式处理图像,以至于我可能会保持这种方式。

首先,添加生成的图像verticalhorizontal。这将为您提供包含水平线和垂直线的图像。由于两个图像均为uint8类型(无符号的8位整数),因此添加它们将不是问题:

res = vertical + horizontal

然后,从用于查找行的原始输入图像res中减去tresh。这样可以去除白线,并且可以将其应用于其他一些形态转换。

fin = thresh - res

答案 4 :(得分:0)

用于去除水平线的样本。

示例图片:

enter image description here

def code = new XmlSlurper().parseText(xml)
                           .Body
                           .validateSignatureResponse
                           .WSReportsDTO
                           .diagnosticData
                           .Signatures
                           .Signature
                           .StructuralValidation
                           .Valid
                           .text()

=> horizo​​ntal_lines_extracted.png:

enter image description here

=> inverse_extracted.png

enter image description here

=> masked_img.png(遮罩后的结果图像)

enter image description here