opencv学习笔记一:边缘检测与轮廓查找

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43667130/article/details/97258568

首先应该区分边缘检测跟轮廓查找的区别,书里的章节把边缘检测放到了第七章:图像变换里,而把轮廓查找放到了第八章:图像轮廓与图像分割修复里。边缘检测算法仅是根据图像像素之间的差异,检测处轮廓边界的像素,但并未把轮廓当作一个整体。而轮廓查找可以将边缘变为一整个轮廓,并可以对其进行一系列其他操作,如矩的计算,轮廓面积计算,轮廓长度计算等。

边缘检测的一般步骤:-》滤波,一般为高斯核Size(5,5)-》增强,可通过计算梯度幅值来确定-》检测,一般通过阈值化的方法检测

边缘检测之Canny算法

Canny边缘检测算法的步骤为:
1:消除噪声。
2:计算梯度幅值和方向(曾强)
3:非极大值抑制
4:滞后阈值
滞后阈值有两个阈值,一个为高阈值,一个为低阈值。
若某一像素高于高阈值,则被保留
若低于低阈值,则被排除
若在两者之间,该像素仅在连接到一个高于高阈值的像素的时候被保留

Canny()函数:

void Canny(
InputArray ima,        #输入图像
outputArray  edges,    #输出图像
double thresholdl,     #高阈值
double thresholed2,    #低阈值
int apertureSize=3,    #Sobel算子大小
bool L2gradient = false#默认值
 );

Canny()函数使用:

// 【1】将原图像转换为灰度图像
	cvtColor( srcImage, grayImage, COLOR_BGR2GRAY );

	// 【2】先用使用 3x3内核来降噪
	blur( grayImage, edge, Size(3,3) );

	// 【3】运行Canny算子
	Canny( edge, edge, 3, 9,3 );

边缘检测之soble算子

soble算法的步骤:
1:分别在X,Y方向对图像I进行求导,分别得到x,y方向的梯度Gx,Gy。
2:对图像每一点求近似梯度:G=Gx,Gy的平方和再开根号

Soble()函数:

void Soble(
InputArray src,         #输入图像
OutputArray dst,      #输出图像
int ddepth,      #输出图像深度
int dx,              #x方向的差分阶数,一般取1
int dy,                #y方向的差分阶数,一般取1
int ksize=3,       #Soble核大小,一般取3,必须为奇数
double scale=1,   #默认值1
double delta=0,   #默认值0
int borderType=BORDER_DEFAULT    #边界模式
);

Soble()函数使用:

//【1】求 X方向梯度
	Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );
	convertScaleAbs( grad_x, abs_grad_x );
	imshow("【效果图】 X方向Sobel", abs_grad_x); 

	//【2】求Y方向梯度
	Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );
	convertScaleAbs( grad_y, abs_grad_y );
	imshow("【效果图】Y方向Sobel", abs_grad_y); 

	//【3】合并梯度(近似)
	addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
	imshow("【效果图】整体方向Sobel", dst); 

轮廓查找

函数一:findCountours()

    void  findCountours(
    InputoutputArray  image,   #8位单通道图像
    outputArrayOfArrays  contours,   #point类型的点向量
    OutputArray  hierarchy,   #可选输出向量  hierarchy【0】hierarchy【1】...
    int mode,    #轮廓检索模式  
    int  method.    #轮廓的近似办法
    Pointoffset=Point()    #每个轮廓点的可选偏移量
    );

找出轮廓后,紧接着自然是将轮廓画出来,就用到绘制轮廓函数
函数二:drawCountours()函数

void drawCountours( 
InputOutArray  image,   #目标图像
InputArrayOfArrays  contours,    #point  类型的vector   存储所有输入的轮廓
int  contourIdx,   #轮廓绘制的指定变量,如果其为负值,则绘制所有轮廓
const  Scalar& color,   #轮廓颜色
int  thickness,   #轮廓线条粗度,默认值1
int lineType,   #线条的类型,默认值8
InputArray  hierarchy=noArray(),    #可选层次结构信息
int  maxLevel=INT_MAX,   #用于绘制的最大等级
Point  offset=Point()    #可选的轮廓偏移参数
)

寻找轮廓的使用:

    // 用Canny算子检测边缘
	Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);
	// 寻找轮廓
	findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	// 绘出轮廓
	Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
	for (int i = 0; i < g_vContours.size(); i++)
	{
		Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//任意值
		drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());
	}

思路就是,如果你直接使用findCountours()函数,效果并不会想象的那样,如下
只用轮廓查找:
在这里插入图片描述我们可以看到效果,边缘其实并不是我们想的那个边缘,这是我们需要先对图像进行Canny()检测,将边缘检测出来,但这时检测出来的边缘并不是一个整体,就需要用边缘查找findContours()函数来查找并将边缘变为一个整体,从而方便对边缘进行进一步处理和运算:

加了Canny()的边缘检测:
在这里插入图片描述
我们可以看到边缘已经被画出来了,线条是用的随机颜色画出的。这时我们还可以对边缘进行一些额外的处理和计算了,会用到一些其他函数,如:

计算轮廓面积:contourArea()
计算轮廓长度:arcLength()
矩的计算: moments()

这些函数在一些特殊的地方会有妙用,比如判断,筛选,返回形状的重心,面积,主轴(moments())等。

霍夫变换

在图像变换这一章中还有一部分用的很多,很重要的一节:霍夫变换。在图像处理和计算机视觉领域中,如何从当前的图像中提取算需要的特征信息是图像识别的关键所在。霍夫变换可以很好的检测直线和圆。

标准霍夫变换HoughLines()函数

函数原型:

C++: void HoughLines(
InputArray image,  #输入图像,需要8位单通道二进制图像,但可以将任意图像源加载进来,再由该函数转换为该格式
OutputArray lines,    #输出矢量
double rho,  #以像素为单位的距离精度
 double theta,   #以弧度为单位的角度精度
  int threshold,    #累加平面的阈值参数
  double srn=0,    
  double stn=0
  )

霍夫圆变换HoughCircles()函数

cvHoughCircles( 
CvArr* image,    #输入图像,8位单通道灰度图
 void* circle_storage,    #圆的输出矢量
  int method,      #使用的检测方法  标识符:HOUGH_GRADIENT
  double dp,      #1.5
  double min_dist,    #检测到的圆与圆之间的最小距离
  double param1=100,   
  double param2=100,   #这个值越大,检测到的圆越完美
  int min_radius=0,    #圆半径的最小值
  int max_radius=0   #圆半径的最大值
  );

猜你喜欢

转载自blog.csdn.net/qq_43667130/article/details/97258568