明月机器学习系列020:图像处理入门篇

2021-10-28 10:00:15 浏览数 (1)

作为图像识别的入门篇,主要是介绍opencv处理图像的常用功能,实现一个简单的功能:识别合同扫描件的文本行。

这个小功能来源一个实际的产品项目:

问题背景


以前很少接触合同,对合同没有多少了解,原来到了今时今日,合同的审核都还是需要花费大量人工去处理的,例如一份60页的合同,发出去给客户签名,客户签完名又发回来,这时问题来了,需要有人去审核发回来的合同是否有被修改过,特别是在金融证券等行业,据说因为这些而导致的纠纷还不少。

为了减少发生纠纷的可能性,于是企业可能常年需要招一批实习生来做合同审核,确保合同的一致性。对于大的金融证券公司,这种需要审核的合同或者类似合同的文档太多了,基本只能使用人工进行抽检,而且人天生就不擅长做这些机械重复的工作,总有老眼昏花的时候,结果就有点看天吃饭了。

机器视觉


很显然,像合同比对这种苦力,是很适合使用机器视觉进行解决的,因为它量大,且规则比较明确。所谓基于机器视觉来完成合同文档比对,其实就是计算扫描件和底稿的相似性。

这其中一个功能就是需要将文本行识别出来,这样我们就能计算每行的相似性,或者对行进行OCR。

图像二值化


例如一个合同文档的图像如下:

通过观察,我们也能知道,我们的合同文本都是一行一行的,行与行之间有间隙,我们就可以利用这个特征,进行文本行的检测。

为了效果更好,很自然我们希望特征能更加明显,这就需要对图像进行二值化,文本自身能加强,而非文本则减弱。

代码语言:javascript复制
# 读入图像
img = cv2.imread('images/合同文本.png', cv2.IMREAD_GRAYSCALE)
print(img.shape)

# 反色处理
img = cv2.bitwise_not(img)

# 几种不同的二值化
ret, th_img = cv2.threshold(img, 40, 255, cv2.cv2.THRESH_BINARY)
ret, thtru_img = cv2.threshold(img, 200, 255, cv2.cv2.THRESH_TRUNC)
ret, otsu_img = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)
ada_th_img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 0)

关于二值化更多可以看这里:https://ivanzz1001.github.io/records/post/python/2017/09/05/python-opencv-part2

这里二值化是需要进行选择的,经过比较,我们选择OTSU生成的二值化,展示效果如下:

看起来字有点模糊,不过这并不影响我们实现文本行的检测,因为我们要的效果就是黑白足够分明。当然,对于OCR这个效果可能并不好。

检测文本行


已经有了二值化图像了,我们只需要按行累加:

代码语言:javascript复制
sum_img = otsu_img/255
print(otsu_img.shape)
sum_img = np.sum(sum_img, axis=1)

plt.barh(range(sum_img.size), sum_img)
plt.ylim(sum_img.size 1, 0)
plt.show()

就可以得到一个条形图,如下:

条形图中,比较大的空白其实就是行间隙之间的空白地带。将这个结果应用到原图中,就能将文本行检测出来:

代码语言:javascript复制
img = cv2.imread('images/合同文本.png')

sum_img = otsu_img/255
h, w = otsu_img.shape
sum_img = np.sum(sum_img, axis=1)

out_img = img.copy()
is_started = False
for i, v in enumerate(sum_img):
    if v == 0:
        if is_started is False:
            continue
        max_v = max(sum_img[i:min(h, i 8)])
        if max_v == 0:
            cv2.line(out_img, (0, i), (w, i), (0, 0, 255), 1)
            is_started = False
            continue
    if v > 0 and is_started is False:
        is_started = True
        cv2.line(out_img, (0, i-1), (w, i-1), (0, 0, 0), 1)
        
display_cv2(out_img)

这段代码其实就是在文本行的上方画一条黑线,下方画一条红线,效果如下:

可以看到,文本行的上下边界识别还是非常有效的。当然这是相对于场景下比较标准的合同文本来说的。

当然,文本行检测只是整体功能中非常小的一个功能,还有很多其他的功能,例如角度纠正,去噪,去水印,表格识别,OCR等等。

项目整体效果


产品上线之后,预计节省10 个实习生的工作量,节省企业成本,关键还是原来无法保证的合同篡改,现在能全量进行覆盖了。

特别适合金融证券,法院,律所,档案馆等这些领域,欢迎交流。

2020-01-06

0 人点赞