字符分割(二)
2015-11-07 15:56
246 查看
ANN在训练和识别时都只能将一个单独的数字作为样本,因此对于扫描图像中的多个连续数字需要进行分割。具体算法如下:
1,确定图像中字符的大致高度范围:先自下而上对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号,然后自上而下对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号。这两个行号就标志出了字符大致的高度范围。
2,确定每个字符的左起始和右终止位置:在第一步得到的高度范围内进行自左向右逐列扫描,遇到第一个黑色像素时,认为是字符分割的起始位,然后继续扫描,直到遇到有一列中没有黑色像素,认为是这个字符的右终止位置,准备开始进行下一个字符的分割。按照上述方法继续扫描,直到扫描到图像的最右端。这样就得到了每个字符的比较精确的快读范围。
3,在已知的每个字符比较精确的宽度范围内,再按照第一步的方法,分别自下而上和自上而下,逐行扫描,来获取每个字符精确的高度范围。
本文较前一篇字符分割的区别在于,增加了对矩形框的筛选功能,并对不需要的矩形框中的字符可以擦除。代码如下:
效果图:
上一篇代码的效果图:
可以看到,本文的代码擦除最后面的小矩形框。原图见上一篇博客。
1,确定图像中字符的大致高度范围:先自下而上对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号,然后自上而下对图像进行逐行扫描,直到遇到第一个黑素像素,记下行号。这两个行号就标志出了字符大致的高度范围。
2,确定每个字符的左起始和右终止位置:在第一步得到的高度范围内进行自左向右逐列扫描,遇到第一个黑色像素时,认为是字符分割的起始位,然后继续扫描,直到遇到有一列中没有黑色像素,认为是这个字符的右终止位置,准备开始进行下一个字符的分割。按照上述方法继续扫描,直到扫描到图像的最右端。这样就得到了每个字符的比较精确的快读范围。
3,在已知的每个字符比较精确的宽度范围内,再按照第一步的方法,分别自下而上和自上而下,逐行扫描,来获取每个字符精确的高度范围。
本文较前一篇字符分割的区别在于,增加了对矩形框的筛选功能,并对不需要的矩形框中的字符可以擦除。代码如下:
/**************************************************************** 功能: 对前景目标(如字符)进行划分,将各个字符的轮廓矩形返回 参数: img:输入等待分割的图像 int areaThreshold:面积阈值,当分割后的字符小于这个值时,会擦除该字符,用于去除噪音等小区域杂点干扰 注 : 只能处理二值图像 返回值: 返回分割字符的矩形框 ***************************************************************/ vector<RECT> Ctry::ObjectSegment(IplImage* img, int areaThreshold) { vector<RECT> vecRoughRECT; //粗略对象轮廓的矩形向量数组 vector<RECT> vecRECT; //精化后对象轮廓的矩形向量数组 vector<RECT> vecRECTBig; //存放精化对象区域中大的的矩形框 //清空用来表示每个对象的vector vecRoughRECT.clear(); vecRECT.clear(); vecRECTBig.clear(); int nTop, nBttom; //整体前景区域的上下边界 int nObjCnt = 0; //对象数目 //从上向下扫描,找到整体区域的前景的上边界 for (int i = 0; i < img->height; i++) { for (int j = 0; j < img->width; j++) { double pixel = cvGetReal2D(img, i, j); if (int(pixel) == 0) { nTop = i; i = img->height; //对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环 break; } } } //从下向上扫描,找到整体区域的前景的下边界 for (int i = img->height - 1; i >= 0; i--) { for (int j = 0; j < img->width; j++) { double pixel = cvGetReal2D(img, i, j); if (int(pixel) == 0) { nBttom = i; i = -1; //对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环 break; } } } bool bStartSeg = false; //是否已经开始某一个对象的分割 bool bBlackInCol; //某一列中是否包含黑色像素 RECT rt; //按列扫描,找到每一个目标的左右边界 for (int j = 0; j < img->width; j++) { bBlackInCol = false; for (int i = 0; i < img->height; i++) { double pixel = cvGetReal2D(img, i, j); if (int(pixel) == 0) { bBlackInCol = true; //该列中发现黑点 if (!bStartSeg) //还没有进入一个对象的分割 { bStartSeg = true;//进入一个对象的分割 rt.left = j; } else break; } } if (j == (img->width - 1)) //扫描到最后一列了,说明整个图像扫描完毕 { break; } //正处在分割状态,且扫描完一列都没有发现黑像素,表明当前对象分割完毕 if (bStartSeg && !bBlackInCol) { rt.right = j; //对象右边界确定 //对象的粗略上下边界(有待精化) rt.top = nTop; rt.bottom = nBttom; ::InflateRect(&rt, 1, 1); //矩形框膨胀一个像素,以免绘制时压到字符 vecRoughRECT.push_back(rt); //插入vector bStartSeg = false; //当前分割结束 nObjCnt++; //对象数目加1 } } RECT rtNEW; //存放精化对象区域的矩形框 //由于得到了精确的左右边界,现在可以精化矩形框的上下边界 int nSize = vecRoughRECT.size(); for (int nObj = 0; nObj < nSize; nObj++) { rt = vecRoughRECT[nObj]; rtNEW.left = rt.left - 1; rtNEW.right = rt.right + 1; //从上向下逐行扫描边界 for (int i = rt.top; i < rt.bottom; i++) { for (int j = rt.left; j < rt.right; j++) { double pixel = cvGetReal2D(img, i, j); if (int(pixel) == 0) { rtNEW.top = i - 1; //对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环 i = rt.bottom; break; } } } //从下向上逐行扫描边界 for (int i = rt.bottom - 1; i > rt.top; i--) { for (int j = rt.left; j < rt.right; j++) { double pixel = cvGetReal2D(img, i, j); if (int(pixel) == 0) { rtNEW.bottom = i + 1; //对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环 i = rt.top - 1; break; } } } vecRECT.push_back(rtNEW); } //提取较大的框,擦除小的矩形框 for (int i = 0; i < vecRECT.size(); i++) { int x = vecRECT[i].left; int y = vecRECT[i].top; int x1 = vecRECT[i].right; int y1 = vecRECT[i].bottom; int area = (x1 - x)*(y1 - y); //矩形的面积 //当面积小于阈值时,擦除当前矩形框中的前景,否则将当前矩形框压入新的vector if (area < areaThreshold) { for (int i = y; i < y1 + 1; i++) { for (int j = x; j < x1 + 1; j++) { cvSetReal2D(img, i, j, 255); } } } else { vecRECTBig.push_back(vecRECT[i]); } } //画矩形框,显示分割字符 for (int i = 0; i < vecRECTBig.size(); i++) { int x = vecRECTBig[i].left - 1; int y = vecRECTBig[i].top - 1; int x1 = vecRECTBig[i].right + 1; int y1 = vecRECTBig[i].bottom + 1; CvPoint pt1(x, y); CvPoint pt2(x1, y1); cvRectangle(img, pt1, pt2, CV_RGB(255, 0, 0), 1); } cvSaveImage("C:\\Users\\Administrator\\Desktop\\rect0.jpg", img); return vecRECTBig; }
效果图:
上一篇代码的效果图:
可以看到,本文的代码擦除最后面的小矩形框。原图见上一篇博客。
相关文章推荐
- 图像质量评价
- PHP获取搜索引擎关键字来源的函数(支持百度和谷歌等搜索引擎)
- poj3295 构造法
- MP3播放器团队项目
- 在一个字符串中提取子字符串
- phonegap3.4.0 创建工程
- java随堂笔记
- 创建maven项目,如何修改其jdk默认版本
- [06]常量、变量
- Qtcreator 常用快捷键总结
- PowerShell介绍 第一回 "Shell Everything"
- [05]数据
- OpenCV那个版本的比较好用、稳定,参考资料也较多的
- 为什么要io复用
- Android如何防止apk程序被反编译
- 使用第三方库MJRefresh上下刷新
- ioctl参数cmd=2错误
- get向服务器提交数据
- 随笔,第一天
- Android APK反编译就这么简单 详解(附图)