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

#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. jelq

    初级 The Newbie Routine 5 minutes hot wrap 5 minutes manual stretch (ten 30-second stretches) 10 minut ...

  2. ansible的使用技巧

    #查看ansible的帮助 $ ansible -h   #ansible 指定不通的模块执行 $ ansible -i /etc/ansible/hosts  docker -u root -m c ...

  3. springmvc之图片上传

    1.接收到的是图片的流时 //上传头像 @RequestMapping(value = "/uploadHeadSculpture", method = RequestMethod ...

  4. Javascript高级程序设计——this、闭包、函数表达式

    在javascript中函数声明会被提升,而函数表达式不会被提升.当函数执行时,会创建一个执行环境和相应的作用域链,然后利用arguments和其他的命名参数的值来初始化函数的活动对象,作用域链链中所 ...

  5. C#GDI+编程基础(一:Graphics画布类)

    GDI+存在的意义:将变成与具体硬件实现细节分开. GDI+步骤:获取画布,绘制图像.处理图像 命名空间: using System.Drawing;//提供对GDI+基本图形功能的访问 using ...

  6. Android开发App工程结构搭建

    本文算是一篇漫谈,谈一谈关于android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角 ...

  7. java中线程分两种,守护线程和用户线程。

    java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...

  8. java使用split分隔,需要注意的点

    String severName = "10.6.62.244"; System.out.println(severName.split(".").length ...

  9. Unity开发-你必须知道的优化建议

    转自:http://blog.csdn.net/leonwei/article/details/18042603 最近研究U3D开发,个人认为,精通一种新的技术,最快最好的方法就是看它的documen ...

  10. C++基础知识(2)---函数

    c++中的函数和C语言中的函数相比,增加了许多新的语法与功能.在这里总结一下c++中常用的引用函数,函数重载和内联函数. 1  引用参数 引用参数最常用的一个例子就是 交换 两个数,如下 void s ...