zoukankan      html  css  js  c++  java
  • OpenCV:二值图像连通区域分析与标记算法实现

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

     

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

    标签: OpenCV连通图两边扫描法种子填充法形成标记算法
     分类:
     
     

    目录(?)[+]

     

    编译环境:

    操作系统:Win8.1  64位

    IDE平台:Visual Studio 2013 Ultimate

    OpenCV:2.4.8

    一、连通域

        在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有2种:4邻接与8邻接。4邻接一共4个点,即上下左右,如下左图所示。8邻接的点一共有8个,包括了对角线位置的点,如下右图所示。

    image       image

       如果像素点A与B邻接,我们称A与B连通,于是我们不加证明的有如下的结论:

       如果A与B连通,B与C连通,则A与C连通。

       在视觉上看来,彼此连通的点形成了一个区域,而不连通的点形成了不同的区域。这样的一个所有的点彼此连通点构成的集合,我们称为一个连通区域。

       下面这符图中,如果考虑4邻接,则有3个连通区域;如果考虑8邻接,则有2个连通区域。(注:图像是被放大的效果,图像正方形实际只有4个像素)。

    image

    二、连通区域的标记

    1)Two-Pass(两遍扫描法)

    下面给出Two-Pass算法的简单步骤:

    (1)第一次扫描:

    访问当前像素B(x,y),如果B(x,y) == 1:

    a、如果B(x,y)的领域中像素值都为0,则赋予B(x,y)一个新的label:

    label += 1, B(x,y) = label;

    b、如果B(x,y)的领域中有像素值 > 1的像素Neighbors:

    1)将Neighbors中的最小值赋予给B(x,y):

    B(x,y) = min{Neighbors}

    2)记录Neighbors中各个值(label)之间的相等关系,即这些值(label)同属同一个连通区域;

     labelSet[i] = { label_m, .., label_n },labelSet[i]中的所有label都属于同一个连通区域(注:这里可以有多种实现方式,只要能够记录这些具有相等关系的label之间的关系即可)

    (2)第二次扫描:

    访问当前像素B(x,y),如果B(x,y) > 1:

    a、找到与label = B(x,y)同属相等关系的一个最小label值,赋予给B(x,y);

    b、完成扫描后,图像中具有相同label值的像素就组成了同一个连通区域。

    2)Seed Filling(种子填充法)

         种子填充方法来源于计算机图形学,常用于对某个图形进行填充。思路:选取一个前景像素点作为种子,然后根据连通区域的两个基本条件(像素值相同、位置相邻)将与种子相邻的前景像素合并到同一个像素集合中,最后得到的该像素集合则为一个连通区域。

    下面给出基于种子填充法的连通区域分析方法:

    (1)扫描图像,直到当前像素点B(x,y) == 1:

    a、将B(x,y)作为种子(像素位置),并赋予其一个label,然后将该种子相邻的所有前景像素都压入栈中;

    b、弹出栈顶像素,赋予其相同的label,然后再将与该栈顶像素相邻的所有前景像素都压入栈中;

    c、重复b步骤,直到栈为空;

    此时,便找到了图像B中的一个连通区域,该区域内的像素值被标记为label;

    (2)重复第(1)步,直到扫描结束;

    扫描结束后,就可以得到图像B中所有的连通区域;

    三、程序代码

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #include "stdafx.h"  
    2. #include<iostream>  
    3. #include <string>  
    4. #include <list>  
    5. #include <vector>  
    6. #include <map>  
    7. #include <stack>  
    8. #include <opencv2/imgproc/imgproc.hpp>  
    9. #include <opencv2/highgui/highgui.hpp>  
    10. using namespace std;  
    11.   
    12. void Seed_Filling(const cv::Mat& binImg, cv::Mat& lableImg)   //种子填充法  
    13. {  
    14.     // 4邻接方法  
    15.   
    16.   
    17.     if (binImg.empty() ||  
    18.         binImg.type() != CV_8UC1)  
    19.     {  
    20.         return;  
    21.     }  
    22.   
    23.     lableImg.release();  
    24.     binImg.convertTo(lableImg, CV_32SC1);  
    25.   
    26.     int label = 1;    
    27.   
    28.     int rows = binImg.rows - 1;    
    29.     int cols = binImg.cols - 1;  
    30.     for (int i = 1; i < rows-1; i++)  
    31.     {  
    32.         int* data= lableImg.ptr<int>(i);  
    33.         for (int j = 1; j < cols-1; j++)  
    34.         {  
    35.             if (data[j] == 1)  
    36.             {  
    37.                 std::stack<std::pair<int,int>> neighborPixels;     
    38.                 neighborPixels.push(std::pair<int,int>(i,j));     // 像素位置: <i,j>  
    39.                 ++label;  // 没有重复的团,开始新的标签  
    40.                 while (!neighborPixels.empty())  
    41.                 {  
    42.                     std::pair<int,int> curPixel = neighborPixels.top(); //如果与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它  
    43.                     int curX = curPixel.first;  
    44.                     int curY = curPixel.second;  
    45.                     lableImg.at<int>(curX, curY) = label;  
    46.   
    47.                     neighborPixels.pop();  
    48.   
    49.                     if (lableImg.at<int>(curX, curY-1) == 1)  
    50.                     {//左边  
    51.                         neighborPixels.push(std::pair<int,int>(curX, curY-1));  
    52.                     }  
    53.                     if (lableImg.at<int>(curX, curY+1) == 1)  
    54.                     {// 右边  
    55.                         neighborPixels.push(std::pair<int,int>(curX, curY+1));  
    56.                     }  
    57.                     if (lableImg.at<int>(curX-1, curY) == 1)  
    58.                     {// 上边  
    59.                         neighborPixels.push(std::pair<int,int>(curX-1, curY));  
    60.                     }  
    61.                     if (lableImg.at<int>(curX+1, curY) == 1)  
    62.                     {// 下边  
    63.                         neighborPixels.push(std::pair<int,int>(curX+1, curY));  
    64.                     }  
    65.                 }         
    66.             }  
    67.         }  
    68.     }  
    69.       
    70. }  
    71.   
    72. void Two_Pass(const cv::Mat& binImg, cv::Mat& lableImg)    //两遍扫描法  
    73. {  
    74.     if (binImg.empty() ||  
    75.         binImg.type() != CV_8UC1)  
    76.     {  
    77.         return;  
    78.     }  
    79.   
    80.     // 第一个通路  
    81.   
    82.     lableImg.release();  
    83.     binImg.convertTo(lableImg, CV_32SC1);  
    84.   
    85.     int label = 1;   
    86.     std::vector<int> labelSet;  
    87.     labelSet.push_back(0);    
    88.     labelSet.push_back(1);    
    89.   
    90.     int rows = binImg.rows - 1;  
    91.     int cols = binImg.cols - 1;  
    92.     for (int i = 1; i < rows; i++)  
    93.     {  
    94.         int* data_preRow = lableImg.ptr<int>(i-1);  
    95.         int* data_curRow = lableImg.ptr<int>(i);  
    96.         for (int j = 1; j < cols; j++)  
    97.         {  
    98.             if (data_curRow[j] == 1)  
    99.             {  
    100.                 std::vector<int> neighborLabels;  
    101.                 neighborLabels.reserve(2);  
    102.                 int leftPixel = data_curRow[j-1];  
    103.                 int upPixel = data_preRow[j];  
    104.                 if ( leftPixel > 1)  
    105.                 {  
    106.                     neighborLabels.push_back(leftPixel);  
    107.                 }  
    108.                 if (upPixel > 1)  
    109.                 {  
    110.                     neighborLabels.push_back(upPixel);  
    111.                 }  
    112.   
    113.                 if (neighborLabels.empty())  
    114.                 {  
    115.                     labelSet.push_back(++label);  // 不连通,标签+1  
    116.                     data_curRow[j] = label;  
    117.                     labelSet[label] = label;  
    118.                 }  
    119.                 else  
    120.                 {  
    121.                     std::sort(neighborLabels.begin(), neighborLabels.end());  
    122.                     int smallestLabel = neighborLabels[0];    
    123.                     data_curRow[j] = smallestLabel;  
    124.   
    125.                     // 保存最小等价表  
    126.                     for (size_t k = 1; k < neighborLabels.size(); k++)  
    127.                     {  
    128.                         int tempLabel = neighborLabels[k];  
    129.                         int& oldSmallestLabel = labelSet[tempLabel];  
    130.                         if (oldSmallestLabel > smallestLabel)  
    131.                         {                             
    132.                             labelSet[oldSmallestLabel] = smallestLabel;  
    133.                             oldSmallestLabel = smallestLabel;  
    134.                         }                         
    135.                         else if (oldSmallestLabel < smallestLabel)  
    136.                         {  
    137.                             labelSet[smallestLabel] = oldSmallestLabel;  
    138.                         }  
    139.                     }  
    140.                 }                 
    141.             }  
    142.         }  
    143.     }  
    144.   
    145.     // 更新等价对列表  
    146.     // 将最小标号给重复区域  
    147.     for (size_t i = 2; i < labelSet.size(); i++)  
    148.     {  
    149.         int curLabel = labelSet[i];  
    150.         int preLabel = labelSet[curLabel];  
    151.         while (preLabel != curLabel)  
    152.         {  
    153.             curLabel = preLabel;  
    154.             preLabel = labelSet[preLabel];  
    155.         }  
    156.         labelSet[i] = curLabel;  
    157.     }  ;  
    158.   
    159.     for (int i = 0; i < rows; i++)  
    160.     {  
    161.         int* data = lableImg.ptr<int>(i);  
    162.         for (int j = 0; j < cols; j++)  
    163.         {  
    164.             int& pixelLabel = data[j];  
    165.             pixelLabel = labelSet[pixelLabel];    
    166.         }  
    167.     }  
    168. }  
    169. //彩色显示  
    170. cv::Scalar GetRandomColor()  
    171. {  
    172.     uchar r = 255 * (rand()/(1.0 + RAND_MAX));  
    173.     uchar g = 255 * (rand()/(1.0 + RAND_MAX));  
    174.     uchar b = 255 * (rand()/(1.0 + RAND_MAX));  
    175.     return cv::Scalar(b,g,r);  
    176. }  
    177.   
    178.   
    179. void LabelColor(const cv::Mat& labelImg, cv::Mat& colorLabelImg)   
    180. {  
    181.     if (labelImg.empty() ||  
    182.         labelImg.type() != CV_32SC1)  
    183.     {  
    184.         return;  
    185.     }  
    186.   
    187.     std::map<int, cv::Scalar> colors;  
    188.   
    189.     int rows = labelImg.rows;  
    190.     int cols = labelImg.cols;  
    191.   
    192.     colorLabelImg.release();  
    193.     colorLabelImg.create(rows, cols, CV_8UC3);  
    194.     colorLabelImg = cv::Scalar::all(0);  
    195.   
    196.     for (int i = 0; i < rows; i++)  
    197.     {  
    198.         const int* data_src = (int*)labelImg.ptr<int>(i);  
    199.         uchar* data_dst = colorLabelImg.ptr<uchar>(i);  
    200.         for (int j = 0; j < cols; j++)  
    201.         {  
    202.             int pixelValue = data_src[j];  
    203.             if (pixelValue > 1)  
    204.             {  
    205.                 if (colors.count(pixelValue) <= 0)  
    206.                 {  
    207.                     colors[pixelValue] = GetRandomColor();  
    208.                 }  
    209.   
    210.                 cv::Scalar color = colors[pixelValue];  
    211.                 *data_dst++   = color[0];  
    212.                 *data_dst++ = color[1];  
    213.                 *data_dst++ = color[2];  
    214.             }  
    215.             else  
    216.             {  
    217.                 data_dst++;  
    218.                 data_dst++;  
    219.                 data_dst++;  
    220.             }  
    221.         }  
    222.     }  
    223. }  
    224.   
    225.   
    226. int main()  
    227. {  
    228.   
    229.     cv::Mat binImage = cv::imread("test.jpg", 0);  
    230.     cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY_INV);  
    231.     cv::Mat labelImg;  
    232.     Two_Pass(binImage, labelImg, num);  
    233.     //Seed_Filling(binImage, labelImg);  
    234.     //彩色显示  
    235.     cv::Mat colorLabelImg;  
    236.     LabelColor(labelImg, colorLabelImg);  
    237.     cv::imshow("colorImg", colorLabelImg);  
    238. /*  //灰度显示 
    239.     cv::Mat grayImg; 
    240.     labelImg *= 10; 
    241.     labelImg.convertTo(grayImg, CV_8UC1); 
    242.     cv::imshow("labelImg", grayImg); 
    243. */  
    244.   
    245.     cv::waitKey(0);  
    246.     return 0;  
    247. }  
    四、演示结果

    原图:

    效果图:                                                                                         

    参考文章:

    http://www.cnblogs.com/ronny/p/img_aly_01.html

    http://blog.csdn.net/icvpr/article/details/10259577

  • 相关阅读:
    Sublime text 2/3 中 Package Control 的安装与使用方法
    http content-type accept的区别
    div布局
    [转]HDFS HA 部署安装
    Hive内置数据类型
    MyBatis注解select in参数
    HTTP协议状态码详解(HTTP Status Code)
    Hive基础(5)---内部表 外部表 临时表
    Hive基础(4)---Hive的内置服务
    MySQL数据备份之mysqldump使用(转)
  • 原文地址:https://www.cnblogs.com/donaldlee2008/p/5229975.html
Copyright © 2011-2022 走看看