OpenCV学习笔记


以下两种为连通区域算法实现图像分割

connectedComponents();

其定义如下:

int  connectedComponents (
   	 InputArrayn image,                
     OutputArray labels,               
     int   connectivity = 8,    
     int   ltype = CV_32S      
    );

>第一个参数 image表示8位单通道的二值图像

>第二个参数 labels表示输出的与输入图像相同大小的图像

>第三个参数 connectivity表示区域的连接方式,有4和8两种不同的连接方法

>第四个参数 ltype表示想要使用的标签图像类型


connectedComponentsWithStats();

其定义如下:

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

>第一个参数 image表示8位单通道的二值图像
>
>第二个参数 labels表示输出的与输入图像相同大小的图像
>
>第三个参数 stats表示每个连通区域的外接矩形和面积,它是一个n×5的矩阵,分别表示的是对应轮廓的x,y,width,height,area
>
 - x 用参数 CC_STAT_LEFT 表示 ,代表连通区域对象的左边x坐标
 - y 用参数 CC_STAT_TOP 表示 , 代表连通区域对象的顶部y坐标
 - width 用参数 CC_STAT_WIDTH 表示 , 代表连通区域对象边框的宽度
 - height 用参数 CC_STAT_HEIGHT 表示 , 代表连通区域对象边框的高度
 - area 用参数 CC_STAT_AREA 表示 , 代表连通区域对象的像素数量(面积) 
>
>第四个参数 centroids表示轮廓的中心点,它是一个n×2的矩阵,表示的是对应轮廓的质心坐标
>
>第五个参数 connectivity表示区域的连接方式,有4和8两种不同的连接方法
>
>第六个参数 ltype表示想要使用的标签图像类型



该函数有返回值,返回一个int整型 n,函数返回值为连通区域的总数N,范围为[0,N-1],其中0代表背景。


以下为提取轮廓算法实现图像分割



findContours();

这是一个轮廓提取函数,其定义如下:

void findContours(  InputArray image,
				   	OutputArrayOfArrays contours,
				   	OutoutArray hierarchy,
				   	int mode,
				   	int method,
				   	Point offset = Point() )

>第一个参数 image表示的是输入图像 
>
>第二个参数 contours表示的是输出轮廓(点向量)
>
>第三个参数 hierarchy表示的是存储轮廓层次结构的可选输出向量,也可以得到轮廓间的拓扑关系
>
>第四个参数 mode表示检索轮廓的方法,有以下几种方法:
>
 - RETR_EXTERNAL: 检索外部轮廓
 - RETR_LIST : 检索没有建立层次结构的轮廓
 - RETR_CCOMP : 检索有两个级别层次结构的所有轮廓
 - RETR_TREE : 检索所有轮廓,并创建轮廓之间完整的层次结构
>
>第五个参数 method表示允许执行检索轮廓形状的近似方法,有以下几种方法:
>
 - CV_CHAIN_APPROX_NONE : 并不适用于近似任何轮廓和存储所有的轮廓点
 - CV_CHAIN_APPROX_SIMPLE : 压缩存储水平、垂直和对角线段的起始点和结束点
>
>第六个参数 offset表示的是用于转移所有轮廓的可选点值


drawContours();

这是一个轮廓绘制函数,其定义如下:

void drawContours( 	InputOutputArray image,
 					InputArrayOfArrays contours,
 					int contourId,
 					const Scalar& color,
 					int thickness = 1, 
 					int lineType = 8, 
 					InputArray hierarchy = noArray(),
 					int maxLevel = INT_MAX,
 					Point offset = Point()
)


