从文本图像中删除非直线

时间:2019-10-24 11:13:28

标签: python image opencv image-processing line

我有一个包含文本但有非直线绘制的图像。

enter image description here

我想删除这些行而不影响/删除文本中的任何内容。
为此,我使用了霍夫概率变换:

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) 

结果不如我预期:

enter image description here

未完全检测到线条(仅检测到线条的某些线段或直线段)。
我对cv2.Cannycv2.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)  

结果要好一些,但是没有检测到整行。

enter image description here

有什么主意如何使线条检测更有效?

2 个答案:

答案 0 :(得分:3)

典型的去除线条的方法是使用水平/垂直内核或cv2.HoughLinesP(),但是这些方法仅在线条是直线时才有效。在这种情况下,线条不是直线,因此一个想法是使用对角核,形态转换和轮廓过滤从文本中删除线条。我将使用在removing horizontal lines in an image中找到但具有对角线内核的上一个答案的方法


我们首先将图像转换为灰度并执行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)

该图像隔离了主要的对角线,但它也包含了文本中的细线。要删除它们,我们找到轮廓并使用轮廓区域进行过滤。如果轮廓通过了我们的过滤器,我们可以通过用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)

从这里我们只需将原始图像cv2.bitwise_xor() # Bitwise-xor with original image opening = cv2.merge([opening, opening, opening]) result = cv2.bitwise_xor(image, opening) 即可获得

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()

注意事项:尽管有可能,但很难删除这些行而不影响文本,并且需要一些巧妙的技巧来“修复”文本。查看remove borders from image but keep text written on borders,了解一种用于重建丢失文本的方法。隔离对角线的另一种方法是采用逆向方法。为什么不尝试确定什么不是诊断线,而不是尝试检测诊断线。您可能可以通过简单的过滤技术来做到这一点。要创建动态对角线内核,可以将np.diag()用于不同的对角线宽度

完整代码完整

const express = require('express')
const path = require('path')
const app = express()    
const PORT = process.env.PORT || 3001
// static folder
app.use(express.static('public'))
// load view engine
// app.set('views', path.join(__dirname, 'view'))
app.set('view engine', 'pug')
// listening
app.listen(PORT, console.log(`Server started on port ${PORT}`))

答案 1 :(得分:-1)

使用connectedComponentsWithStats找出图像中最长的两个相连组件。