【CCL】连通区域提取
根据朋友给的一份原理写的 感觉还挺清楚
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
using namespace cv; #define MAXWIDTH 352
#define MAXHEIGHT 288 typedef struct PTNode{
int data;
int parent;
}PTNode; void GetCCL(Mat &imgsrc, Mat &imgdst)
{ PTNode nodes[MAXWIDTH * MAXHEIGHT]; //线性树 数据的位置 与 数据本身 相同 即 nodes[x].data = x
memset(nodes, , MAXWIDTH * MAXHEIGHT * sizeof(PTNode));
int nodenum = ;
int row, col;
nodes[].data = ;
nodes[].parent = -;
for(row = ; row < imgsrc.rows; row++)
{
for(col = ; col < imgsrc.cols; col++)
{
if(imgsrc.at<uchar>(row, col) == ) //像素为0的认为是背景 全黑色
{
imgdst.at<uchar>(row, col) = ;
}
else //前景
{
if(row != && col != ) //不是边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - )) // 判断 先左 后上
{
imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row, col - ); //如果和左边相同 标号和左边相同
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col) && imgdst.at<uchar>(row, col) != imgdst.at<uchar>(row - , col)) //同时与左边 上边相连 且两个序号不同
{
imgdst.at<uchar>(row, col) = (imgdst.at<uchar>(row, col) > imgdst.at<uchar>(row - , col)) ? imgdst.at<uchar>(row - , col) : imgdst.at<uchar>(row, col); //取小的编号 PTNode nodetmp1, nodetmp2;
nodetmp1 = nodes[imgdst.at<uchar>(row, col - )];
nodetmp2 = nodes[imgdst.at<uchar>(row - , col)];
while(nodetmp1.parent != -)
{
nodetmp1 = nodes[nodetmp1.parent];
}
while(nodetmp2.parent != -)
{
nodetmp2 = nodes[nodetmp2.parent];
}
if(nodetmp2.data > nodetmp1.data) //小的序号做parent 大序号做child
{
nodes[nodetmp2.data].parent = nodetmp1.data; //这里一定要对nodes中的值修改, 直接写nodetmp2.parent = nodetmp1.data 是不行的因为nodetmp2只是一个局部变量 nodes[]里的值根本没有修改
}
else if(nodetmp2.data < nodetmp1.data)
{
nodes[nodetmp1.data].parent = nodetmp2.data;
} }
}
else if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col)) //仅与上面相同 序号等于上面
{
imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row - , col);
}
else //与两个方向的序号都不同 新建一个序号 序号与位置相同
{
nodenum++;
imgdst.at<uchar>(row, col) = nodenum;
nodes[imgdst.at<uchar>(row, col)].parent = -;
nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col);
}
}
else if(row == && col != ) //横向边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - ))
{
imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row, col - );
}
else
{
nodenum++;
imgdst.at<uchar>(row, col) = nodenum;
nodes[imgdst.at<uchar>(row, col)].parent = -;
nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col);
}
}
else if(col == && row != ) //竖向边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col))
{
imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row - , col);
}
else
{
nodenum++;
imgdst.at<uchar>(row, col) = nodenum;
nodes[imgdst.at<uchar>(row, col)].parent = -;
nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col);
}
}
else //开始的(0 ,0)点 直接新建
{
nodenum++;
imgdst.at<uchar>(row, col) = nodenum;
nodes[imgdst.at<uchar>(row, col)].parent = -;
nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col);
}
} }
} //FILE * out = fopen("D:\\dst.txt", "w");
//for(row = 0; row < imgsrc.rows; row++)
//{
// for(col = 0; col < imgsrc.cols; col++)
// {
// fprintf(out, "%d ", imgdst.at<uchar>(row, col));
// }
// fprintf(out, "\n");
//}
//把森林中每一个颗树都标成统一的颜色
for(row = ; row < imgsrc.rows; row++)
{
for(col = ; col < imgsrc.cols; col++)
{
PTNode nodetmp = nodes[imgdst.at<uchar>(row, col)];
while(nodetmp.parent != -)
{
nodetmp = nodes[nodetmp.parent];
}
imgdst.at<uchar>(row, col) = nodetmp.data * % ; //随意设个颜色显示
}
} } void main()
{
IplImage* img = cvLoadImage("D:\\Users\\CCL\\1.jpg", );
IplImage* imgdst = cvCreateImage(cvGetSize(img), , );
cvThreshold(img,img,,,);
cvShowImage("ori", img);
Mat src(img), dst(imgdst);
GetCCL(src, dst);
cvShowImage("ccl", imgdst);
cvWaitKey();
}
效果:

但是下面的图片出了问题:

字母检测的很凌乱
但是单独把一个字母拿出来 放大再检测就ok

