简体   繁体   English

使用Open CV屏蔽水平和垂直线

[英]Masking horizontal and vertical lines with Open CV

I'm trying to remove horizontal and vertical lines in this image in order to have more distinct text areas. 我正在尝试删除此图像中的水平和垂直线条,以便拥有更多不同的文本区域。

在此输入图像描述

I'm using the below code, which follows this 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)

After this, I know I would need to isolate the lines and mask the original image with the white lines, however I'm not really sure on how to proceed. 在此之后,我知道我需要隔离线条并用白线掩盖原始图像,但是我不确定如何继续。

Does anyone have any suggestion? 有没有人有任何建议?

Jeru's answer already gives you what you want. Jeru的答案已经为你提供了你想要的东西。 But I wanted to add an alternative that is maybe a bit more general than what you have so far. 但是我想添加一个可能比你到目前为止更通用的替代方案。

You are converting the color image to gray-value, then apply adaptive threshold in an attempt to find lines. 您正在将彩色图像转换为灰度值,然后应用自适应阈值以尝试查找线条。 You filter this to get only the long horizontal and vertical lines, then use that mask to paint the original image white at those locations. 您对此进行过滤以仅获取较长的水平和垂直线,然后使用该蒙版在这些位置将原始图像绘制为白色。

Here we look for all lines, and remove them from the image making painting them with whatever the surrounding color is. 在这里,我们寻找所有线条,并将它们从图像中移除,使用任何周围的颜色绘制它们。 This process does not involve thresholding at all, all morphological operations are applied to the channels of the color image. 该过程根本不涉及阈值处理,所有形态学操作都应用于彩色图像的通道。

Ideally we'd use color morphology, but implementations of that are rare. 理想情况下,我们使用颜色形态,但实现很少。 Mathematical morphology is based on maximum and minimum operations, and the maximum or minimum of a color triplet (ie a vector) is not well defined. 数学形态学基于最大和最小操作,并且颜色三元组(即矢量)的最大值或最小值未被很好地定义。

So instead we apply the following procedure to each of the three color channels independently. 因此,我们将以下程序独立地应用于三个颜色通道中的每一个。 This should produce results that are good enough for this application: 这应该产生足以满足此应用程序的结果:

  1. Extract the red channel: take the input RGB image, and extract the first channel. 提取红色通道:获取input RGB图像,并提取第一个通道。 This is a gray-value image. 这是灰度值图像。 We'll call this image channel . 我们称之为图片channel

  2. Apply a top-hat filter to detect the thin structures: the difference between a closing with a small structuring element (SE) applied to channel , and channel (a closing is a dilation followed by an erosion with the same SE, you're using this to find lines as well). 应用顶帽过滤器来检测薄结构:应用于channel的小结构元素(SE)的闭合与channel之间的差异(闭合是扩张,随后是相同SE的侵蚀,您正在使用这也是为了找到线条)。 We'll call this output thin . 我们称这个输出thin thin = closing(channel)-channel . thin = closing(channel)-channel This step is similar to your local thresholding, but no actual threshold is applied. 此步骤类似于本地阈值,但不应用实际阈值。 The resulting intensities indicate how dark the lines are wrt to background. 得到的强度表明线条与背景有多深。 If you add thin to channel , you'll fill in these thin structures. 如果向channel添加thin ,则会填充这些薄结构。 The size of the SE here determines what is considered "thin". 这里SE的大小决定了什么被认为是“瘦”。

  3. Filter out the short lines, to keep only the long ones: apply an opening with a long horizontal SE to thin , and an opening with a long vertical SE to thin , and take the maximum of the two result. 过滤掉短线,只保留较长的短线:将长水平SE的开口应用于thin ,将长垂直SE应用于thin ,并取两个结果中的最大值。 We'll call this lines . 我们称之为这lines Note that this is the same process you used to generate horizontal and vertical . 请注意,这与您用于生成horizontalvertical过程相同。 Instead of adding them together as Jeru suggested, we take the maximum. 而不是像Jeru建议的那样将它们加在一起,而是采取最大值。 This makes it so that output intensities still match the contrast in channel . 这使得输出强度仍然与channel中的对比度相匹配。 (In Mathematical Morphology parlance, the supremum of openings is an opening). (在数学形态学的说法中,开口的上限是一个开头)。 The length of the SEs here determines what is long enough to be a line. 这里SE的长度决定了什么是足够长的线。

  4. Fill in the lines in the original image channel: now simply add lines to channel . 填写原始图像通道中的行:现在只需在channel添加lines Write the result to the first channel of the output image. 将结果写入输出图像的第一个通道。

  5. Repeat the same process with the other two channels. 用其他两个通道重复相同的过程。

Using PyDIP this is quite a simple script: 使用PyDIP这是一个非常简单的脚本:

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

Edit: 编辑:

When increasing the size of the first SE, above set to 5, to be large enough to remove also the thicker gray bar in the middle of the example image, causes part of the block containing the inverted text "POWERLIFTING" to be left in thin . 当增加第一个SE的大小(上面设置为5),大到足以移除示例图像中间的较粗灰色条时,会导致包含反转文本“POWERLIFTING”的块的一部分保持thin

To filter out those parts as well, we can change the definition of thin as follows: 要过滤掉这些部分,我们可以更改thin的定义,如下所示:

notthin = dip.Closing(channel, dip.SE(11, 'rectangular'), ["add max"]))
notthin = dip.MorphologicalReconstruction(notthin, channel, 1, "erosion")
thin = notthin - channel

