使用openCV去除文字中乱入的线条实例


Posted in Python onJune 02, 2020

今天上午,朋友发来一张图片如下。没错,这就是原图,他希望可以通过一些简单的算法将图中这条穿过单词间的直线去掉,使得到的结果能够通过他的文字识别算法并得出正确结果——The Techniques of Machine Vision。

使用openCV去除文字中乱入的线条实例

乍一看这似乎挺简单,(1)将图像二值化;(2)找出这条直线;(3)将直线区域填成背景色(即白色);(4)再通过膨胀、腐蚀等操作将单词缺失的部分给补全。以上4步似乎可以满足要求,但测试发现,效果不尽人意。

一、按上述方法实现过程

使用openCV去除文字中乱入的线条实例

二值化结果如图1.1所示,可以看到图像并不标准,直线粗细也不一,我们尝试用霍夫变换找一下直线,代码如下

void findLines(IplImage* raw, IplImage* dst)
{
	IplImage* src = cvCloneImage(raw);
	IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
	cvCanny(src, canny, 20, 200, 3);
	CvMemStorage* stor = cvCreateMemStorage(0);
	CvSeq* lines = NULL;
	lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30);
	cvZero(dst);
	CvPoint maxStart, maxEnd;
	int maxDistance = 0;
	for (int i = 0; i < lines->total; i++)
	{
		CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i);
		if (abs(line[0].x - line[1].x) > maxDistance)
		{
			maxDistance = abs(line[0].x - line[1].x);
			maxStart = line[0];
			maxEnd = line[1];
		}
	}
	cvLine(dst, maxStart, maxEnd, cvScalar(255), 1);
	cvReleaseImage(&src);
	cvReleaseMemStorage(&stor);
}

简要解释一下这段代码。函数的功能是在输入图像中找出一条直线,输入的图像是灰度图raw,返回值为dst,返回值是以图片的形式,将找到的直线画上图中。

函数lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30);的参数表明,要求直线长度在200个像素以上,且两条在同一直线上的线段,如果相隔不到30个像素,就把它们连起来【注:图片尺寸为1066×148】。对于找到的多条直线,认为最长的一条是我们要找的那条。找距离时用了abs(line[0].x - line[1].x);是不严格的,严格来讲应该是

sqrt((line[0].x - line[1].x)*(line[0].x - line[1].x)+(line[0].y - line[1].y)*(line[0].x - line[1].x))

不过图中的直线接近水平,这里就简化一下啦。

所以将运行这段代码后,返回的图片dst应该是这样子的

使用openCV去除文字中乱入的线条实例

图1.2中直线的粗线可以通过改变cvLine(dst, maxStart, maxEnd, cvScalar(255), 1);最后一个参数来调整,这里用的是1。

接下来步骤就是在二值化图(图1.1)中去掉这条线,代码如下:

void eraseLine(IplImage* src, IplImage* flag)
{// flag为图1.2所示的图片,src为图1.1所示的二值化图片
	for (int row = 0; row < src->height; row++)
		for (int col = 0; col < src->width; col++)
		{	// 如果在白色线段上,则将二值化图片填为白色
			if (cvGet2D(flag, row, col).val[0] == 255)
				cvSet2D(src, row, col, cvScalar(255));
		}
}

当直线的宽度分别为2、3个像素时,二值化图去掉直线后的效果如下

使用openCV去除文字中乱入的线条实例

可以看到,效果很差,如果要膨胀(黑色部分减小),单词下边部分都会消失了,直接腐蚀(黑色部分增大),线又不能完全去掉。

后来,我采用的办法是,对图1.3重新找一次直线(减去一次直线后,中间还残留一部分短些的直线),再减掉,再找再减掉。后面再对图像进行腐蚀(黑色部分增长)。最终效果最好这就如下图所示

使用openCV去除文字中乱入的线条实例

但这种方法用时长、针对不同的直线,找直线-减直线 的重复次数还不一样,不具有可移植性。而且啊,这个图片识别出来的结果是

The Technique_sJ_otMachine Vision

所以需要采用新的办法来解决这个问题。

二、新的办法

源代码如下

#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;
/*
函数功能:在输入图像中找一条直线
输入输出:输入的图像是灰度图raw,返回值为dst,返回值是一条白色的线
lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30);
参数中的200是指要找的直线长度要在200个像素以上;
参数中的30指的是两条在同一直线上的线段,如果相隔不到30,则把它们连起来
*/
void findLines(IplImage* raw, IplImage* dst)
{
 IplImage* src = cvCloneImage(raw); // clone the input image
 IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); // create a tmp image head to save gradient image
 cvCanny(src, canny, 20, 200, 3); // Generate its gradient image
 CvMemStorage* stor = cvCreateMemStorage(0);
 CvSeq* lines = NULL;
 // find a line whose length bigger than 200 pixels
 lines = cvHoughLines2(canny, stor, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 200, 30);
 cvZero(dst);
 CvPoint maxStart, maxEnd; // save the coordinate of the head and rear of the line we want
 int maxDistance = 0; // The maximum distance of all lines found by [cvHoughLines2]
 for (int i = 0; i < lines->total; i++) // lines->total: the number of lines 
 {
 // variable 'lines' is a sequence, [cvGetSeqElem] gets the (i)th line, and it returns its head and rear.
 CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); 
 // line[0] and line[1] is respectively the line's coordinate of its head and rear
 if (abs(line[0].x - line[1].x) > maxDistance)
 {/* It's a trick because the line is almost horizontal.
 strictly, it should be 
 sqrt((line[0].x - line[1].x)*(line[0].x - line[1].x)+(line[0].y - line[1].y)*(line[0].x - line[1].x))
 */
 maxDistance = abs(line[0].x - line[1].x);
 maxStart = line[0];
 maxEnd = line[1];
 }
 }
 cvLine(dst, maxStart, maxEnd, cvScalar(255), 1); // draw the white line[cvScalar(255)] in a black background
 cvReleaseImage(&src); // free the memory
 cvReleaseMemStorage(&stor);
}
/*
函数功能:擦除面积小于【15个像素】的小块儿
输入输出:无返回值,直接对输入的图像进行操作
*/
void erase(IplImage* raw)
{
 IplImage* src = cvCloneImage(raw);
 /*Binarization and inverse the black and white because the function next only find white area while
 the word in image is black.*/
 cvThreshold(src, src, 120, 255, CV_THRESH_BINARY_INV); 
 // create some space to save the white areas but we access it via variable 'cont'
 CvMemStorage* stor = cvCreateMemStorage(0); 
 CvSeq* cont;
 cvFindContours(src, stor, &cont, sizeof(CvContour), CV_RETR_EXTERNAL); // find the white regions
 for (; cont; cont = cont->h_next) // Traversal
 {
 if (fabs(cvContourArea(cont)) < 15) // if its Area smaller than 15, we fill it with white[cvScalar(255)]
 cvDrawContours(raw, cont, cvScalar(255), cvScalar(255), 0, CV_FILLED, 8);
 }
 cvReleaseImage(&src);
}
 
int main()
{
 IplImage* src = cvLoadImage("D:/test.png");
 cvNamedWindow("原图", 1);
 cvShowImage("原图", src);
 IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
 IplImage* canny = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
 IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
 IplImage* binary = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
 
 cvCvtColor(src, gray, CV_RGB2GRAY);
 cvThreshold(gray, binary, 120, 255, CV_THRESH_OTSU);
 
 findLines(gray, dst);
 cvNamedWindow("dst", 1);
 cvShowImage("dst", dst);
 
 for (int row = 0; row < binary->height; row++)
 for (int col = 0; col < binary->width; col++)
 {
 if (cvGet2D(dst, row, col).val[0] == 255)
 {
 int up = 0, down = 0;
 int white = 0;
 for (int i = row; i >= 0; i--)
 {
 if (cvGet2D(binary, i, col).val[0] == 0)
 {
 up++; 
 white = 0;
 }
 else white++;
 if(white > 2) break;
 }
 white = 0;
 for (int i = row; i < binary->height; i++)
 {
 if (cvGet2D(binary, i, col).val[0] == 0)
 {
 down++;
 white = 0;
 }
 else white++;
 if (white > 2) break;
 }
 if (up + down < 8)
 {
 for (int i = -up; i <= down; i++) cvSet2D(binary, row + i, col, cvScalar(255));
 }
 }
 }
 cvNamedWindow("结果", 1);
 cvShowImage("结果", binary);
 erase(binary);
 //cvDilate(binary, binary, NULL, 1);
 cvErode(binary, binary, NULL, 1);
 cvNamedWindow("膨胀腐蚀", 1);
 cvShowImage("膨胀腐蚀", binary);
 cvSaveImage("D:/result.png", binary);
 cvReleaseImage(&src);
 cvReleaseImage(&canny);
 cvReleaseImage(&gray);
 cvReleaseImage(&dst);
 cvReleaseImage(&binary);
 cvWaitKey(0);
 return 0;
}

这个方法很简单的,就是在找到直线(直线宽度为1)后,沿着直线从左到右对二值化图进行上下扫描,如果这个直线的宽度(黑色的宽度)小于8个像素,则认为它只是直线,而不是文字的一部分,那么将它填成白色;反之,对于直线是文字的一部分这种情况,则不对它进行任何操作。

这样得到的结果如下图2.1所示

使用openCV去除文字中乱入的线条实例

当然这个结果有点差强人意,如果你有更好的想法,请在下面留言,我们交流交流。

以上这篇使用openCV去除文字中乱入的线条实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用Python脚本生成随机IP的简单方法
Jul 30 Python
Python中的复制操作及copy模块中的浅拷贝与深拷贝方法
Jul 02 Python
Python将多个excel表格合并为一个表格
Feb 22 Python
PyQt5每天必学之QSplitter实现窗口分隔
Apr 19 Python
python操作excel的方法(xlsxwriter包的使用)
Jun 11 Python
Python深拷贝与浅拷贝用法实例分析
May 05 Python
安装好Pycharm后如何配置Python解释器简易教程
Jun 28 Python
pytorch 可视化feature map的示例代码
Aug 20 Python
在Python3.74+PyCharm2020.1 x64中安装使用Kivy的详细教程
Aug 07 Python
Python requests上传文件实现步骤
Sep 15 Python
Python+logging输出到屏幕将log日志写入文件
Nov 11 Python
python实现马丁策略的实例详解
Jan 15 Python
Python能做什么
Jun 02 #Python
什么是Python中的匿名函数
Jun 02 #Python
学习python需要有编程基础吗
Jun 02 #Python
python中if及if-else如何使用
Jun 02 #Python
python3+openCV 获取图片中文本区域的最小外接矩形实例
Jun 02 #Python
python编写一个会算账的脚本的示例代码
Jun 02 #Python
使用opencv识别图像红色区域,并输出红色区域中心点坐标
Jun 02 #Python
You might like
我用php+mysql写的留言本
2006/10/09 PHP
在CentOS上搭建LAMP+vsftpd环境的简单指南
2015/08/01 PHP
php多文件打包下载的实例代码
2017/07/12 PHP
PHPUnit测试私有属性和方法功能示例
2018/06/12 PHP
如何实现iframe(嵌入式帧)的自适应高度
2006/07/26 Javascript
拖拉表格的JS函数
2008/11/20 Javascript
jquery的ajax请求全面了解
2013/03/20 Javascript
js使用for循环与innerHTML获取选中tr下td值
2014/09/26 Javascript
javascript中AJAX用法实例分析
2015/01/30 Javascript
jQuery遍历页面所有CheckBox查看是否被选中的方法
2015/04/14 Javascript
javascript合并表格单元格实例代码
2016/01/03 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
聊一聊JS中this的指向问题
2016/06/17 Javascript
Chrome不支持showModalDialog模态对话框和无法返回returnValue问题的解决方法
2016/10/30 Javascript
BootStrap表单宽度设置方法
2017/03/10 Javascript
websocket+node.js实现实时聊天系统问题咨询
2017/05/17 Javascript
原生js 封装get ,post, delete 请求的实例
2017/08/11 Javascript
VUEJS 2.0 子组件访问/调用父组件的实例
2018/02/10 Javascript
node之本地服务器图片上传的方法示例
2019/03/26 Javascript
python unittest实现api自动化测试
2018/04/04 Python
pandas 对series和dataframe进行排序的实例
2018/06/09 Python
Python读取txt文件数据的方法(用于接口自动化参数化数据)
2018/06/27 Python
Windows下安装Scrapy
2018/10/17 Python
Python爬取豆瓣视频信息代码实例
2019/11/16 Python
CSS3实现酷炫的3D旋转透视效果
2019/11/21 HTML / CSS
腾讯技术类校园招聘笔试试题
2014/05/06 面试题
SQL面试题
2013/12/09 面试题
教师评优事迹材料
2014/01/10 职场文书
合作意向书格式及范文
2014/03/31 职场文书
论文评语大全
2014/04/29 职场文书
中等生评语大全
2014/05/04 职场文书
2014党员民主评议个人总结
2014/09/10 职场文书
公安派出所所长四风问题个人对照检查材料
2014/10/04 职场文书
环境卫生工作汇报材料
2014/10/28 职场文书
党员转正申请报告
2015/05/15 职场文书
2019经典广告词集锦!
2019/07/02 职场文书