最近一段时间的工作都是研究如何去除图片中的红色印章,在网上查找了大量的大佬写过的方法,发现大多数是采取颜色通道分离法来做,并且效果还不错。站在前人的肩膀上,我又做了些许调整,谈不上是改进,但是能应对更对的情况总还是好的,特此记录。
拿发票做例子,印章相较于其它部分最大的不同就是印章是红色的,而其它部分不是(这是一句废话)。
那么第一步当然就是将红色分离出来,这一步可以采用Python第三方库OpenCV里的 cv2.split()方法,该方法是把RGB图像的三个通道拆分开来,得到的是蓝色、绿色和红色通道的灰度图。
Blue通道:
Green通道:
Red通道:
前2张与原图相差不大,所以用处也不大,有用的是第3张图。原始图中越红的像素点在图4中的灰度值就越大,越接近255,在图4中看起来就越白。
这时,我们可以采取OpenCV中的cv2.threshold()方法对图4进行二值化,即将一个灰色的图片,设定一个阈值,像素灰度值大于这个阈值的处理成白色,小于阈值的处理成黑色,这样就能将图片处理成下图中的样子:
可以说效果还是不错的,但是这个阈值应该怎么定呢?
通过参考的几个大佬的方法,基本都是将红色通道灰度值输出成一个直方图,人为观察一个最佳的阈值,然后设定这个阈值。不过这样一来就没办法应对多张图片的情况了,总不能一张图片设定一个阈值吧。
幸好cv2.threshold()还支持一种写法,就是多传入一个参数cv2.THRESH_OTSU,并且把阈值thresh设为0,算法会找到最优阈值:
cv2.threshold(red_c, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
具体原理这里不多赘述,大家可以自行了解,我只是将大概的思路说一下,接下来贴代码:
# -*- encoding: utf-8 -*-
import cv2
import numpy as np
class SealRemove(object):
"""
印章处理类
"""
def remove_red_seal(self, image):
"""
去除红色印章
"""
# 获得红色通道
blue_c, green_c, red_c = cv2.split(image)
# 多传入一个参数cv2.THRESH_OTSU,并且把阈值thresh设为0,算法会找到最优阈值
thresh, ret = cv2.threshold(red_c, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 实测调整为95%效果好一些
filter_condition = int(thresh * 0.95)
_, red_thresh = cv2.threshold(red_c, filter_condition, 255, cv2.THRESH_BINARY)
# 把图片转回 3 通道
result_img = np.expand_dims(red_thresh, axis=2)
result_img = np.concatenate((result_img, result_img, result_img), axis=-1)
return result_img
if __name__ == '__main__':
image = 'D:/test/test.png'
img = cv2.imread(image)
seal_rm = SealRemove()
rm_img = seal_rm.remove_red_seal(img)
cv2.imwrite("D:/test/result.png",rm_img)
参考:基于颜色通道分离法去除图像中印章,http://deanhan.com/2018/06/15/channel/#comments