使用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查询示例
Mar 26 Python
Python with的用法
Aug 22 Python
Django数据库操作的实例(增删改查)
Sep 04 Python
Python程序运行原理图文解析
Feb 10 Python
wxPython实现窗口用图片做背景
Apr 25 Python
python更改已存在excel文件的方法
May 03 Python
Python3.6基于正则实现的计算器示例【无优化简单注释版】
Jun 14 Python
解决pycharm的Python console不能调试当前程序的问题
Jan 20 Python
python matplotlib中的subplot函数使用详解
Jan 19 Python
python实现逆滤波与维纳滤波示例
Feb 26 Python
使用Django实现把两个模型类的数据聚合在一起
Mar 28 Python
parser.add_argument中的action使用
Apr 20 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开发入门教程之面向对象
2006/12/05 PHP
PHP简单实现循环链表功能示例
2017/11/10 PHP
PHP Ajax跨域问题解决方案代码实例
2020/08/01 PHP
JS日期和时间选择控件升级版(自写)
2013/08/02 Javascript
导航跟随滚动条置顶移动示例代码
2013/09/11 Javascript
javascript获取checkbox复选框获取选中的选项
2014/08/12 Javascript
Javascript学习笔记之数组的构造函数
2014/11/23 Javascript
JS实现的5级联动Select下拉选择框实例
2015/08/17 Javascript
canvas雪花效果核心代码分享
2017/02/19 Javascript
vue-cli webpack 引入jquery的方法
2018/01/10 jQuery
微信小程序如何自定义table组件
2019/06/29 Javascript
JS 实现发送短信验证码的“59秒后重新发送验证短信”功能
2019/08/23 Javascript
微信小程序里引入SVG矢量图标的方法
2019/09/20 Javascript
Python字符串切片操作知识详解
2016/03/28 Python
使用PyInstaller将Python程序文件转换为可执行程序文件
2016/07/08 Python
Python一句代码实现找出所有水仙花数的方法
2018/11/13 Python
python实现贪吃蛇小游戏
2020/03/21 Python
Django中FilePathField字段的用法
2020/05/21 Python
pycharm不以pytest方式运行,想要切换回普通模式运行的操作
2020/09/01 Python
纯DOM+CSS3实现简单的小风车动画
2016/09/27 HTML / CSS
html5的自定义data-*属性与jquery的data()方法的使用
2014/07/02 HTML / CSS
Nike西班牙官方网站:Nike.com (ES)
2017/10/30 全球购物
Dillard’s百货官网:Dillards.com
2018/05/26 全球购物
《姥姥的剪纸》教学反思
2014/02/25 职场文书
企业业务员岗位职责
2014/03/14 职场文书
消防安全责任书范本
2014/04/15 职场文书
质量保证书范本
2014/04/29 职场文书
产品售后服务承诺书
2014/05/21 职场文书
优秀大学生自荐信
2014/06/09 职场文书
房产公证书样本
2015/01/23 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
《刺客之王:C罗全景传记》:时代从来不会亏待手艺人
2019/11/28 职场文书
创业计划书之闲置物品置换中心
2019/12/25 职场文书
Nginx服务器添加Systemd自定义服务过程解析
2021/03/31 Servers
PostgreSQL自动更新时间戳实例代码
2021/11/27 PostgreSQL
vue项目支付功能代码详解
2022/02/18 Vue.js