精华内容
下载资源
问答
  • 一.图像二值化(Image Thresholding) 二.背景检测(Background Estimation/Detection) 三.连通区域分析(Connected-Component Labeling/Analysis)

    引子:如何计算下图中米粒的个数及各米粒的大小?
    在这里插入图片描述

    1.进行二值化(背景-->黑,前景(米粒)-->白)
    2.计算连通的白色区域数(米粒数)
    3.计算每个区域包含多少个像素(米粒大小)
    
    #二值化结果:见下图
    

    在这里插入图片描述

    问题:
    1.背景中存在噪点(没有米粒的地方出现了白色的像素)
    2.一些米粒消失了
    
    原因:
    打光不均匀,上半部分比下半部分亮
    导致上半部分的背景被误判为前景(问题1)
    下半部分的前景被误判为背景(问题2)
    

    在这里插入图片描述

    解决方法:进行背景估计,得到背景,然后从原图中剪掉背景
    
    >> I = imread('rice.png');
    >> BG = imopen(I,strel('disk',15));
    >> imshow(BG);%结果见下图1
    >> I2 = imsubtract(I,BG);
    >> imshow(I2)%结果见下图2
    >> bw2=im2bw(I2,graythresh(I2));
    >> imshow(bw2)%结果见下图3
    

    在这里插入图片描述
    一.图像二值化(Image Binarization)
    1.基本原理:

    1.找到灰度阈值(Gray Scale Thresholding),记为T
    2.将灰度大于T的像素变为白色,灰度小于T的像素变为黑色
    3.处理后即得到"二值图像"(Binary Image)
    

    2.方法:
    (1)计算灰度阈值:

    [<T>[,<EM>]] = graythresh(<I>):使用Otsu方法计算灰度图的全局灰度阈值
      #(???)Otsu方法(即最大类间方差法)选择1个阈值,使阈值化的黑白像素的类内方差最小化
      #全局阈值T可与imbinarize()结合使用以将灰度图转换为二值图像
      #参数说明:
        I:指定灰度图
        T:返回得到的全局灰度阈值
        EM:返回有效性度量
    
    #实例:
    >> I = imread('coins.png');
    >> level = graythresh(I)
    level = 0.4941
    >> BW = imbinarize(I,level);
    >> imshowpair(I,BW,'montage')%结果见下图
    

    在这里插入图片描述
    (2)进行二值化:

    <BW> = imbinarize(<I>[,<method>]):通过阈值化将2/3维灰度图像二值化
      #参数说明:
        I:指定2/3维灰度图像
        method:指定寻找阈值的ff;默认为Otsu方法
          #详情参见官方文档
        BW:返回二值图像
    <BW> = imbinarize(<I>,<T>):功能同上,不过是手动指定阈值
      #参数说明:BW,I同上
        T:指定阈值
    <BW> = imbinarize(<I>,"adaptive"[,"<Name>",<Value>]):使用键值对组控制阈值,创建二值图像
      #参数说明:BW,I同上
        Name,Value:指定键值对,用于控制自适应阈值的各个方面
    
    #实例:
    >> I = imread('coins.png');
    >> BW = imbinarize(I);
    >> figure
    >> imshowpair(I,BW,'montage')%结果见下图1
    
    >> I = imread('rice.png');
    >> BW = imbinarize(I, 'adaptive');
    >> figure
    >> imshowpair(I,BW,'montage')%结果见下图2
    
    %对前景比背景暗的图像进行二值化:
    >> I = imread('printedtext.png');
    >> figure
    >> imshow(I)%结果见下图3
    >> title('Original Image')
    >> BW = imbinarize(I,'adaptive','ForegroundPolarity','dark','Sensitivity',0.4);
    >> figure
    >> imshow(BW)%结果见下图4
    >> title('Binary Version of Image')
    
    %使用全局阈值对三维体进行二值化:
    >> load mristack;
    >> V = mristack;
    >> figure
    >> slice(double(V),size(V,2)/2,size(V,1)/2,size(V,3)/2)%查看3维图像的切片,结果见下图5
    >> colormap gray 
    >> shading interp
    >> J = imbinarize(V);
    >> figure
    >> slice(double(J),size(J,2)/2,size(J,1)/2,size(J,3)/2)%查看3维二值图像的切片,结果见下图6
    >> colormap gray 
    >> shading interp
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    <BW> = im2bw(<I>,<level>):基于阈值将灰度图转换为二值图像(不推荐)
      #参数说明:
        I:指定灰度图
        level:指定阈值;为[0,1]内的标量
          灰度>level的像素替换为白色,灰度≤level的像素替换为黑色
        BW:返回二值图像
    <BW> = im2bw(<X>,<map>,<level>):基于阈值将索引图像转换为二值图像(不推荐)
      #参数说明:BW,level同上
        X:指定索引图像
        map:指定颜色图
    <BW> = im2bw(<RGB>,<level>):将于阈值将真彩色图像转换为二值图像(不推荐)
      #参数说明:BW,level同上
        RGB:指定真彩色图像
    
    注意:如果输入不是灰度图像,则先将图像转换为灰度图像,再通过阈值将灰度图像转换为二值图像
    
    #实例:
    >> load trees
    >> BW = im2bw(X,map,0.4);
    >> imshow(X,map);%结果见下图1
    >> figure;
    >> imshow(BW);%结果见下图2
    

    在这里插入图片描述
    在这里插入图片描述
    3.不利用内置函数进行二值化:

    I = imread('rice.png');
    [h,w] = size(I);%获得图片的高度(height)/宽度(width)
    level = 100;%输入阈值
    for i = 1 : h
        for j = 1 : w
            if I(i,j) > level%若大于阈值,则转换为白色
                I(i,j) = 1;
            else%否则转换为黑色
                I(i,j) = 0;
            end
        end
    end
    I = uint8(I * 255);%转为位图
    imshow(I)%结果见下图
    

    在这里插入图片描述
    二.背景估计(Background Estimation)
    1.对图像进行开运算:

    对灰度图像进行形态学开运算:<J> = imopen(<I>,<SE>)
                           <J> = imopen(<I>,<nhood>)
      #后者等效于imopen(I,strel(nhood))
      #即使用同样的结构元素先对图像进行腐蚀操作后进行膨胀操作;常用于获得背景
      #参数说明:
        I:指定灰度图像
        SE:由strel()/offsetstrel()返回的单个构造元素对象
        nhood:用于指定结构元素邻域的矩阵,由0/1构成
          #imopen()通过floor((size(nhood)+1)/2)确定邻域的中心元素
        J:返回处理后的图像
    

    2.对图像进行减运算:

    从图像的每个像素中减去指定量:Z = imsubtract(X,Y)
      #常用于从图像中去除背景
      #参数说明:
        X:指定原图像
        Y:指定要减去的量;为scalar/matrix
        Z:返回处理后的图像
    

    三.连通区域分析(Connected-Component Labeling/Analysis)
    1.概述
    (1)连通区域:

    在图像中,每个像素周围有8个相邻像素.常见的邻接关系有2种:4邻接与8邻接.对4邻接来说,某个像素有4个邻接像素(上下左右);对8邻接来说,每个像素则有8个邻接
    像素(多了通过对角相邻的像素)
    

    在这里插入图片描述

    如果像素点A与B邻接且二者的值相同,则称A与B"连通";并且若A与B连通,B与C连通,则A与C连通.从视觉上看来,彼此连通的点形成了1个区域.这样1个由所有彼此连
    通的前景像素点构成的图像区域,称为1个"连通区域"(Connected Component).如下图中包含3个4连通区域或2个8连通区域
    

    在这里插入图片描述
    (2)连通区域分析:

    背景像素仍标记为原来的值(如0);而将前景像素分为不同的连通区域,相同连通区域中的点标记为相同的值,不同连通区域中的点则标记为不同的值(如1,2...).连通区域
    标记算法有很多种,有的可以1次遍历图像完成标记,有的则需要2次或更多次遍历,这就造成了不同的算法时间效率的差别
    

    2.连通区域标记
    (1)进行连通区域标记:

    对二值图像进行连通区域标记:[L,n] = bwlabel(BW[,conn])
      #参数说明:
      	BW:指定二值图像
      	conn:指定连通性;为4(4联通)/8(默认值;8联通)
      	L:返回标记矩阵
      	  #背景像素会被标记为0,前景像素依据所在连通区域会被标记为1,2...
      	n:返回联通区域的数量
    
    #实例:
    >> [L,n] = bwlabel(bw2);
    >> L
    
    L =
    
      1 至 25 列
    
         0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0    15    15    15    15
         0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0    15    15    15    15    15
         0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0    15    15    15    15    15
    ...%此处省略
         0     0     0     0     0     0
         0     0     0     0     0     0
    
    >> n
    
    n =
    
        99
    
    

    (2)将标记矩阵转换为RGB图像:

    将标记矩阵转换为RGB图像:RGB = label2rgb(L[,cmap,zerocolor,order,'OutputFormat',outputFormat])
      #参数说明:
      	L:指定标记矩阵
      	cmap:指定标记与RGB颜色间的映射关系;为'jet' (默认值)/matrix(包含3列)/function
      	zerocolor:指定背景像素(被标记为0)的RGB颜色;为str/vector(长度为3;默认为[1,1,1])
      	order:指定RGB颜色与标记对应的顺序;为'noshuffle'(默认值)/'shuffle'
      	outputFormat:指定返回的RGB数据的格式;为'image'(默认值)/'triplets'
      	RGB:返回RGB图像
    
    #实例:
    >> RGB=label2rgb(L);
    >> imshow(RGB)%结果见下图
    

    在这里插入图片描述
    3.连通区域操作
    (1)查看连通区域属性:

    查看各个连通区域的属性:stats = regionprops([output,]BW[,I,properties])
    					stats = regionprops([output,]CC[,I,properties])
    					stats = regionprops([output,]L[I,properties])
      #虽然regionprops()也接受3维图像,但对3维图像来说,跟推荐regionprops3(),因为可获得更多信息
      #参数说明:
        output:指定返回值的类型
      	BW:指定二值图像
      	  #返回其中每个8连通区域的指定属性
      	CC:指定由bwconncomp()返回的结构体
      	  #返回其中每个连通区域的指定属性
      	L:指定测量标注图像
      	  #返回其中每个标注区域的指定属性
      	I:指定灰度图像
      	  #返回其中每个标注区域的指定属性,由BW/CC/L标识I中的区域
      	  #可不指定I而指定properties
      	properties:指定要查看的属性;为str,默认为'Area'/'Centroid'/'BoundingBox'
      	stats:返回指定属性的测量值
    

    (2)选择物体:

    选择二值图像中的指定物体:[x,y,BW2,idx,xi,yi] = bwselect(BW,c,r[,n])
      #参数说明:
      	BW:指定二值图像
      	c,r:选择BW(r,c)所在的物体;均为num/array
      	n:指定连通性;为4/8
      	BW2:返回指定物体构成的二值图像
    
    展开全文
  • OpenCV_连通区域分析

    2021-02-09 13:54:13
    连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。连通区域分析(Connected Component Analysis,Connected Component Labeling)是指将图像中的...

    0. 写在前面
    连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。连通区域分析(Connected Component Analysis,Connected Component Labeling)是指将图像中的各个连通区域找出并标记。

    连通区域分析是一种在图像分析处理的众多应用领域中较为基本的方法。例如:OCR识别中字符分割提取(车牌识别、文本识别、字幕识别等)、视觉跟踪中的运动前景目标分割与提取(行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等)、医学图像处理(感兴趣目标区域提取)、等等。也就是说,在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析方法,通常连通区域分析处理的对象是一张二值化后的图像。

    1. OPENCV算子
    函数原型为:

    /** @overload
    @param image the 8-bit single-channel image to be labeled
    @param labels destination labeled image
    @param stats statistics output for each label, including the background label, see below for
    available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of
    #ConnectedComponentsTypes. The data type is CV_32S.
    @param centroids centroid output for each label, including the background label. Centroids are
    accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F.
    @param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
    @param ltype output image label type. Currently CV_32S and CV_16U are supported.
    */
    CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels,
                                                  OutputArray stats, OutputArray centroids,
                                                  int connectivity = 8, int ltype = CV_32S);
    

    参数:
    stats:各个连通区域的基本统计信息。包含背景。
    centroids:各个连通区域的质心。包含背景。
    connectivity:选择8邻还是4邻域进行连通区域的计算。

    与此同时OPENCV还提供不计算统计信息的基本版本算子:

    /** @overload
    
    @param image the 8-bit single-channel image to be labeled
    @param labels destination labeled image
    @param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
    @param ltype output image label type. Currently CV_32S and CV_16U are supported.
    */
    CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels,
                                         int connectivity = 8, int ltype = CV_32S);
    

    2. 连通区域分析基本算法

    本文主要介绍图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通性分析法(连通区域标记法)。文中介绍了两种常见的连通性分析的算法:1)Two-pass;2)Seed-Filling种子填充,并给出了两个算法的基于OpenCV的C++实现代码。

    从连通区域的定义可以知道,一个连通区域是由具有相同像素值的相邻像素组成像素集合,因此,我们就可以通过这两个条件在图像中寻找连通区域,对于找到的每个连通区域,我们赋予其一个唯一的标识(Label),以区别其他连通区域。

    2.1 Two-Pass法
    Note:

    a、这里的扫描指的是按行或按列访问以便图像的所有像素,本文算法采用的是按行扫描方式;

    b、图像记为B,为二值图像:前景像素(pixel value = 1),背景像素(pixel value = 0)

    c、label从2开始计数;

    d、像素相邻关系:4-领域、8-领域,本文算法采用4-邻域;
    在这里插入图片描述
    下面给出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):

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

    B(x,y) = min{Neighbors} (假设B(x,y)的邻域内有三个像素大于1,其值分别为3、4、6,那么令B(x,y)=3 )

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

    labelSet[i] = { label_m, …, label_n },labelSet[i]中的所有label都属于同一个连通区域(注:这里可以有多种实现方式,只要能够记录这些具有相等关系的label之间的关系即可)
    (这段话比较难以理解。继续假设B(x,y)的邻域内有三个像素大于1,其值分别为3、4、6,那么令B(x,y)为其中最小的3。 将标签3、标签4和标签6记录为相等关系,比如labelSet[1] = { 3, 4,6} )

    (2)第二次扫描:

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

    a、找到与label = B(x,y)同属相等关系的一个最小label值,赋予给B(x,y);
    完成扫描后,图像中具有相同label值的像素就组成了同一个连通区域。
    (假设当前B(x,y)所属标签为4,根据刚才之前得到的labelSet[1] = { 3, 4,6}关系,取集合最小值3,让当前B(x,y)的最终标签为3 )

    下面这张图动态地演示了Two-pass算法:

    Two-pass算法演示图

    2.2 Seed Filling(种子填充法)

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

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

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

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

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

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

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

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

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

    下面这张图动态地演示了种子填充算法演示图:
    种子填充算法演示图

    3 代码

    3.1 Two-Pass的一种实现

    //  Connected Component Analysis/Labeling By Two-Pass Algorithm 
    //  Author:  www.icvpr.com  
    //  Blog  :  http://blog.csdn.net/icvpr 
    #include <iostream>
    #include <string>
    #include <list>
    #include <vector>
    #include <map>
     
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
     
     
    void icvprCcaByTwoPass(const cv::Mat& _binImg, cv::Mat& _lableImg)
    {
    	// connected component analysis (4-component)
    	// use two-pass algorithm
    	// 1. first pass: label each foreground pixel with a label
    	// 2. second pass: visit each labeled pixel and merge neighbor labels
    	// 
    	// foreground pixel: _binImg(x,y) = 1
    	// background pixel: _binImg(x,y) = 0
     
     
    	if (_binImg.empty() ||
    		_binImg.type() != CV_8UC1)
    	{
    		return ;
    	}
     
    	// 1. first pass
     
    	_lableImg.release() ;
    	_binImg.convertTo(_lableImg, CV_32SC1) ;
     
    	int label = 1 ;  // start by 2
    	std::vector<int> labelSet ;
    	labelSet.push_back(0) ;   // background: 0
    	labelSet.push_back(1) ;   // foreground: 1
     
    	int rows = _binImg.rows - 1 ;
    	int cols = _binImg.cols - 1 ;
    	for (int i = 1; i < rows; i++)
    	{
    		int* data_preRow = _lableImg.ptr<int>(i-1) ;
    		int* data_curRow = _lableImg.ptr<int>(i) ;
    		for (int j = 1; j < cols; j++)
    		{
    			if (data_curRow[j] == 1)
    			{
    				std::vector<int> neighborLabels ;
    				neighborLabels.reserve(2) ;
    				int leftPixel = data_curRow[j-1] ;
    				int upPixel = data_preRow[j] ;
    				if ( leftPixel > 1)
    				{
    					neighborLabels.push_back(leftPixel) ;
    				}
    				if (upPixel > 1)
    				{
    					neighborLabels.push_back(upPixel) ;
    				}
     
    				if (neighborLabels.empty())
    				{
    					labelSet.push_back(++label) ;  // assign to a new label
    					data_curRow[j] = label ;
    					labelSet[label] = label ;
    				}
    				else
    				{
    					std::sort(neighborLabels.begin(), neighborLabels.end()) ;
    					int smallestLabel = neighborLabels[0] ;  
    					data_curRow[j] = smallestLabel ;
     
    					// save equivalence
    					for (size_t k = 1; k < neighborLabels.size(); k++)
    					{
    						int tempLabel = neighborLabels[k] ;
    						int& oldSmallestLabel = labelSet[tempLabel] ;
    						if (oldSmallestLabel > smallestLabel)
    						{							
    							labelSet[oldSmallestLabel] = smallestLabel ;
    							oldSmallestLabel = smallestLabel ;
    						}						
    						else if (oldSmallestLabel < smallestLabel)
    						{
    							labelSet[smallestLabel] = oldSmallestLabel ;
    						}
    					}
    				}				
    			}
    		}
    	}
     
    	// update equivalent labels
    	// assigned with the smallest label in each equivalent label set
    	for (size_t i = 2; i < labelSet.size(); i++)
    	{
    		int curLabel = labelSet[i] ;
    		int preLabel = labelSet[curLabel] ;
    		while (preLabel != curLabel)
    		{
    			curLabel = preLabel ;
    			preLabel = labelSet[preLabel] ;
    		}
    		labelSet[i] = curLabel ;
    	}
     
     
    	// 2. second pass
    	for (int i = 0; i < rows; i++)
    	{
    		int* data = _lableImg.ptr<int>(i) ;
    		for (int j = 0; j < cols; j++)
    		{
    			int& pixelLabel = data[j] ;
    			pixelLabel = labelSet[pixelLabel] ;	
    		}
    	}
    }
    
    

    3.2 Seed-Filling种子填充方法的一种实现

    //  Connected Component Analysis/Labeling By Seed-Filling Algorithm 
    //  Author:  www.icvpr.com  
    //  Blog  :  http://blog.csdn.net/icvpr 
    #include <iostream>
    #include <string>
    #include <list>
    #include <vector>
    #include <map>
    #include <stack>
     
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
     
     
    void icvprCcaBySeedFill(const cv::Mat& _binImg, cv::Mat& _lableImg)
    {
    	// connected component analysis (4-component)
    	// use seed filling algorithm
    	// 1. begin with a foreground pixel and push its foreground neighbors into a stack;
    	// 2. pop the top pixel on the stack and label it with the same label until the stack is empty
    	// 
    	// foreground pixel: _binImg(x,y) = 1
    	// background pixel: _binImg(x,y) = 0
     
     
    	if (_binImg.empty() ||
    		_binImg.type() != CV_8UC1)
    	{
    		return ;
    	}
     
    	_lableImg.release() ;
    	_binImg.convertTo(_lableImg, CV_32SC1) ;
     
    	int label = 1 ;  // start by 2
     
    	int rows = _binImg.rows - 1 ;
    	int cols = _binImg.cols - 1 ;
    	for (int i = 1; i < rows-1; i++)
    	{
    		int* data= _lableImg.ptr<int>(i) ;
    		for (int j = 1; j < cols-1; j++)
    		{
    			if (data[j] == 1)
    			{
    				std::stack<std::pair<int,int>> neighborPixels ;   
    				neighborPixels.push(std::pair<int,int>(i,j)) ;     // pixel position: <i,j>
    				++label ;  // begin with a new label
    				while (!neighborPixels.empty())
    				{
    					// get the top pixel on the stack and label it with the same label
    					std::pair<int,int> curPixel = neighborPixels.top() ;
    					int curX = curPixel.first ;
    					int curY = curPixel.second ;
    					_lableImg.at<int>(curX, curY) = label ;
     
    					// pop the top pixel
    					neighborPixels.pop() ;
     
    					// push the 4-neighbors (foreground pixels)
    					if (_lableImg.at<int>(curX, curY-1) == 1)
    					{// left pixel
    						neighborPixels.push(std::pair<int,int>(curX, curY-1)) ;
    					}
    					if (_lableImg.at<int>(curX, curY+1) == 1)
    					{// right pixel
    						neighborPixels.push(std::pair<int,int>(curX, curY+1)) ;
    					}
    					if (_lableImg.at<int>(curX-1, curY) == 1)
    					{// up pixel
    						neighborPixels.push(std::pair<int,int>(curX-1, curY)) ;
    					}
    					if (_lableImg.at<int>(curX+1, curY) == 1)
    					{// down pixel
    						neighborPixels.push(std::pair<int,int>(curX+1, curY)) ;
    					}
    				}		
    			}
    		}
    	}
    }
    
    

    3.3 颜色标记(用于显示)

    //  Connected Component Analysis/Labeling -- Color Labeling 
    //  Author:  www.icvpr.com  
    //  Blog  :  http://blog.csdn.net/icvpr 
    #include <iostream>
    #include <string>
    #include <list>
    #include <vector>
    #include <map>
    #include <stack>
     
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
     
    cv::Scalar icvprGetRandomColor()
    {
    	uchar r = 255 * (rand()/(1.0 + RAND_MAX));
    	uchar g = 255 * (rand()/(1.0 + RAND_MAX));
    	uchar b = 255 * (rand()/(1.0 + RAND_MAX));
    	return cv::Scalar(b,g,r) ;
    }
     
     
    void icvprLabelColor(const cv::Mat& _labelImg, cv::Mat& _colorLabelImg) 
    {
    	if (_labelImg.empty() ||
    		_labelImg.type() != CV_32SC1)
    	{
    		return ;
    	}
     
    	std::map<int, cv::Scalar> colors ;
     
    	int rows = _labelImg.rows ;
    	int cols = _labelImg.cols ;
     
    	_colorLabelImg.release() ;
    	_colorLabelImg.create(rows, cols, CV_8UC3) ;
    	_colorLabelImg = cv::Scalar::all(0) ;
     
    	for (int i = 0; i < rows; i++)
    	{
    		const int* data_src = (int*)_labelImg.ptr<int>(i) ;
    		uchar* data_dst = _colorLabelImg.ptr<uchar>(i) ;
    		for (int j = 0; j < cols; j++)
    		{
    			int pixelValue = data_src[j] ;
    			if (pixelValue > 1)
    			{
    				if (colors.count(pixelValue) <= 0)
    				{
    					colors[pixelValue] = icvprGetRandomColor() ;
    				}
    				cv::Scalar color = colors[pixelValue] ;
    				*data_dst++   = color[0] ;
    				*data_dst++ = color[1] ;
    				*data_dst++ = color[2] ;
    			}
    			else
    			{
    				data_dst++ ;
    				data_dst++ ;
    				data_dst++ ;
    			}
    		}
    	}
    }
    
    

    3.4 测试程序

    //  Connected Component Analysis/Labeling -- Test code
    //  Author:  www.icvpr.com  
    //  Blog  :  http://blog.csdn.net/icvpr 
    #include <iostream>
    #include <string>
    #include <list>
    #include <vector>
    #include <map>
    #include <stack>
     
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
     
    int main(int argc, char** argv)
    {
    	cv::Mat binImage = cv::imread("../icvpr.com.jpg", 0) ;
    	cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY_INV) ;
     
    	// connected component labeling
    	cv::Mat labelImg ;
    	icvprCcaByTwoPass(binImage, labelImg) ;
    	//icvprCcaBySeedFill(binImage, labelImg) ;
     
    	// show result
    	cv::Mat grayImg ;
    	labelImg *= 10 ;
    	labelImg.convertTo(grayImg, CV_8UC1) ;
    	cv::imshow("labelImg", grayImg) ;
     
    	cv::Mat colorLabelImg ;
    	icvprLabelColor(labelImg, colorLabelImg) ;
    	cv::imshow("colorImg", colorLabelImg) ;
    	cv::waitKey(0) ;
     
    	return 0 ;
    }
    
    
    展开全文
  • opencv图像连通区域分析

    千次阅读 2018-10-20 14:33:15
    本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通性分析法(连通区域标记法)。文中介绍了两种常见的连通性分析的算法:1)Two-pass;2)Seed-Filling种子填充,并给出了两个...

    一、连通区域分析

    连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。连通区域分析(Connected Component Analysis,Connected Component Labeling)是指将图像中的各个连通区域找出并标记。

    连通区域分析是一种在CVPR和图像分析处理的众多应用领域中较为常用和基本的方法。例如:OCR识别中字符分割提取(车牌识别、文本识别、字幕识别等)、视觉跟踪中的运动前景目标分割与提取(行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等)、医学图像处理(感兴趣目标区域提取)、等等。也就是说,在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析方法,通常连通区域分析处理的对象是一张二值化后的图像。

    二、连通区域分析的算法
    从连通区域的定义可以知道,一个连通区域是由具有相同像素值的相邻像素组成像素集合,因此,我们就可以通过这两个条件在图像中寻找连通区域,对于找到的每个连通区域,我们赋予其一个唯一的标识(Label),以区别其他连通区域。
    连通区域分析有基本的算法,也有其改进算法,本文介绍其中的两种常见算法:

    1. Two-Pass法;
    2. Seed-Filling种子填充法;

    注意:
    a、这里的扫描指的是按行或按列访问以便图像的所有像素,本文算法采用的是按行扫描方式;
    b、图像记为B,为二值图像:前景像素(pixel value = 1),背景像素(pixel value = 0)
    c、label从2开始计数;
    d、像素相邻关系:4-领域、8-领域,本文算法采用4-邻域;

    在这里插入图片描述

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

    两遍扫描法,正如其名,指的就是通过扫描两遍图像,就可以将图像中存在的所有连通区域找出并标记。思路:第一遍扫描时赋予每个像素位置一个label,扫描过程中同一个连通区域内的像素集合中可能会被赋予一个或多个不同label,因此需要将这些属于同一个连通区域但具有不同值的label合并,也就是记录它们之间的相等关系;第二遍扫描就是将具有相等关系的equal_labels所标记的像素归为一个连通区域并赋予一个相同的label(通常这个label是equal_labels中的最小值)。

    下面给出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);

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

    下面这张图动态地演示了Two-pass算法:
    在这里插入图片描述
    2)Seed Filling(种子填充法)

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

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

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

    a、将B(x,y)作为种子(像素位置),并赋予其一个label,然后将该种子相邻的所有前景像素都压入栈中;
    b、弹出栈顶像素,赋予其相同的label,然后再将与该栈顶像素相邻的所有前景像素都压入栈中;
    c、重复b步骤,直到栈为空;此时,便找到了图像B中的一个连通区域,该区域内的像素值被标记为label;

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

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

    下面这张图动态地演示了Seed-Filling算法:
    在这里插入图片描述

    三、实验演示

    1)前景二值图像
    在这里插入图片描述
    2)连通区域分析方法标记后得到的label图像
    在这里插入图片描述
    3)color后的label图像
    在这里插入图片描述

    四、代码

    1)Two-pass算法的一种实现

    说明:基于OpenCV和C++实现,领域:4-领域。实现与算法描述稍有差别(具体为记录具有相等关系的label方法实现上)。

    代码请参考:https://blog.csdn.net/icvpr/article/details/10259577

    2)Seed-Filling种子填充方法

    说明:基于OpenCV和C++实现;领域:4-领域。

    代码请参考:https://blog.csdn.net/icvpr/article/details/10259577

    3)颜色标记(用于显示)

    代码请参考:https://blog.csdn.net/icvpr/article/details/10259577

    4)测试程序

    代码请参考:https://blog.csdn.net/icvpr/article/details/10259577

    展开全文
  • 图像分割-连通区域分析

    千次阅读 2018-06-21 11:33:58
    转自:https://blog.csdn.net/icvpr/article/details/10259577OpenCV_连通区域分析(Connected Component Analysis/Labeling)【摘要】本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法...

    OpenCV提供了专门的函数标记连通区域connectedComponents,它有四种形式:

    1. int connectedComponents(InputArray image, OutputArray labels, int connectivity, int ltype, int ccltype);

    参数说明:

    image : 输入原始图像,8bit单通道,二值图像

    labels : 输出标记图像

    connectivity : 邻域类型,4连通或8连通,所以这个值通常为两个值(4或8)

    ltype : 输出标记的类型,只支持 CV_32S和CV_16U

    ccltype : 连通标记的算法,这个值有三个选择CCL_WU(8连通和4连通均用SAUF算法),CCL_DEFAULT(8连通用BBDT算法,4连通用SAUF算法)和CCL_GRANA(8连通用BBDT算法,4连通用SAUF算法)。(后两种方法是一样的,这里有疑问)


    2. int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype = CV_32S);

    这个函数实际上就是第一个的简化版

    这两个函数的返回值是标记的个数[0,N-1],0表示背景区域


    3. int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids,  int connectivity, int ltype, int ccltype);

    参数说明:

    image : 输入原始图像,8bit单通道,二值图像

    labels : 输出标记图像

    stats : 每个标记的统计输出,类型为CV_32S,使用格式为stats(label, COLUMN),其中COLUMN为连通类型,包括CC_STAT_LEFT、CC_STAT_TOP、CC_STAT_WIDTH、CC_STAT_HEIGHT、CC_STAT_AREA

    centroids : 每个标记区域的质心,类型为CV_64F,使用方法为x = centroids(label,0), y = centroids(label, 1)

    connectivity : 邻域类型,4连通或8连通,所以这个值通常为两个值(4或8)

    ltype : 输出标记的类型,只支持 CV_32S和CV_16U


    4. int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids,  int connectivity = 8, int ltype = CV_32S);

    第三个函数的简化版


    ----------------------华丽的分界线------------------------


    以下转自:https://blog.csdn.net/icvpr/article/details/10259577

    OpenCV_连通区域分析(Connected Component Analysis/Labeling)


    【摘要】

    本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通性分析法(连通区域标记法)。文中介绍了两种常见的连通性分析的算法:1)Two-pass;2)Seed-Filling种子填充,并给出了两个算法的基于OpenCV的C++实现代码。


    一、连通区域分析

    连通区域(Connected Component)一般是指图像中具有相同像素值且位置相邻的前景像素点组成的图像区域(Region,Blob)。连通区域分析(Connected Component Analysis,Connected Component Labeling)是指将图像中的各个连通区域找出并标记。

    连通区域分析是一种在CVPR和图像分析处理的众多应用领域中较为常用和基本的方法。例如:OCR识别中字符分割提取(车牌识别、文本识别、字幕识别等)、视觉跟踪中的运动前景目标分割与提取(行人入侵检测、遗留物体检测、基于视觉的车辆检测与跟踪等)、医学图像处理(感兴趣目标区域提取)、等等。也就是说,在需要将前景目标提取出来以便后续进行处理的应用场景中都能够用到连通区域分析方法,通常连通区域分析处理的对象是一张二值化后的图像。

    二、连通区域分析的算法

    从连通区域的定义可以知道,一个连通区域是由具有相同像素值的相邻像素组成像素集合,因此,我们就可以通过这两个条件在图像中寻找连通区域,对于找到的每个连通区域,我们赋予其一个唯一的标识(Label),以区别其他连通区域。

    连通区域分析有基本的算法,也有其改进算法,本文介绍其中的两种常见算法:

    1)Two-Pass法;2)Seed-Filling种子填充法;

    Note:

    a、这里的扫描指的是按行或按列访问以便图像的所有像素,本文算法采用的是按行扫描方式;

    b、图像记为B,为二值图像:前景像素(pixel value = 1),背景像素(pixel value = 0)

    c、label从2开始计数;

    d、像素相邻关系:4-领域、8-领域,本文算法采用4-邻域;


                                         

    4—领域图例                                                     8—领域图例



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

    两遍扫描法,正如其名,指的就是通过扫描两遍图像,就可以将图像中存在的所有连通区域找出并标记。思路:第一遍扫描时赋予每个像素位置一个label,扫描过程中同一个连通区域内的像素集合中可能会被赋予一个或多个不同label,因此需要将这些属于同一个连通区域但具有不同值的label合并,也就是记录它们之间的相等关系;第二遍扫描就是将具有相等关系的equal_labels所标记的像素归为一个连通区域并赋予一个相同的label(通常这个label是equal_labels中的最小值)。


    下面给出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)

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


    下面这张图动态地演示了Two-pass算法:





    2)Seed Filling(种子填充法)

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

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

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

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

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

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

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

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

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

    下面这张图动态地演示了Seed-Filling算法:



    三、实验演示


    1)前景二值图像


    2)连通区域分析方法标记后得到的label图像


    Two-pass算法:



    Seed-filling算法:


    注:为了显示方便,将像素值乘以了一个整数进行放大。


    3)color后的label图像

    Two-pass算法:


    Seed-filling算法:



    注:颜色是随机生成的


    四、代码

    1)Two-pass算法的一种实现

    说明:

    基于OpenCV和C++实现,领域:4-领域。实现与算法描述稍有差别(具体为记录具有相等关系的label方法实现上)。

    1. //  Connected Component Analysis/Labeling By Two-Pass Algorithm   
    2. //  Author:  www.icvpr.com    
    3. //  Blog  :  http://blog.csdn.net/icvpr   
    4. #include <iostream>  
    5. #include <string>  
    6. #include <list>  
    7. #include <vector>  
    8. #include <map>  
    9.   
    10. #include <opencv2/imgproc/imgproc.hpp>  
    11. #include <opencv2/highgui/highgui.hpp>  
    12.   
    13.   
    14. void icvprCcaByTwoPass(const cv::Mat& _binImg, cv::Mat& _lableImg)  
    15. {  
    16.     // connected component analysis (4-component)  
    17.     // use two-pass algorithm  
    18.     // 1. first pass: label each foreground pixel with a label  
    19.     // 2. second pass: visit each labeled pixel and merge neighbor labels  
    20.     //   
    21.     // foreground pixel: _binImg(x,y) = 1  
    22.     // background pixel: _binImg(x,y) = 0  
    23.   
    24.   
    25.     if (_binImg.empty() ||  
    26.         _binImg.type() != CV_8UC1)  
    27.     {  
    28.         return ;  
    29.     }  
    30.   
    31.     // 1. first pass  
    32.   
    33.     _lableImg.release() ;  
    34.     _binImg.convertTo(_lableImg, CV_32SC1) ;  
    35.   
    36.     int label = 1 ;  // start by 2  
    37.     std::vector<int> labelSet ;  
    38.     labelSet.push_back(0) ;   // background: 0  
    39.     labelSet.push_back(1) ;   // foreground: 1  
    40.   
    41.     int rows = _binImg.rows - 1 ;  
    42.     int cols = _binImg.cols - 1 ;  
    43.     for (int i = 1; i < rows; i++)  
    44.     {  
    45.         int* data_preRow = _lableImg.ptr<int>(i-1) ;  
    46.         int* data_curRow = _lableImg.ptr<int>(i) ;  
    47.         for (int j = 1; j < cols; j++)  
    48.         {  
    49.             if (data_curRow[j] == 1)  
    50.             {  
    51.                 std::vector<int> neighborLabels ;  
    52.                 neighborLabels.reserve(2) ;  
    53.                 int leftPixel = data_curRow[j-1] ;  
    54.                 int upPixel = data_preRow[j] ;  
    55.                 if ( leftPixel > 1)  
    56.                 {  
    57.                     neighborLabels.push_back(leftPixel) ;  
    58.                 }  
    59.                 if (upPixel > 1)  
    60.                 {  
    61.                     neighborLabels.push_back(upPixel) ;  
    62.                 }  
    63.   
    64.                 if (neighborLabels.empty())  
    65.                 {  
    66.                     labelSet.push_back(++label) ;  // assign to a new label  
    67.                     data_curRow[j] = label ;  
    68.                     labelSet[label] = label ;  
    69.                 }  
    70.                 else  
    71.                 {  
    72.                     std::sort(neighborLabels.begin(), neighborLabels.end()) ;  
    73.                     int smallestLabel = neighborLabels[0] ;    
    74.                     data_curRow[j] = smallestLabel ;  
    75.   
    76.                     // save equivalence  
    77.                     for (size_t k = 1; k < neighborLabels.size(); k++)  
    78.                     {  
    79.                         int tempLabel = neighborLabels[k] ;  
    80.                         int& oldSmallestLabel = labelSet[tempLabel] ;  
    81.                         if (oldSmallestLabel > smallestLabel)  
    82.                         {                             
    83.                             labelSet[oldSmallestLabel] = smallestLabel ;  
    84.                             oldSmallestLabel = smallestLabel ;  
    85.                         }                         
    86.                         else if (oldSmallestLabel < smallestLabel)  
    87.                         {  
    88.                             labelSet[smallestLabel] = oldSmallestLabel ;  
    89.                         }  
    90.                     }  
    91.                 }                 
    92.             }  
    93.         }  
    94.     }  
    95.   
    96.     // update equivalent labels  
    97.     // assigned with the smallest label in each equivalent label set  
    98.     for (size_t i = 2; i < labelSet.size(); i++)  
    99.     {  
    100.         int curLabel = labelSet[i] ;  
    101.         int preLabel = labelSet[curLabel] ;  
    102.         while (preLabel != curLabel)  
    103.         {  
    104.             curLabel = preLabel ;  
    105.             preLabel = labelSet[preLabel] ;  
    106.         }  
    107.         labelSet[i] = curLabel ;  
    108.     }  
    109.   
    110.   
    111.     // 2. second pass  
    112.     for (int i = 0; i < rows; i++)  
    113.     {  
    114.         int* data = _lableImg.ptr<int>(i) ;  
    115.         for (int j = 0; j < cols; j++)  
    116.         {  
    117.             int& pixelLabel = data[j] ;  
    118.             pixelLabel = labelSet[pixelLabel] ;   
    119.         }  
    120.     }  
    121. }


    2)Seed-Filling种子填充方法

    说明:

    基于OpenCV和C++实现;领域:4-领域。

    1. //  Connected Component Analysis/Labeling By Seed-Filling Algorithm   
    2. //  Author:  www.icvpr.com    
    3. //  Blog  :  http://blog.csdn.net/icvpr   
    4. #include <iostream>  
    5. #include <string>  
    6. #include <list>  
    7. #include <vector>  
    8. #include <map>  
    9. #include <stack>  
    10.   
    11. #include <opencv2/imgproc/imgproc.hpp>  
    12. #include <opencv2/highgui/highgui.hpp>  
    13.   
    14.   
    15. void icvprCcaBySeedFill(const cv::Mat& _binImg, cv::Mat& _lableImg)  
    16. {  
    17.     // connected component analysis (4-component)  
    18.     // use seed filling algorithm  
    19.     // 1. begin with a foreground pixel and push its foreground neighbors into a stack;  
    20.     // 2. pop the top pixel on the stack and label it with the same label until the stack is empty  
    21.     //   
    22.     // foreground pixel: _binImg(x,y) = 1  
    23.     // background pixel: _binImg(x,y) = 0  
    24.   
    25.   
    26.     if (_binImg.empty() ||  
    27.         _binImg.type() != CV_8UC1)  
    28.     {  
    29.         return ;  
    30.     }  
    31.   
    32.     _lableImg.release() ;  
    33.     _binImg.convertTo(_lableImg, CV_32SC1) ;  
    34.   
    35.     int label = 1 ;  // start by 2  
    36.   
    37.     int rows = _binImg.rows - 1 ;  
    38.     int cols = _binImg.cols - 1 ;  
    39.     for (int i = 1; i < rows-1; i++)  
    40.     {  
    41.         int* data= _lableImg.ptr<int>(i) ;  
    42.         for (int j = 1; j < cols-1; j++)  
    43.         {  
    44.             if (data[j] == 1)  
    45.             {  
    46.                 std::stack<std::pair<int,int>> neighborPixels ;     
    47.                 neighborPixels.push(std::pair<int,int>(i,j)) ;     // pixel position: <i,j>  
    48.                 ++label ;  // begin with a new label  
    49.                 while (!neighborPixels.empty())  
    50.                 {  
    51.                     // get the top pixel on the stack and label it with the same label  
    52.                     std::pair<int,int> curPixel = neighborPixels.top() ;  
    53.                     int curX = curPixel.first ;  
    54.                     int curY = curPixel.second ;  
    55.                     _lableImg.at<int>(curX, curY) = label ;  
    56.   
    57.                     // pop the top pixel  
    58.                     neighborPixels.pop() ;  
    59.   
    60.                     // push the 4-neighbors (foreground pixels)  
    61.                     if (_lableImg.at<int>(curX, curY-1) == 1)  
    62.                     {// left pixel  
    63.                         neighborPixels.push(std::pair<int,int>(curX, curY-1)) ;  
    64.                     }  
    65.                     if (_lableImg.at<int>(curX, curY+1) == 1)  
    66.                     {// right pixel  
    67.                         neighborPixels.push(std::pair<int,int>(curX, curY+1)) ;  
    68.                     }  
    69.                     if (_lableImg.at<int>(curX-1, curY) == 1)  
    70.                     {// up pixel  
    71.                         neighborPixels.push(std::pair<int,int>(curX-1, curY)) ;  
    72.                     }  
    73.                     if (_lableImg.at<int>(curX+1, curY) == 1)  
    74.                     {// down pixel  
    75.                         neighborPixels.push(std::pair<int,int>(curX+1, curY)) ;  
    76.                     }  
    77.                 }         
    78.             }  
    79.         }  
    80.     }  
    81. }


    3)颜色标记(用于显示)

    1. //  Connected Component Analysis/Labeling -- Color Labeling   
    2. //  Author:  www.icvpr.com    
    3. //  Blog  :  http://blog.csdn.net/icvpr   
    4. #include <iostream>  
    5. #include <string>  
    6. #include <list>  
    7. #include <vector>  
    8. #include <map>  
    9. #include <stack>  
    10.   
    11. #include <opencv2/imgproc/imgproc.hpp>  
    12. #include <opencv2/highgui/highgui.hpp>  
    13.   
    14. cv::Scalar icvprGetRandomColor()  
    15. {  
    16.     uchar r = 255 * (rand()/(1.0 + RAND_MAX));  
    17.     uchar g = 255 * (rand()/(1.0 + RAND_MAX));  
    18.     uchar b = 255 * (rand()/(1.0 + RAND_MAX));  
    19.     return cv::Scalar(b,g,r) ;  
    20. }  
    21.   
    22.   
    23. void icvprLabelColor(const cv::Mat& _labelImg, cv::Mat& _colorLabelImg)   
    24. {  
    25.     if (_labelImg.empty() ||  
    26.         _labelImg.type() != CV_32SC1)  
    27.     {  
    28.         return ;  
    29.     }  
    30.   
    31.     std::map<int, cv::Scalar> colors ;  
    32.   
    33.     int rows = _labelImg.rows ;  
    34.     int cols = _labelImg.cols ;  
    35.   
    36.     _colorLabelImg.release() ;  
    37.     _colorLabelImg.create(rows, cols, CV_8UC3) ;  
    38.     _colorLabelImg = cv::Scalar::all(0) ;  
    39.   
    40.     for (int i = 0; i < rows; i++)  
    41.     {  
    42.         const int* data_src = (int*)_labelImg.ptr<int>(i) ;  
    43.         uchar* data_dst = _colorLabelImg.ptr<uchar>(i) ;  
    44.         for (int j = 0; j < cols; j++)  
    45.         {  
    46.             int pixelValue = data_src[j] ;  
    47.             if (pixelValue > 1)  
    48.             {  
    49.                 if (colors.count(pixelValue) <= 0)  
    50.                 {  
    51.                     colors[pixelValue] = icvprGetRandomColor() ;  
    52.                 }  
    53.                 cv::Scalar color = colors[pixelValue] ;  
    54.                 *data_dst++   = color[0] ;  
    55.                 *data_dst++ = color[1] ;  
    56.                 *data_dst++ = color[2] ;  
    57.             }  
    58.             else  
    59.             {  
    60.                 data_dst++ ;  
    61.                 data_dst++ ;  
    62.                 data_dst++ ;  
    63.             }  
    64.         }  
    65.     }  
    66. }  


    4)测试程序


    1. //  Connected Component Analysis/Labeling -- Test code  
    2. //  Author:  www.icvpr.com    
    3. //  Blog  :  http://blog.csdn.net/icvpr   
    4. #include <iostream>  
    5. #include <string>  
    6. #include <list>  
    7. #include <vector>  
    8. #include <map>  
    9. #include <stack>  
    10.   
    11. #include <opencv2/imgproc/imgproc.hpp>  
    12. #include <opencv2/highgui/highgui.hpp>  
    13.   
    14. int main(int argc, char** argv)  
    15. {  
    16.     cv::Mat binImage = cv::imread("../icvpr.com.jpg", 0) ;  
    17.     cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY_INV) ;  
    18.   
    19.     // connected component labeling  
    20.     cv::Mat labelImg ;  
    21.     icvprCcaByTwoPass(binImage, labelImg) ;  
    22.     //icvprCcaBySeedFill(binImage, labelImg) ;  
    23.   
    24.     // show result  
    25.     cv::Mat grayImg ;  
    26.     labelImg *= 10 ;  
    27.     labelImg.convertTo(grayImg, CV_8UC1) ;  
    28.     cv::imshow("labelImg", grayImg) ;  
    29.   
    30.     cv::Mat colorLabelImg ;  
    31.     icvprLabelColor(labelImg, colorLabelImg) ;  
    32.     cv::imshow("colorImg", colorLabelImg) ;  
    33.     cv::waitKey(0) ;  
    34.   
    35.     return 0 ;  
    36. }
    展开全文
  • OpenCV中支持的两种背景提取算法都是基于模型密度评估,然后在像素级对图像进行前景背景分类的方法,它们具有相同的假设前提 – 各个像素之间是没有相关性的,跟它们算法思想不同的方法主要是基于马尔可夫随机场...
  • 基于OpenCV和C++的连通区域分析与标记代码:包括两种常见的连通性分析的算法:1)Two- pass;2)Seed-Filling种子填充。
  • 数字图像分析大作业-区域形态分析

    千次阅读 2018-02-27 20:49:02
    基于标记控制的分水岭分割算法通过对图像的前景内部像素进行标记以及背景标记进行计算,并使得梯度图只能在这些标记区域取得极小值,从而有效改善了分水岭容易过分割的特点。  具体实现的流程参考matlab关于Marker-...
  • OpenCV_连通区域分析(Connected Component Analysis/Labeling) 【摘要】本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通性分析法(连通区域标记法)。文中介绍了两种常见的...
  • 本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通性分析法(连通区域标记法)。文中介绍了两种常见的连通性分析的算法:1)Two-pass;2)Seed-Filling种子填充,并给出了两个...
  • OpenCV_连通区域分析与标记(Connected Component ...【摘要】 本文主要介绍在CVPR和图像处理领域中较为常用的一种图像区域(Blob)提取的方法——连通区域分析法(连通区域标记法)。文中介绍了两种常见的连
  • 通过区域运动特性分析提取出背景运动区域,对背景运动区域特征点做帧间匹配;计算仿射参数,配准差分后提取出精确的目标区域。实验结果表明,该算法能够去除前景目标特征点对背景配准的影响,可获得较为精确的目标区域
  • 微软 HoloLens眼镜 现状与前景 分析

    千次阅读 2015-01-22 19:38:33
    @浩浩 分析的似乎很全面,但感觉欠缺一些东西,有一些点也没说到。 1、HoloLens就是增强现实。 虚拟现实与增强现实(眼镜或头盔)的现状与未来-简介http://blog.csdn.net/yanzhanyi/article/details/41982033 增强...
  • regionprops统计被标记的区域的面积分布,显示区域总数。 配合[L,num]=bwlabel(bw,8); %另一篇博文里有函数regionprops语法规则为: STATS = regionprops(C,properties) properties字符串列表 Area ...
  • 背景建模技术(二):BgsLibrary的框架、背景建模的37种算法性能分析背景建模技术的挑战 1、基于MFC的BgsLibrary软件下载 下载地址:http://download.csdn.net/detail/frd2009041510/8691475 该...
  • 下面给出基于种子填充法的连通区域分析方法: (1)扫描图像,直到当前像素点B(x,y) == 1: a、将B(x,y)作为种子(像素位置),并赋予其一个label,然后将该种子相邻的所有前景像素都压入栈中; b、...
  • 为了快速又精确地分离四联指图的前景背景区域,提出了一种基于频域分析的联指图前后背景分离算法。该算法主要包括:①基于局部3阶累积量的前后背景粗分离,有效地降低了噪声对方差的影响,并对低对比度区域能够...
  • 大数据背景下互联网用户行为分析

    千次阅读 2018-03-29 11:03:34
    原文地址:https://blog.csdn.net/sinat_22659313/article/details/53420492一、用户行为分析概念:是指在获得网站访问量基本数据的情况下,对有关数据进行统计、分析,从中发现用户访问网站的规律,并将这些规律与...
  • Tableau数据分析-Chapter07多边形地图和背景地图 本专栏将使用tableau来进行数据分析,Tableau数据分析-Chapter07多边形地图和背景地图:设置地理信息(自定义地图码导入、设置地图源),记录所得所学,作者:北山...
  • 机器视觉的发展现状和前景分析

    千次阅读 2019-03-14 17:53:25
    线扫描是一种在高分辨率下实现更高成像速率的常用技术,但这些组件的集成可能比普通区域成像应用更复杂。同样,高速成像和处理是需要熟练集成的任务。 最后,深度学习变得越来越受欢迎。虽然机器视觉并不是全新的,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 99,856
精华内容 39,942
关键字:

区域背景分析