找到上面多字母问题的原因了。问题出在下面一句:
imgdst.at<uchar>(row, col) = nodenum;
这里nodenum是可能超过255的 但是在传给imgdst时被强制转换成了uchar型,导致后面的结果出错。
用tmp.create(imgsrc.rows, imgsrc.cols, CV_32F);来修改错误。
修改后的代码如下:
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
using namespace cv; #define MAXWIDTH 352
#define MAXHEIGHT 288 typedef struct PTNode{
int data;
int parent;
}PTNode; void GetCCL(Mat &imgsrc, Mat &imgdst)
{
Mat tmp;
tmp.create(imgsrc.rows, imgsrc.cols, CV_32F);
PTNode nodes[MAXWIDTH * MAXHEIGHT]; //线性树 数据的位置 与 数据本身 相同 即 nodes[x].data = x
memset(nodes, , MAXWIDTH * MAXHEIGHT * sizeof(PTNode));
int nodenum = ;
int row, col;
nodes[].data = ;
nodes[].parent = -;
for(row = ; row < imgsrc.rows; row++)
{
for(col = ; col < imgsrc.cols; col++)
{
if(imgsrc.at<uchar>(row, col) == ) //像素为0的认为是背景 全黑色
{
tmp.at<int>(row, col) = ;
}
else //前景
{
if(row != && col != ) //不是边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - )) // 判断 先左 后上
{
tmp.at<int>(row, col) = tmp.at<int>(row, col - ); //如果和左边相同 标号和左边相同
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col) && tmp.at<int>(row, col) != tmp.at<int>(row - , col)) //同时与左边 上边相连 且两个序号不同
{
tmp.at<int>(row, col) = (tmp.at<int>(row, col) > tmp.at<int>(row - , col)) ? tmp.at<int>(row - , col) : tmp.at<int>(row, col); //取小的编号 PTNode nodetmp1, nodetmp2;
nodetmp1 = nodes[tmp.at<int>(row, col - )];
nodetmp2 = nodes[tmp.at<int>(row - , col)];
while(nodetmp1.parent != -)
{
nodetmp1 = nodes[nodetmp1.parent];
}
while(nodetmp2.parent != -)
{
nodetmp2 = nodes[nodetmp2.parent];
}
if(nodetmp2.data > nodetmp1.data) //小的序号做parent 大序号做child
{
nodes[nodetmp2.data].parent = nodetmp1.data; //这里一定要对nodes中的值修改, 直接写nodetmp2.parent = nodetmp1.data 是不行的因为nodetmp2只是一个局部变量 nodes[]里的值根本没有修改
}
else if(nodetmp2.data < nodetmp1.data)
{
nodes[nodetmp1.data].parent = nodetmp2.data;
} }
}
else if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col)) //仅与上面相同 序号等于上面
{
tmp.at<int>(row, col) = tmp.at<int>(row - , col);
}
else //与两个方向的序号都不同 新建一个序号 序号与位置相同
{
nodenum++;
tmp.at<int>(row, col) = nodenum;
nodes[tmp.at<int>(row, col)].parent = -;
nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col);
}
}
else if(row == && col != ) //横向边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - ))
{
tmp.at<int>(row, col) = tmp.at<int>(row, col - );
}
else
{
nodenum++;
tmp.at<int>(row, col) = nodenum; //这里有问题, nodenum可能会大于255 但是传给tmp.at<int>(row, col) 时被转换为uchar型
nodes[tmp.at<int>(row, col)].parent = -;
nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col);
}
}
else if(col == && row != ) //竖向边界
{
if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - , col))
{
tmp.at<int>(row, col) = tmp.at<int>(row - , col);
}
else
{
nodenum++;
tmp.at<int>(row, col) = nodenum;
nodes[tmp.at<int>(row, col)].parent = -;
nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col);
}
}
else //开始的(0 ,0)点 直接新建
{
nodenum++;
tmp.at<int>(row, col) = nodenum;
nodes[tmp.at<int>(row, col)].parent = -;
nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col);
}
} }
} //FILE * out = fopen("D:\\dst.txt", "w");
//for(row = 0; row < imgsrc.rows; row++)
//{
// for(col = 0; col < imgsrc.cols; col++)
// {
// fprintf(out, "%d ", tmp.at<int>(row, col));
// }
// fprintf(out, "\n");
//}
//把森林中每一个颗树都标成统一的颜色
for(row = ; row < imgsrc.rows; row++)
{
for(col = ; col < imgsrc.cols; col++)
{
PTNode nodetmp = nodes[tmp.at<int>(row, col)];
while(nodetmp.parent != -)
{
nodetmp = nodes[nodetmp.parent];
}
imgdst.at<uchar>(row, col) = nodetmp.data * % ; //随意设个颜色显示
}
} } void main()
{
IplImage* img = cvLoadImage("D:\\Users\\2.jpg", );
IplImage* imgdst = cvCreateImage(cvGetSize(img), , );
cvThreshold(img,img,,,);
cvNot(img, img);
cvShowImage("ori", img);
Mat src(img), dst(imgdst);
GetCCL(src, dst);
cvShowImage("ccl", imgdst);
cvWaitKey();
}

