根据朋友给的一份原理写的 感觉还挺清楚

#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】连通区域提取的更多相关文章

  1. Opencv2系列学习笔记10(提取连通区域轮廓)

    连通区域指的是二值图像中相连像素组成的形状.而内.外轮廓的概念及opencv1中如何提取二值图像的轮廓见我的这篇博客:http://blog.csdn.net/lu597203933/article/ ...

  2. Opencv2系列学习笔记10(提取连通区域轮廓) 另一个

    http://blog.csdn.net/lu597203933/article/details/17362457 连通区域指的是二值图像中相连像素组成的形状.而内.外轮廓的概念及opencv1中如何 ...

  3. [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), ...

  4. 使用OpenCV查找二值图中最大连通区域

    http://blog.csdn.net/shaoxiaohu1/article/details/40272875 使用OpenCV查找二值图中最大连通区域 标签: OpenCVfindCoutour ...

  5. OpenCV:二值图像连通区域分析与标记算法实现

    http://blog.csdn.net/cooelf/article/details/26581539?utm_source=tuicool&utm_medium=referral Open ...

  6. ​​​​​​​ARCGIS API for Python进行城市区域提取

    ​ArcGIS API for Python主要用于Web端的扩展和开发,提供简单易用.功能强大的Python库,以及大数据分析能力,可轻松实现实时数据.栅格数据.空间数据等多源数据的接入和GIS分析 ...

  7. 《图像处理实例》 之 目标旋转矫正(基于区域提取、DFT变换)

    目标:1.把矩形旋转正.          2.把文字旋转校正.                                                                     ...

  8. matlab函数_连通区域

    1. matlab函数bwareaopen──删除小面积对象格式:BW2 = bwareaopen(BW,P,conn)作用:删除二值图像BW中面积小于P的对象,默认情况下使用8邻域.算法:(1)De ...

  9. 【转】matlab函数_连通区域

    转载自einyboy的博文Matlab的regionprops详解 1. matlab函数bwareaopen──删除小面积对象格式:BW2 = bwareaopen(BW,P,conn)作用:删除二 ...

随机推荐

  1. Perl 正则表达式

    匹配:m/<regexp>;/ (还可以简写为 /<regexp>;/ ,略去 m)替换:s/<pattern>;/<replacement>;/转化: ...

  2. POJ 2486 Apple Tree

    好抽象的树形DP......... Apple Tree Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6411 Accepte ...

  3. eclipse无法自动识别出svn项目

    因为重新安装了svn插件,重启后发现原来的svn项目无法自动识别出来,连Team->Share Project都没有,而本地用tortoiseSvn是可以正常操作的. 后来我把项目删除然后重新导 ...

  4. wordpress不用插件实现Pagenavi页面导航功能

    Pagenavi 是一个很好的功能,现在 WordPress 博客一般都是使用 WP-Pagenavi 插件来实现,但是如果插件一多的话整个wordpress效率就降低了,我们力求用尽量少的插件来实现 ...

  5. MySQL监控系统MySQL MTOP的搭建(转VIII)

    MySQLMTOP是一个由Python+PHP开发的MySQL企业级监控系统.系统由Python实现多进程数据采集和告警,PHP实现WEB展示和管理.最重要是MySQL服务器无需安装任何Agent,只 ...

  6. Unity手游之路<十>自动寻路Navmesh之跳跃,攀爬,斜坡

    http://blog.csdn.net/janeky/article/details/17598113 在之前的几篇Blog总,我们已经系统学习了自动寻路插件Navmesh的相关概念和细节.然而,如 ...

  7. leetcode 124. Binary Tree Maximum Path Sum

    Given a binary tree, find the maximum path sum. For this problem, a path is defined as any sequence ...

  8. 对C++虚函数、虚函数表的简单理解

    一.虚函数的作用 以一个通用的图形类来了解虚函数的定义,代码如下: #include "stdafx.h" #include <iostream> using name ...

  9. python 字节与字符串转换

    name = 'laogaoyang' nameBytes = name.encode('utf-8') # 字节 nameStr = nameBytes.decode('utf-8')# 字符串 p ...

  10. zipimport.ZipImportError: can't decompress data; zlib not available 解决办法

    第一步,下载python-pip的tar包 # wget https://pypi.python.org/packages/source/p/pip/pip-1.3.1.tar.gz --no-che ...