zoukankan      html  css  js  c++  java
  • 【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, 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);
    }

    修正后结果就好了。有几个字母看起来像是丢了,其实是因为我随机选颜色,可能导致用黑色填充。

     

  • 相关阅读:
    IoC就是IoC,不是什么技术,与GoF一样,是一种 设计模式。
    控制反转是Spring框架的核心。
    一种是CI(Constructor Injection)构造方法注入,另一种是SI(Set Injection) set 注入
    IOC 的理解与解释
    java 单例模式5种写法
    AOP(Aspect Oriented Programming),即面向切面编程
    AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题
    ioc和aop的区别?
    JAVA设计模式之单例模式
    详解JNDI的lookup资源引用 java:/comp/env
  • 原文地址:https://www.cnblogs.com/dplearning/p/3817766.html
Copyright © 2011-2022 走看看