修正后结果就好了。有几个字母看起来像是丢了,其实是因为我随机选颜色,可能导致用黑色填充。
【CCL】连通区域提取的更多相关文章
- Opencv2系列学习笔记10(提取连通区域轮廓)
连通区域指的是二值图像中相连像素组成的形状.而内.外轮廓的概念及opencv1中如何提取二值图像的轮廓见我的这篇博客:http://blog.csdn.net/lu597203933/article/ ...
- Opencv2系列学习笔记10(提取连通区域轮廓) 另一个
http://blog.csdn.net/lu597203933/article/details/17362457 连通区域指的是二值图像中相连像素组成的形状.而内.外轮廓的概念及opencv1中如何 ...
- [LeetCode] Number of Connected Components in an Undirected Graph 无向图中的连通区域的个数
Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...
- 使用OpenCV查找二值图中最大连通区域
http://blog.csdn.net/shaoxiaohu1/article/details/40272875 使用OpenCV查找二值图中最大连通区域 标签: OpenCVfindCoutour ...
- OpenCV:二值图像连通区域分析与标记算法实现
http://blog.csdn.net/cooelf/article/details/26581539?utm_source=tuicool&utm_medium=referral Open ...
- ARCGIS API for Python进行城市区域提取
ArcGIS API for Python主要用于Web端的扩展和开发,提供简单易用.功能强大的Python库,以及大数据分析能力,可轻松实现实时数据.栅格数据.空间数据等多源数据的接入和GIS分析 ...
- 《图像处理实例》 之 目标旋转矫正(基于区域提取、DFT变换)
目标:1.把矩形旋转正. 2.把文字旋转校正. ...
- matlab函数_连通区域
1. matlab函数bwareaopen──删除小面积对象格式:BW2 = bwareaopen(BW,P,conn)作用:删除二值图像BW中面积小于P的对象,默认情况下使用8邻域.算法:(1)De ...
- 【转】matlab函数_连通区域
转载自einyboy的博文Matlab的regionprops详解 1. matlab函数bwareaopen──删除小面积对象格式:BW2 = bwareaopen(BW,P,conn)作用:删除二 ...
随机推荐
- 字符编码-UNICODE,GBK,UTF-8区别【转转】
字符编码介绍及不同编码区别 今天看到这篇关于字符编码的文章,抑制不住喜悦(总结的好详细)所以转到这里来.转自:祥龙之子http://www.cnblogs.com/cy163/archive/2007 ...
- Android与Dalvik
自学android的同事说:Android 这个妈蛋!! 当初就应该选择c++/c 来开发.现在为了效率又搞ART.简直是折腾,art在android5.0 的时候就是默认了.前段时间还在学习andr ...
- java系列-使用maven创建web项目(二)
推荐2个maven找jar包配置的网站,只需要搜索关键字即可找到需要的Jar包,非常方便,比如:MySQL就可以找到mysql-connect-Java.jar. http://search.mave ...
- 从程序员到CTO的Java技术路线图
转自:http://zz563143188.iteye.com/blog/1877266 企业级项目实战地址:http://zz563143188.iteye.com/blog/1825168 开发资 ...
- Why Reflection is slowly?(Trail: The Reflection API)
反射的使用 反射通常用于在JVM中应用程序运行中需要检查或者修改运行时行为的项目.这是一个相对高级的特性,并且仅仅可以被对深刻理解java原理的开发者使用.这里给出一个警告的意见,反射是一个强大的技术 ...
- 运动曲线提升CSS动画效果
原文链接 译文\译者鞠大宝 先有UI动画,然后才会有好的UI动画.好的动画会让人惊叹“哇哦!”——因为页面看上去很流畅.很漂亮,最重要的是,自然,一点都不会让人觉得不和谐或者僵硬死板.如果你经常逛Dr ...
- STL next_permutation和prev_permutation函数
利用next_permutation实现全排列升序输出,从尾到头找到第一个可以交换的位置, 直接求到第一个不按升序排列的序列. #include <iostream> #include & ...
- BZOJ 3907: 网格
Description 求不跨过直线 \(y=x\) ,到达 \((n,m)\) 的方案数. Sol 组合数学+高精度. 这个推导过程跟 \(Catalan\) 数是一样的. 答案就是 \(C^{n+ ...
- php批量下载文件
最近用codeigniter开发一个图片网站,发现单文件下载很容易实现,批量下载的话,就有点麻烦. 普通php下载比较简单,比如我封装的一个函数: function shao_download($fi ...
- [转]Mysql海量数据存储和解决方案之一—分布式DB方案
1) 分布式DB水平切分中用到的主要关键技术:分库,分表,M-S,集群,负载均衡 2) 需求分析:一个大型互联网应用每天几十亿的PV对DB造成了相当高的负载,对系统的稳定性的扩展性带来极大挑战. 3 ...