>第一个参数 image表示要绘制轮廓的图像
>
>第二个参数 contours表示所有输入的轮廓,每个轮廓都被储存成一个点向量
>
>第三个参数 contourId表示指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
>
>第四个参数 color表示绘制轮廓所用的颜色
>
>第五个参数 thickness表示绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充
>
>第六个参数 lineType表示绘制轮廓的线的连通性
>
>第七个参数 hierarchy表示轮廓层级的可选参数,只有绘制部分轮廓时才会用到
>
>第八个参数 maxLevel表示绘制轮廓的最高级别,这个参数只有在hierarchy有效的时候才可使用
>
 - maxLevel=0 : 绘制与输入轮廓属于同一层次的所有轮廓即输入轮廓和与其相邻的轮廓
 - maxLevel=1 : 绘制与输入轮廓同一层次的所有轮廓与其子节点。
 - maxLevel=2 : 绘制与输入轮廓同一层次的所有轮廓与其子节点以及子节点的子节点
>
>第九个参数 offset表示的是用于转移所有轮廓的可选点值


代码演示结果如下

# CMakeLists.txt 内容如下
cmake_minimum_required(VERSION 2.8)
project(test11)

# add c++ standard of 11
set( CMAKE_CXX_FLAGS "-std=c++11" )

# find the library of OpenCV
find_package( OpenCV 3 REQUIRED )

# add head file
include_directories( ${OpenCV_INCLUDE_DIRS} )

add_executable( test11 test11.cpp )

#link the library of OpenCV
target_link_libraries( test11 ${OpenCV_LIBS} )
// test11 内容如下
#include<iostream>
#include<string>
#include<sstream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/features2d/features2d.hpp>

using namespace std;
using namespace cv;

const char * keys = {
	"{help h ? || print this message}"
	"{@image || Image to process}"
	"{@lightPattern || Image light pattern to apply to image input}"
	"{lightMethod l| 1 | Method to remove background light , 0 difference, 1 division } "
	"{segMethod s| 1 | Method to segment: 1 connected Components, 2 connected Components with stats , 3 find contours}"
};


//随机颜色生成
static Scalar randomColor(RNG& rng)
{
	int icolor = (unsigned)rng;
	return Scalar(icolor & 255, (icolor >> 8) & 255, (icolor >> 16) & 255);
}

Mat removeLight( Mat img,Mat pattern , int method )
{
	Mat aux;
	//如果方法是归一化
	if(method==1){
		//先将图像转为32位浮点型方便计算
		Mat img32 , pattern32;
		img.convertTo(img32,CV_32F);
		pattern.convertTo(pattern32,CV_32F);
		//division	
		aux=1-(img32/pattern32);
		aux=255*aux;
		aux.convertTo(aux,CV_8U);
	}else{
		aux=pattern - img;
	}
	return aux;
}

Mat calculateLightPattern( Mat img )
{
	Mat pattern;
	//用基本和有效的方式来计算图像光纹
	blur(img ,pattern , Size(img.cols/3,img.cols/3));
	return pattern;
}


	
void ConnectedComponents(Mat img)
{
	//使用连通区域分离符合要求的部分图像Mat标签
	Mat labels;
	int num_objects = connectedComponents(img , labels);
	if(num_objects <2 ){
		cout<<"No objects detected"<<endl;
		return ;
	}else{
		cout<<"Number of objects detected: "<<num_objects -1<<endl;
	}	//对象中还包括一个背景图像
	
	//创建彩色的输出图像
	Mat output = Mat::zeros(img.rows,img.cols,CV_8UC3);
	RNG rng( 0xFFFFFFFF );
	for(int i=1 ; i<num_objects ; i++){
		Mat mask = labels==i;
		output.setTo(randomColor(rng),mask );
	}

   	imshow("result",output);
}
	

void ConnectedComponentsStats(Mat img)
{
	Mat labels , stats , centroids;
	int num_objects = connectedComponentsWithStats(img , labels , stats , centroids);
	if(num_objects<2){
		cout<<"No objects detected"<<endl;
		return ;
	}else{
		cout<<"Number of objects detected: "<<num_objects - 1<<endl;
	}
	Mat output = Mat::zeros(img.rows,img.cols,CV_8UC3);
	RNG rng( 0xFFFFFFFF );
	for(int i=1 ; i<num_objects ; i++){
		cout<<"Object "<< i << "with position: "<<centroids.at<Point2d>(i) <<"with area : "<<stats.at<int>(i , CC_STAT_AREA)<<endl;
		Mat mask = labels==i;
		output.setTo(randomColor(rng),mask);
		stringstream ss;
		ss<<"area:"<<stats.at<int>(i,CC_STAT_AREA);

		putText(output,ss.str(),centroids.at<Point2d>(i),FONT_HERSHEY_SIMPLEX,0.4,Scalar(255,255,255));
	}
	imshow("result",output);
}


