sin*_*ium 5 python opencv image image-processing line
我有一个包含文本但有非直线绘制的图像。
我想删除这些行而不影响/删除文本中的任何内容。
为此,我使用了霍夫概率变换:
import cv2
import numpy as np
def remove_lines(filename):
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 200)
lines = cv2.HoughLinesP(edges, rho=1, theta=1*np.pi/180,
threshold=100, minLineLength=100, maxLineGap=5)
# Draw lines on the image
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 3)
cv2.imwrite('result', img)
Run Code Online (Sandbox Code Playgroud)
结果不如我预期的好:
没有完全检测到线条(仅检测到线条的某些线段,直线段)。
我对cv2.Canny和cv2.HoughLinesP参数做了一些调整,但是也没有用。
我也尝试过cv2.createLineSegmentDetector(由于许可证问题,在最新版本的opencv中不可用,因此我不得不将opencv降级到4.0.0.21版):
import cv2
import numpy as np
def remove_lines(filename):
im = cv2.imread(filename)
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# Create default parametrization LSD
lsd = cv2.createLineSegmentDetector(0)
# Detect lines in the image (Position 0 of the returned tuple are the
# detected lines)
lines = lsd.detect(gray)[0]
# drawn_img = lsd.drawSegments(res, lines)
for element in lines:
if (abs(int(element[0][0]) - int(element[0][2])) > 70 or
abs(int(element[0][1]) - int(element[0][3])) > 70):
cv2.line(im, (int(element[0][0]), int(element[0][1])), (int(
element[0][2]), int(element[0][3])), (0, 0, 255), 3)
cv2.imwrite('lsd.jpg', im)
Run Code Online (Sandbox Code Playgroud)
结果要好一些,但没有检测到整条线。
任何想法如何使线路检测更有效?
去除线条的典型方法是使用水平/垂直内核,或者cv2.HoughLinesP()这些方法仅在线条是直的情况下才有效。在这种情况下,线条不是直线,因此一个想法是使用对角核、形态变换和轮廓过滤来从文本中删除线条。我将使用以前的答案在删除图像中的水平线时找到的方法,但使用对角线内核
我们首先将图像转换为灰度并执行 Otsu 阈值以获得二值图像。接下来,我们创建一个对角线内核,然后执行变形接近以检测/过滤掉对角线。由于cv2.getStructuringElement()没有任何内置对角内核,我们创建自己的

# Read in image, grayscale, and Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Create diagonal kernel
kernel = np.array([[0, 0, 1],
[0, 1, 0],
[1, 0, 0]], dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
Run Code Online (Sandbox Code Playgroud)
该图像隔离了主要的对角线,但也包括文本中的小线。为了去除它们,我们找到轮廓并使用轮廓区域进行过滤。如果轮廓通过我们的过滤器,我们会通过用 来“填充”轮廓来有效地去除噪声cv2.drawContours()。这给我们留下了我们想要删除的对角线

# Find contours and filter using contour area to remove noise
cnts = cv2.findContours(opening, 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)
if area < 500:
cv2.drawContours(opening, [c], -1, (0,0,0), -1)
Run Code Online (Sandbox Code Playgroud)
从这里我们简单地cv2.bitwise_xor()使用原始图像来获得我们的结果

# Bitwise-xor with original image
opening = cv2.merge([opening, opening, opening])
result = cv2.bitwise_xor(image, opening)
Run Code Online (Sandbox Code Playgroud)
注意:虽然有可能并且需要一些巧妙的技巧来“修复”文本,但很难在不影响文本的情况下删除这些行。看看从图像中删除边框,但保留写在边框上的文本,以便重建丢失的文本。另一种隔离对角线的方法是采取逆势方法;与其尝试检测诊断线,不如尝试确定什么不是诊断线。您可能可以通过简单的过滤技术来做到这一点。要创建动态对角内核,您可以使用np.diag()不同的对角线宽度
完整代码
import cv2
import numpy as np
# Read in image, grayscale, and Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Create diagonal kernel
kernel = np.array([[0, 0, 1],
[0, 1, 0],
[1, 0, 0]], dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
# Find contours and filter using contour area to remove noise
cnts = cv2.findContours(opening, 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)
if area < 500:
cv2.drawContours(opening, [c], -1, (0,0,0), -1)
# Bitwise-xor with original image
opening = cv2.merge([opening, opening, opening])
result = cv2.bitwise_xor(image, opening)
cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('result', result)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)