根据朋友给的一份原理写的 感觉还挺清楚
#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, 0, MAXWIDTH * MAXHEIGHT * sizeof(PTNode)); int nodenum = 0; int row, col; nodes[0].data = 0; nodes[0].parent = -1; for(row = 0; row < imgsrc.rows; row++) { for(col = 0; col < imgsrc.cols; col++) { if(imgsrc.at<uchar>(row, col) == 0) //像素为0的认为是背景 全黑色 { imgdst.at<uchar>(row, col) = 0; } else //前景 { if(row != 0 && col != 0) //不是边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - 1)) // 判断 先左 后上 { imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row, col - 1); //如果和左边相同 标号和左边相同 if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - 1, col) && imgdst.at<uchar>(row, col) != imgdst.at<uchar>(row - 1, col)) //同时与左边 上边相连 且两个序号不同 { imgdst.at<uchar>(row, col) = (imgdst.at<uchar>(row, col) > imgdst.at<uchar>(row - 1, col)) ? imgdst.at<uchar>(row - 1, col) : imgdst.at<uchar>(row, col); //取小的编号 PTNode nodetmp1, nodetmp2; nodetmp1 = nodes[imgdst.at<uchar>(row, col - 1)]; nodetmp2 = nodes[imgdst.at<uchar>(row - 1, col)]; while(nodetmp1.parent != -1) { nodetmp1 = nodes[nodetmp1.parent]; } while(nodetmp2.parent != -1) { 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 - 1, col)) //仅与上面相同 序号等于上面 { imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row - 1, col); } else //与两个方向的序号都不同 新建一个序号 序号与位置相同 { nodenum++; imgdst.at<uchar>(row, col) = nodenum; nodes[imgdst.at<uchar>(row, col)].parent = -1; nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col); } } else if(row == 0 && col != 0) //横向边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - 1)) { imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row, col - 1); } else { nodenum++; imgdst.at<uchar>(row, col) = nodenum; nodes[imgdst.at<uchar>(row, col)].parent = -1; nodes[imgdst.at<uchar>(row, col)].data = imgdst.at<uchar>(row, col); } } else if(col == 0 && row != 0) //竖向边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - 1, col)) { imgdst.at<uchar>(row, col) = imgdst.at<uchar>(row - 1, col); } else { nodenum++; imgdst.at<uchar>(row, col) = nodenum; nodes[imgdst.at<uchar>(row, col)].parent = -1; 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 = -1; 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, " "); //} //把森林中每一个颗树都标成统一的颜色 for(row = 0; row < imgsrc.rows; row++) { for(col = 0; col < imgsrc.cols; col++) { PTNode nodetmp = nodes[imgdst.at<uchar>(row, col)]; while(nodetmp.parent != -1) { nodetmp = nodes[nodetmp.parent]; } imgdst.at<uchar>(row, col) = nodetmp.data * 52 % 255; //随意设个颜色显示 } } } void main() { IplImage* img = cvLoadImage("D:\Users\CCL\1.jpg", 0); IplImage* imgdst = cvCreateImage(cvGetSize(img), 8, 1); cvThreshold(img,img,125,255,0); cvShowImage("ori", img); Mat src(img), dst(imgdst); GetCCL(src, dst); cvShowImage("ccl", imgdst); cvWaitKey(0); }
效果:
但是下面的图片出了问题:
字母检测的很凌乱
但是单独把一个字母拿出来 放大再检测就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, 0, MAXWIDTH * MAXHEIGHT * sizeof(PTNode)); int nodenum = 0; int row, col; nodes[0].data = 0; nodes[0].parent = -1; for(row = 0; row < imgsrc.rows; row++) { for(col = 0; col < imgsrc.cols; col++) { if(imgsrc.at<uchar>(row, col) == 0) //像素为0的认为是背景 全黑色 { tmp.at<int>(row, col) = 0; } else //前景 { if(row != 0 && col != 0) //不是边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - 1)) // 判断 先左 后上 { tmp.at<int>(row, col) = tmp.at<int>(row, col - 1); //如果和左边相同 标号和左边相同 if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - 1, col) && tmp.at<int>(row, col) != tmp.at<int>(row - 1, col)) //同时与左边 上边相连 且两个序号不同 { tmp.at<int>(row, col) = (tmp.at<int>(row, col) > tmp.at<int>(row - 1, col)) ? tmp.at<int>(row - 1, col) : tmp.at<int>(row, col); //取小的编号 PTNode nodetmp1, nodetmp2; nodetmp1 = nodes[tmp.at<int>(row, col - 1)]; nodetmp2 = nodes[tmp.at<int>(row - 1, col)]; while(nodetmp1.parent != -1) { nodetmp1 = nodes[nodetmp1.parent]; } while(nodetmp2.parent != -1) { 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 - 1, col)) //仅与上面相同 序号等于上面 { tmp.at<int>(row, col) = tmp.at<int>(row - 1, col); } else //与两个方向的序号都不同 新建一个序号 序号与位置相同 { nodenum++; tmp.at<int>(row, col) = nodenum; nodes[tmp.at<int>(row, col)].parent = -1; nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col); } } else if(row == 0 && col != 0) //横向边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row, col - 1)) { tmp.at<int>(row, col) = tmp.at<int>(row, col - 1); } else { nodenum++; tmp.at<int>(row, col) = nodenum; //这里有问题, nodenum可能会大于255 但是传给tmp.at<int>(row, col) 时被转换为uchar型 nodes[tmp.at<int>(row, col)].parent = -1; nodes[tmp.at<int>(row, col)].data = tmp.at<int>(row, col); } } else if(col == 0 && row != 0) //竖向边界 { if(imgsrc.at<uchar>(row, col) == imgsrc.at<uchar>(row - 1, col)) { tmp.at<int>(row, col) = tmp.at<int>(row - 1, col); } else { nodenum++; tmp.at<int>(row, col) = nodenum; nodes[tmp.at<int>(row, col)].parent = -1; 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 = -1; 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, " "); //} //把森林中每一个颗树都标成统一的颜色 for(row = 0; row < imgsrc.rows; row++) { for(col = 0; col < imgsrc.cols; col++) { PTNode nodetmp = nodes[tmp.at<int>(row, col)]; while(nodetmp.parent != -1) { nodetmp = nodes[nodetmp.parent]; } imgdst.at<uchar>(row, col) = nodetmp.data * 51 % 255; //随意设个颜色显示 } } } void main() { IplImage* img = cvLoadImage("D:\Users\2.jpg", 0); IplImage* imgdst = cvCreateImage(cvGetSize(img), 8, 1); cvThreshold(img,img,125,255,0); cvNot(img, img); cvShowImage("ori", img); Mat src(img), dst(imgdst); GetCCL(src, dst); cvShowImage("ccl", imgdst); cvWaitKey(0); }
修正后结果就好了。有几个字母看起来像是丢了,其实是因为我随机选颜色,可能导致用黑色填充。