void FindContoursBasic(Mat img)
{
	vector<vector<Point>> contours;
	findContours(img,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
	Mat output=Mat::zeros(img.rows,img.cols,CV_8UC3);
	if(contours.size()==0){
		cout<<"No objects detected"<<endl;
	}else{
		cout<<"Number of objects detected: "<<contours.size()<<endl;
	}
	RNG rng( 0xFFFFFFFF );
	for(int i=1;i<contours.size();i++){
		drawContours(output , contours , i , randomColor(rng) );
	}
	imshow("result",output);
}

int main( int argc , char **argv)
{
	CommandLineParser parser(argc , argv , keys );
	parser.about("Let's Go");

        if(parser.has("help")){
		parser.printMessage();
		return 0;
	}

	String img_file = parser.get<String>(0);
	String light_pattern_file = parser.get<String>(1);
	int method_light = parser.get<int>("lightMethod");
	int method_seg = parser.get<int>("segMethod");

	if(!parser.check()){
		parser.printErrors();
		return 0;
	}

	Mat img = imread(img_file , 0);
	if(img.data==NULL){
		cout<<"Error loading image"<<img_file<<endl;
		return 0;
	}

	//使用中值滤波去除噪声
	Mat img_noise;
	medianBlur(img , img_noise , 3 );
		
	//创建一个光背景,在removeLight函数中作为光纹遮挡
	Mat img_pattern;
        img_pattern=calculateLightPattern(img);

	//使用光纹去除背景
	Mat img_no_light1;
	img_no_light1=removeLight(img_noise,img_pattern,method_light);

	
	//为了分割图像,先进行二值化
	Mat img_thr1;
	if(method_light!=2){
		threshold(img_no_light1,img_thr1,30,255,THRESH_BINARY);
	}else{
		threshold(img_no_light1,img_thr1,140,255,THRESH_BINARY_INV);
	}

	namedWindow("src",0);
	imshow("src",img);
	namedWindow("no_noise",1);
	imshow("no_noise",img_noise);
	namedWindow("no_light",1);
	imshow("no_light",img_no_light1);
	namedWindow("pattern",1);
	imshow("pattern",img_pattern);
	namedWindow("threshold",1);
	imshow("threshold",img_thr1);
	if(method_seg==1){
		cout<<"connected Components"<<endl;
		ConnectedComponents(img_thr1);
	}else if(method_seg==2){
		cout<<"connected Components with stats"<<endl;
		ConnectedComponentsStats(img_thr1);
	}else{
		cout<<"find contours"<<endl;
		FindContoursBasic(img_thr1);
	}

	waitKey(0);
	return 0;

}



演示结果如下



$ ./test11 screw.jpeg -s=1 -l=1    #使用connected Components连通区域 使用除法除去光

opencv 提取连通域 opencv连通域分割_cmake


除去光的过程为: opencv 提取连通域 opencv连通域分割_#include_02


$ ./test11 screw.jpeg -s=1 -l=0     #使用connected Components连通区域算法 使用减法除去光

opencv 提取连通域 opencv连通域分割_#include_03


除去光的过程为: opencv 提取连通域 opencv连通域分割_cmake_04

我们可以发现 使用减法的去光过程得到的图像除法得到的图像 的目标更加模糊,没有那么清晰



$ ./test11 screw.jpeg -s=2 -l=1    #使用connected Components with stats连通区域算法 使用除法除去光

opencv 提取连通域 opencv连通域分割_cmake_05


result结果图中显示了具体的信息


$ ./test11 screw.jpeg -s=1 -l=1    #使用findContours算法 使用除法除去光

opencv 提取连通域 opencv连通域分割_cmake_06


result结果图中显示的是轮廓信息