相关文章推荐
卖萌的皮带  ·  VMware vCenter Server ...·  1 年前    · 
个性的小刀  ·  c# - RichTextBox ...·  1 年前    · 

原文作者: 韩鼎


摘要

本文以“去除图像中的红色印章”为研究对象,应用颜色特征,将图像的前景从背景中分离。编制Python程序并进行仿真,最终提取出的前景图非常令人满意。

问题

给定如下一张图片:

OpenCV|基于颜色通道分离法去除图像中印章_灰度值

我们希望去除图像中的 红色 印章,以便能更好地读取文字。


技术路线

该问题中,​ ​字​ ​​像素为前景(frontground),​ ​印章​ ​​和​ ​纸​ ​​所在的像素为背景(background)。我们希望能将前景(​ ​字​ ​)提取出来。


分析图片我们发现,​ ​字​ ​的颜色是 黑色 , ​ ​印章​ ​的颜色是 红色 ,​ ​纸​ ​的颜色是 灰色(接近白色)


前景和背景在颜色上存在差异,这就启发我们通过颜色特征将前景和背景分离。


现在,我们分别提取原始图片的Red, Green和Blue三个颜色通道,得到如下三张图片:

OpenCV|基于颜色通道分离法去除图像中印章_直方图_02

OpenCV|基于颜色通道分离法去除图像中印章_python_03

OpenCV|基于颜色通道分离法去除图像中印章_直方图_04

图3,4与原始图像相似,利用价值不高。然而图2,即红色通道灰度值图片,非常值得我们关注: 图1中越红的像素点在图2中的灰度值就越大,越接近255,在图2中看起来就越白 。我们还得到以下发现:


  1. ​印章​ ​像素为白色,灰度值接近255
  2. ​纸​ ​的像素为灰色(接近白色),灰度值接近255
  3. ​字​ ​的像素为黑色,灰度值接近0


此时,我们就可以通过一个​ ​阈值​ ​​,将前景像素(​ ​字​ ​)提取出来。


公式为:

OpenCV|基于颜色通道分离法去除图像中印章_灰度值_05


其中: I r ( x , y )Ir(x,y)是坐标( x , y )(x,y)处的像素点的红色分量的灰度值, t h r e s h o l d threshold为阈值,可以通过求取图2的灰度直方图进行设定,如图5所示

OpenCV|基于颜色通道分离法去除图像中印章_直方图_06

我们可以看到,图5所示的红色通道直方图中,明显存在两个​ ​集中区域​ ​​,左侧的区域灰度值较低,对应颜色为​ ​黑色​ ​​,是前景像素(​ ​字​ ​​);右侧集中区域灰度值较高,对应颜色为白色区域,是背景像素(​ ​印章​ ​​和​ ​纸​ ​)。


在这两个灰度集中的区域的中间部分,设定一个阈值,可以将这两个区域分割开来。需要指出的是,阈值的设定对于最终的处理结果具有较大影响,这里我们选择阈值为110.


对于二值化后的图片,我们还可以通过数学形态学中的​ ​膨胀​ ​算子对图像进行视觉增强操作。


现在总结 技术路线


  1. 读取原始图像A
  2. 提取图像的 红色 通道,得到红色通道灰度值图片B
  3. 计算B的统计直方图C,确定最佳的阈值threshold
  4. 根据阈值,对B进行二值化,得到最终图片D
  5. (可选)应用膨胀算子对D进行操作,得到图片E


源代码

采用Python 2.7,更高版本估计也可以(未测试)。主要的包为OpenCV 3.4

# -*- coding: utf-8 -*-
"""
Created on Wed Jun 13 15:09:40 2018


@author: handsomeboy
"""


#去除印章
import cv2
import numpy as np
import matplotlib.pyplot as plt


#读入图像,三通道
image=cv2.imread("stamp.jpg",cv2.IMREAD_COLOR) #timg.jpeg


#读入图像尺寸
cols,rows,_=image.shape
#缩放比例
ratio=0.3


#缩放后的尺寸
cols=int(ratio*cols)
rows=int(ratio*rows)


#缩放图片
image = cv2.resize(image,(rows,cols) )


#获得三个通道
Bch,Gch,Rch=cv2.split(image)


#cv2.imshow('Blue channel',cv2.merge([Bch,0*Gch,0*Rch]))
#cv2.imshow('Green channel',cv2.merge([0*Bch,Gch,0*Rch]))
#cv2.imshow('Red channel',cv2.merge([0*Bch,0*Gch,Rch]))




cv2.imshow('Blue channel',Bch)
cv2.imshow('Green channel',Gch)
cv2.imshow('Red channel',Rch)


cv2.imwrite('Blue channel.jpg',Bch)
cv2.imwrite('Green channel.jpg',Gch)
cv2.imwrite('Red channel.jpg',Rch)




#红色通道的histgram
#变换程一维向量
pixelSequence=Rch.reshape([rows*cols,])


#统计直方图的组数
numberBins=256


#计算直方图
plt.figure()
manager = plt.get_current_fig_manager()
manager.window.showMaximized()


histogram,bins,patch=plt.hist(pixelSequence,numberBins,facecolor='black',histtype='bar') #facecolor设置为黑色


#设置坐标范围
y_maxValue=np.max(histogram)
plt.axis([0,255,0,y_maxValue])
#设置坐标轴
plt.xlabel("gray Level",fontsize=20)
plt.ylabel('number of pixels',fontsize=20)
plt.title("Histgram of red channel", fontsize=25)
plt.xticks(range(0,255,10))
#显示直方图
plt.pause(0.05)
plt.savefig("histgram.png",dpi=260,bbox_inches="tight")
plt.show()




#红色通道阈值
_,RedThresh = cv2.threshold(Rch,110,255,cv2.THRESH_BINARY)


#膨胀操作
element = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
erode = cv2.erode(RedThresh, element)


#显示效果
cv2.imshow('original color image',image)
cv2.imwrite('scaleimage.jpg',image)


cv2.imshow("RedThresh",RedThresh)
cv2.imwrite('RedThresh.jpg',RedThresh)


cv2.imshow("erode",erode)
cv2.imwrite("erode.jpg",erode)

计算结果

图1,图3,图6分别对应计算流程的A,B,C三张图。下面给出D,E两幅图:

OpenCV|基于颜色通道分离法去除图像中印章_直方图_07

OpenCV|基于颜色通道分离法去除图像中印章_python_08

结论

将图1和图7做对比,我们发现,印章 完全消失 了!效果非常令人满意。


“去除图像中的印章”,这个任务看起来很困难,但是如果知道颜色通道的思想,该任务就显得非常简单。但这里的关键是,能够想到这个思想,这就需要对图像处理的知识掌握扎实,在以后的应用中才会灵活运用。


参考文献

  1. [OpenCV探索之路(二十六):如何去除票据上的印章]
  2. python opencv入门 形态学转换(13)
  3. 《数字图像处理》冈萨雷斯