【OpenCV基础】第十一课:形态学操作的应用

提取水平线和垂直线,提取验证码

Posted by x-jeff on May 23, 2020

本文为原创文章,未经本人允许,禁止转载。转载请注明出处。

1.提取水平线和垂直线

在之前的博客【OpenCV基础】第十课:形态学操作中,我们介绍了很多形态学操作。其中,膨胀与腐蚀操作可以使用任意的结构元素,例如矩形、圆、直线等各种自定义形状。

提取水平线和垂直线的具体步骤见下:

1.1.步骤一:读入原始图像

使用imread函数读入原始图像:

1.2.步骤二:转换为灰度图像

使用cvtColor函数将原始图像转换为灰度图像:

当然,也可以直接通过修改imread的参数直接将原始图像转换成灰度图像读进来。具体请见:【OpenCV基础】第二课:加载、修改、保存图像

1.3.步骤三:转换为二值图像

这一步我们需要使用一个新的API:adaptiveThreshold

该API的参数说明:

1
2
3
4
5
6
7
8
9
void adaptiveThreshold(
	InputArray src,
	OutputArray dst,
	double maxValue,
	int adaptiveMethod,
	int thresholdType,
	int blockSize,
	double C
);
  1. InputArray src是输入图像。
  2. OutputArray dst是输出图像。
  3. double maxValue用于计算第5个参数。
  4. int adaptiveMethod是阈值的计算方法,同样也用于计算第5个参数。有两种方式:
    • ADAPTIVE_THRESH_MEAN_C:阈值等于blockSize内像素的平均值减去第7个参数C
    • ADAPTIVE_THRESH_GAUSSIAN_C:阈值等于blockSize内像素的高斯均值(即高斯加权的均值)减去第7个参数C
  5. int thresholdType是图像的转换方式。有八种方式:THRESH_BINARYTHRESH_BINARY_INVTHRESH_TRUNCTHRESH_TOZEROTHRESH_TOZERO_INVTHRESH_MASKTHRESH_OTSU以及THRESH_TRIANGLE。这里只介绍涉及二值图像转换的两种方法,其中dst(x,y)为输出图像$(x,y)$点的像素值,src(x,y)为输入图像$(x,y)$点的像素值,T(x,y)为$(x,y)$点的阈值(由参数4获得),maxValue由参数3获得:
  6. int blockSize为正奇数,用法见参数4。
  7. double C用法见参数4,可以为正数、0、负数。

我们尝试将得到的灰度图像通过adaptiveThreshold 转换为二值图像:

1
adaptiveThreshold(gray1, binary1, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);

发现我们要提取的水平线和垂直线旁边有很宽的白条,会影响到我们的提取效果,因此我们需要先对灰度图像做个取反操作,即~gray1

1
adaptiveThreshold(~gray1, binary1, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);

再来看下效果:

~通过位运算对图像进行取反操作。例如对于灰度图像来说,像素值的类型是unsigned char,即八位无符号二进制数。假设像素值是2,其反码为0000 0010,按位取反得到~21111 1101,因为是无符号数,所以该二进制数对应的十进制数为253,即255-2。

bitwise_not也是通过位运算进行取反操作,效果等同于~

1.4.步骤四:定义结构元素

使用getStructuringElement定义我们所需的结构元素。

用于提取水平线的结构元素:

1
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));

用于提取垂直线的结构元素:

1
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));

1.5.步骤五:执行开操作

然后我们通过执行开操作来提取水平线和垂直线。

提取水平线:

提取垂直线:

2.提取验证码

基于第1部分的内容,我们来处理验证码图片中的干扰项。例如要处理的验证码图片见下:

我们依然按照1.1~1.5这5个步骤来处理这张图片。但是我们需要稍作修改。

针对第四步,我们这次使用矩形的结构元素:

1
Mat kernel=getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1));

使用矩形结构元素的好处在于可以帮助我们去除各种线段(不局限于水平线或者垂直线),并且也可以去除图片中的散点。

此外,对最终提取得到的图像做了取反操作,使其看起来更顺眼一点:

3.代码地址

  1. 形态学操作的应用