That is, instead of thin=closing(channel)-channel , we do thin=reconstruct(closing(channel))-channel . 也就是说,我们不是thin=closing(channel)-channel ,而是thin=reconstruct(closing(channel))-channel The reconstruction simply expands selected (not thin) structures so that where part of a structure was selected, now the full structure is selected. 重建只是扩展选定的(非薄的)结构,以便在选择结构的一部分时,现在选择完整的结构。 The only thing that is now in thin are lines that are not connected to thicker structures. 现在唯一的thin是没有连接到较厚结构的线。

I've also added "add max" as a boundary condition -- this causes the closing to expand the area outside the image with white, and therefore see lines at the edges of the image as lines. 我还添加了"add max"作为边界条件 - 这会导致关闭使用白色扩展图像外部的区域,因此将图像边缘的线条看作线条。

产量

To elaborate more here is what to do: 这里详细说明如何做:

  • First, add the resulting images of vertical and horizontal . 首先,添加verticalhorizontal的结果图像。 This will give you an image containing both the horizontal and vertical lines. 这将为您提供包含水平和垂直线的图像。 Since both the images are of type uint8 (unsigned 8-bit integer) adding them won't be a problem: 由于两个图像都是uint8类型(无符号8位整数),因此添加它们不会成为问题:

res = vertical + horizontal

  • Finally, mask the resulting image obtained above with the original 3-channel image. 最后,使用原始3通道图像掩蔽上面获得的结果图像。 This can be accomplished using cv2.bitwise_and : 这可以使用cv2.bitwise_and来完成:

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

Do you want something like this? 你想要这样的东西吗?

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)

结果

So I have found a solution by using part of Juke's suggestion. 所以我通过使用Juke的部分建议找到了解决方案。 Eventually I would need to continue to process the image using a binary mode so figured I might keep it that way. 最终我需要继续使用二进制模式处理图像,所以我想可以保持这种方式。

First, add the resulting images of vertical and horizontal . 首先,添加verticalhorizontal的结果图像。 This will give you an image containing both the horizontal and vertical lines. 这将为您提供包含水平和垂直线的图像。 Since both the images are of type uint8 (unsigned 8-bit integer) adding them won't be a problem: 由于两个图像都是uint8类型(无符号8位整数),因此添加它们不会成为问题:

res = vertical + horizontal

Then, subtract res from the original input image tresh , which was used to find the lines. 然后,从原始输入图像tresh减去res ,用于查找线条。 This will remove the white lines and can than be used to apply some other morphology transformations. 这将删除白线,并可用于应用一些其他形态变换。

fin = thresh - res

A sample for removing horizontal lines. 用于去除水平线的样本。

Sample image: 示例图片:

在此输入图像描述

import cv2
import numpy as np

img = cv2.imread("Image path", 0)

if len(img.shape) != 2:
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
    gray = img

gray = cv2.bitwise_not(gray)
bw = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, 
cv2.THRESH_BINARY, 15, -2)

horizontal = np.copy(bw)

cols = horizontal.shape[1]
horizontal_size = cols // 30

horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))

horizontal = cv2.erode(horizontal, horizontalStructure)
horizontal = cv2.dilate(horizontal, horizontalStructure)

cv2.imwrite("horizontal_lines_extracted.png", horizontal)

horizontal_inv = cv2.bitwise_not(horizontal)
cv2.imwrite("inverse_extracted.png", horizontal_inv)

masked_img = cv2.bitwise_and(gray, gray, mask=horizontal_inv)
masked_img_inv = cv2.bitwise_not(masked_img)

cv2.imwrite("masked_img.jpg", masked_img_inv)

=> horizontal_lines_extracted.png: => horizo​​ntal_lines_extracted.png:

在此输入图像描述

=> inverse_extracted.png => inverse_extracted.png

在此输入图像描述

=> masked_img.png(resultant image after masking) => masked_img.png(掩蔽后的结果图像)

在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM