使用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自动生成文章
Dec 25 Python
Python中字典的setdefault()方法教程
Feb 07 Python
对python .txt文件读取及数据处理方法总结
Apr 23 Python
Python比较配置文件的方法实例详解
Jun 06 Python
Python生成一个迭代器的实操方法
Jun 18 Python
使用TensorFlow直接获取处理MNIST数据方式
Feb 10 Python
使用keras时input_shape的维度表示问题说明
Jun 29 Python
python使用nibabel和sitk读取保存nii.gz文件实例
Jul 01 Python
Python调用C语言程序方法解析
Jul 07 Python
详解python中GPU版本的opencv常用方法介绍
Jul 24 Python
搭建pypi私有仓库实现过程详解
Nov 25 Python
python 三边测量定位的实现代码
Apr 22 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判断服务器是否是HTTPS连接
2013/07/05 PHP
PHP包含文件函数include、include_once、require、require_once区别总结
2014/04/05 PHP
PHP中多线程的两个实现方法
2016/10/14 PHP
visual studio code 调试php方法(图文详解)
2017/09/15 PHP
capacityFixed 基于jquery的类似于新浪微博新消息提示的定位框
2011/05/24 Javascript
js确定对象类型方法
2012/03/30 Javascript
jQuery固定浮动侧边栏实现思路及代码
2014/09/28 Javascript
基于jQuery实现的图片切换焦点图整理
2014/12/07 Javascript
javascript实现获取字符串hash值
2015/05/10 Javascript
JavaScript实现快速排序的方法
2015/07/31 Javascript
AngularJS模块学习之Anchor Scroll
2016/01/19 Javascript
使用AJAX实现Web页面进度条的实例分享
2016/05/06 Javascript
Webpack+Vue如何导入Jquery和Jquery的第三方插件
2017/02/20 Javascript
浅谈微信页面入口文件被缓存解决方案
2018/09/29 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
2019/11/05 Javascript
如何用JS模拟实现数组的map方法
2020/07/30 Javascript
[49:56]VG vs Optic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别
2017/06/23 Python
TensorFlow 实战之实现卷积神经网络的实例讲解
2018/02/26 Python
Python二叉树的遍历操作示例【前序遍历,中序遍历,后序遍历,层序遍历】
2018/12/24 Python
python实现给微信指定好友定时发送消息
2019/04/29 Python
django-allauth入门学习和使用详解
2019/07/03 Python
numpy按列连接两个维数不同的数组方式
2019/12/06 Python
win10系统下python3安装及pip换源和使用教程
2020/01/06 Python
利用Python如何制作贪吃蛇及AI版贪吃蛇详解
2020/08/24 Python
Chain Reaction Cycles芬兰:世界上最大的在线自行车商店
2017/12/06 全球购物
Revolution Beauty美国官网:英国知名化妆品网站
2018/07/23 全球购物
Hobbs官方网站:英国奢华女性时尚服装
2020/02/22 全球购物
求职信写作要突出重点
2014/01/01 职场文书
档案检查欢迎词
2014/01/13 职场文书
幼儿园教师节活动方案
2014/02/02 职场文书
幼儿园健康教育方案
2014/06/14 职场文书
小学生十佳少年事迹材料
2014/08/20 职场文书
《你在为谁工作》心得体会(共8篇)
2016/01/20 职场文书
mysql分组后合并显示一个字段的多条数据方式
2022/01/22 MySQL
Ruby序列化和持久化存储 Marshal和Pstore介绍
2022/04/18 Ruby