[英]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:
这应该产生足以满足此应用程序的结果:
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
。
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的大小决定了什么被认为是“瘦”。
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
. 请注意,这与您用于生成
horizontal
和vertical
过程相同。 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的长度决定了什么是足够长的线。
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. 将结果写入输出图像的第一个通道。
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: 这里详细说明如何做:
vertical
and horizontal
. vertical
和horizontal
的结果图像。 This will give you an image containing both the horizontal and vertical lines. uint8
(unsigned 8-bit integer) adding them won't be a problem: uint8
类型(无符号8位整数),因此添加它们不会成为问题: res = vertical + horizontal
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
. 首先,添加
vertical
和horizontal
的结果图像。 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: => horizontal_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.