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

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

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

一、按上述方法实现过程

二值化结果如图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应该是这样子的

图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个像素时,二值化图去掉直线后的效果如下

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

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

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

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去除文字中乱入的线条实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • python+opencv实现霍夫变换检测直线

    本文实例为大家分享了python+opencv实现霍夫变换检测直线的具体代码,供大家参考,具体内容如下 python+opencv实现高斯平滑滤波 python+opencv实现阈值分割 功能: 创建一个滑动条来控制检测直线的长度阈值,即大于该阈值的检测出来,小于该阈值的忽略 注意:这里用的函数是HoughLinesP而不是HoughLines,因为HoughLinesP直接给出了直线的断点,在画出线段的时候可以偷懒 代码: # -*- coding: utf-8 -*- import cv2

  • python opencv如何实现图片绘制

    这篇文章主要介绍了python opencv如何实现图片绘制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 点和圆 : circle(img,center,radius,color,thickness=None,lineType=None,shift=None).各参数意义及作用如下. img:待画圆所在的图像. center:待画圆的圆心坐标. radius:待画圆的半径. color:待画圆的边框颜色,颜色格式为bgr格式.就是通道值 th

  • 在Python 中实现图片加框和加字的方法

    第一步:安装opencv-python rpm -ivh opencn-python-2.4.5-3.el7.ppc64le.rpm 第二步:引用cv2 import cv2 第三步:读入图片,必须是全路径 im = cv2.imread(filename) 第四步:设置需要画框的左上角与右下角的坐标,必须是整数 sx1, sx2, sy1, sy2 cv2.rectangle(im,(int(sx1),int(sy1)),(int(sx2),int(sy2)),(0,255,0),3) 函数参

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

    今天上午,朋友发来一张图片如下.没错,这就是原图,他希望可以通过一些简单的算法将图中这条穿过单词间的直线去掉,使得到的结果能够通过他的文字识别算法并得出正确结果--The Techniques of Machine Vision. 乍一看这似乎挺简单,(1)将图像二值化:(2)找出这条直线:(3)将直线区域填成背景色(即白色):(4)再通过膨胀.腐蚀等操作将单词缺失的部分给补全.以上4步似乎可以满足要求,但测试发现,效果不尽人意. 一.按上述方法实现过程 二值化结果如图1.1所示,可以看到图像并

  • C#去除字符串中的反斜杠实例(推荐)

    如下,可以使用C#的Replace()方法来替换,但有一点需要注意的是backslash(反斜杠)是特殊字符. string s = "[\"aaaaaaaaaaaaaaaaaaaaaa\"]"; string ss = s.Replace("\\",""); Response.Write(ss); 以上这篇C#去除字符串中的反斜杠实例(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • python中opencv实现文字分割的实践

    图片文字分割的时候,常用的方法有两种.一种是投影法,适用于排版工整,字间距行间距比较宽裕的图像:还有一种是用OpenCV的轮廓检测,适用于文字不规则排列的图像. 投影法 对文字图片作横向和纵向投影,即通过统计出每一行像素个数,和每一列像素个数,来分割文字. 分别在水平和垂直方向对预处理(二值化)的图像某一种像素进行统计,对于二值化图像非黑即白,我们通过对其中的白点或者黑点进行统计,根据统计结果就可以判断出每一行的上下边界以及每一列的左右边界,从而实现分割的目的. 算法步骤: 使用水平投影和垂直投

  • python 基于opencv去除图片阴影

    一.前言 如果你自己打印过东西,应该有过这种经历.如果用自己拍的图片,在手机上看感觉还是清晰可见,但是一打印出来就是漆黑一片.比如下面这两张图片: 因为左边的图片有大片阴影,所以打印出来的图片不堪入目(因为打印要3毛钱,所以第二张图片只是我用程序模拟的效果). 那有什么办法可以解决吗?答案是肯定的,今天我们就来探讨几个去除阴影的方法. 二.如何去除阴影? 首先为了方便处理,我们通常会对图片进行灰度转换(即将图片转换成只有一个图层的灰色图像). 然后我们分析一下,在上面的图片中有三个主色调,分别是

  • 详解opencv去除背景算法的方法比较

    目录 背景减除法 (1)BackgroundSubtractorMOG (2)BackgroundSubtractorMOG2 (3)BackgroundSubtractorGMG 帧差法 最近做opencv项目时,使用肤色分割的方法检测目标物体时,背景带来的干扰非常让人头痛.于是先将背景分割出去,将影响降低甚至消除.由于初次接触opencv,叙述不当的地方还请指正. 背景减除法 (以下文字原文来源于https://docs.opencv.org/3.4.7/d8/d38/tutorial_bg

  • 如何去除Java中List集合中的重复数据

    1.循环list中的所有元素然后删除重复 public class duplicatRemoval { public static List removeDuplicate(List list){ for(int i=0;i<list. size()-1;i++){ for(int j=list.size()-1;j>i;j--){ if(list. get(j). equals(list.get(i))){ list.remove(j); } } } return list; } } 总结:

  • C#使用OpenCV剪切图像中的圆形和矩形的示例代码

    前言 本文主要介绍如何使用OpenCV剪切图像中的圆形和矩形. 准备工作 首先创建一个Wpf项目--WpfOpenCV,这里版本使用Framework4.7.2. 然后使用Nuget搜索[Emgu.CV],如下图. 这里的Emgu.CV选择4.3.0.3890版本,然后安装Emgu.CV和Emgu.CV.runtime.windows. 使用OPenCV剪切矩形 现在,我们进入项目,进行OPenCV的调用. 首先引入命名空间,如下: using Emgu.CV; using Emgu.CV.Cv

  • python+opencv实现文字颜色识别与标定功能

    最近接了一个比较简单的图像处理的单子,花了一点时间随便写了一下: 数据集客户没有是自己随便创建的: 程序如下: """ Code creation time:September 11, 2021 Author:PanBo Realize function:It mainly realizes the recognition and calibration of fonts with different colors """ import nump

  • 利用python OpenCV去除视频水印

    上面的图片展示的是视频中的某一帧,需要将图片中的,实体拓展和退出全屏以及右上角的两个圆圈按钮给删除掉. 解决思路一个很简单的想法就是,从图片上其它两块背景相同的地方,拷贝两个块替换掉按钮位置的块 用下面的代码先从视频中导出一帧图片,然后统计需要删除按钮的坐标位置 实现代码如下: import cv2 # 创建读取视频的类 capture = cv2.VideoCapture("watermark.mp4") # 得到视频的高度 height = capture.get(cv2.CAP_

  • Java + Selenium + OpenCV解决自动化测试中的滑块验证问题

    目录 1.滑块验证思路 2.使用OpenCV进行图片解析 2.1 OpenCV引入项目 2.2 实现图片解析,计算所需距离 2.3 算法解析说明 3.Selenium处理滑块滑动 4.最终效果 最近工作过程中,一个常用的被测网站突然增加了滑块验证环节,导致整个自动化项目失效了. 为了解决这个滑块验证问题,在网上查阅了一些资料后,总结并实现了解决方案,现记录如下. 1.滑块验证思路 被测对象的滑块对象长这个样子.相对而言是比较简单的一种形式,需要将左侧的拼图通过下方的滑块进行拖动,嵌入到右侧空槽中

随机推荐