Opencv2.4.9源码分析——SimpleBlobDetector
- 格式:doc
- 大小:175.00 KB
- 文档页数:11
基于OpenCV2.4.9和Visual Studio2013的人脸自动识别程序冯丽娜,冯宇(昆明医科大学继续教育学院,云南昆明650031)【摘要】随着现代科学技术发展,人脸识别系统应运而生,逐渐成为现代智能化发展的重要表现方法。
在研究人脸识别系统过程中,因为受到全新技术和算法的影响,使得人脸识别系统的研究工作面临重重困难。
所以,本文详细介绍了一种人脸自动识别程序的设计思路,基于OpenCV2.4.9和Visual Studio2013软件收集一定数量的人脸数据,通过深度学习的方式对人脸识别模型进行训练,通过训练好的模型来进行人脸的识别。
通过本文的研究,旨在为当前人脸识别研究提供借鉴。
【关键词】人脸自动识别;OpenCV2.4.9;Visual Studio2013;深度学习【中图分类号】TP391【文献标识码】A【文章编号】1006-4222(2020)12-0188-04因为受到年龄因素、气色因素、光线因素、化妆水平因素、拍摄角度因素以及配饰挂件因素影响,即使是同一个人,在完成照片拍摄以后,所呈现出来的像素层面也存在很大的不同,专家可以在获取较高准确率特征值的时候,需要根据专业经验以及不断试错以获取。
因此,要实现计算机对人脸的识别,需要用到深度学习的算法。
深度学习在应用中,所呈现的最大优势是借助于训练算法对各参数权重实现自行调整,从而快速完成函数构建并保证准确率。
当给定一张照片时,通过数据计算能够捕获特征值,完成归类,最终实现对人脸的识别。
1自动识别程序的介绍本程序分为两大环节以及三个阶段。
为了实现人脸的自动识别,需要具有以下两个环节:人脸检测,即检测出照片中的人脸位置;人脸识别,即给定待检测人脸和一定规模的数据集,最终识别出人脸。
具体的实施分为三个阶段:人脸数据的收集和预处理、训练模型、人脸识别。
OpenCV有机整合了计算机视觉算法和图像处理算法等多种通用算法,作为计算机视觉库,在应用中还有很高的使用价值。
Opencv2.4.9源码分析——CascadeClassification(二)训练级联分类器的源码在OpenCV/sources/app/traincascade目录下。
首先我们给出级联分类器的特征类型的相关类和函数。
CvHaarFeatureParams、CvLBPFeatureParams和CvHOGFeatureParams分别表示HAAR状特征、LBP特征和HOG特征的参数类,它们都继承于CvFeatureParams:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片class CvFeatureParams : public CvParams{public://级联分类器能够使用三种特征类型用于训练样本:HAAR、LBP和HOGenum { HAAR = 0, LBP = 1, HOG = 2 };//缺省构造函数,赋值maxCatCount为0,featSize为1CvFeatureParams();//初始化maxCatCount和featSizevirtual void init( const CvFeatureParams& fp );//表示向params.xml文件写入一些信息:maxCatCount和featSize的值virtual void write( cv::FileStorage &fs ) const;//表示从params.xml文件内读取一些信息:maxCatCount和featSize的值virtual bool read( const cv::FileNode &node );//构建相应的特征参数,即级联分类器具体使用哪种特征,如果输入参数featureType=0,则应用HAAR,该函数返回CvHaarFeatureParams的指针;如果featureType=1,则应用LBP,该函数返回CvLBPFeatureParams的指针;如果featureType=2,则应用HOG,该函数返回CvHOGFeatureParams的指针。
`SimpleBlobDetector` 是OpenCV 中的一个类,用于检测图像中的简单blob。
Blob 通常指的是一个与周围像素明显不同的区域,通常用于检测物体、标记图像或进行其他视觉处理任务。
`detect` 方法是`SimpleBlobDetector` 类的一个成员函数,用于在给定的图像上检测blob。
参数:* `image`:输入图像,通常是灰度图像。
* `threshold`:二值化图像时使用的阈值。
* `threshold1` 和`threshold2`:两个阈值,用于计算blob 的大小。
* `minWidth` 和`maxWidth`:blob 的最小和最大宽度。
* `minHeight` 和`maxHeight`:blob 的最小和最大高度。
* `minRepeat`:最小重复次数,用于去除噪声。
* `minDistXB` 和`minDistYB`:X 和Y 方向上的最小距离,用于去除重叠的blobs。
返回值:* 检测到的blob 的坐标列表。
示例代码:```pythonimport cv2import numpy as np# 加载图像并转换为灰度图像image = cv2.imread('input.jpg')gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 创建SimpleBlobDetector 对象params = cv2.SimpleBlobDetector_Params() params.filterByArea = Trueparams.minArea = 1500params.filterByCircularity = Trueparams.minCircularity = 0.1params.filterByConvexity = Trueparams.minConvexity = 0.87params.filterByInertia = Trueparams.minInertiaRatio = 0.01detector = cv2.SimpleBlobDetector_create(params)# 检测blobskeypoints = detector.detect(gray)# 在图像上绘制检测到的blobsimage_with_keypoints = cv2.drawKeypoints(image, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)cv2.imshow('Blobs', image_with_keypoints)cv2.waitKey(0)cv2.destroyAllWindows()```这段代码首先加载一个图像并将其转换为灰度图像。
Opencv2.4.9源码分析——CascadeClassification(三)下面我们以车牌识别为例,具体讲解OpenCV的级联分类器的用法。
在这里我们只对蓝底白字的普通车牌进行识别判断,对于其他车牌不在考虑范围内。
而且车牌是正面照,略微倾斜可以,倾斜程度太大也是不在识别范围内的。
我们通过不同渠道共收集了1545幅符合要求的带有车牌图像的照片(很遗憾,我只能得到这么多车牌照片,如果能再多一些就更好了!),通过ACDSee软件手工把车牌图像从照片中剪切出来,并统一保存为jpg格式。
为便于后续处理,我们把文件名按照数字顺序命名,如图8所示。
然后我们把这些车牌图像保存到pos文件夹内。
图8 蓝底白字车牌图像需要注意的是,在这里我们没有必要把车牌图像缩放成统一的尺寸(即正样本图像的大小),更没有必要把它们转换成灰度图像,这些工作完全可以由系统完成。
我们只需要告诉系统车牌图像文件、车牌的位置,以及车牌的尺寸大小即可。
为了高效的完成上述工作,我们编写了以下代码:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片#include "opencv2/core/core.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include <iostream>#include <fstream>#include <string>using namespace cv;using namespace std;int main( int argc, char** argv ){ofstream postxt("pos.txt",ios::out); //创建pox.txt文件if ( !postxt.is_open() ){cout<<"can not creat pos txt file!";return false;}//N表示车牌图像的总数,c表示最终可以利用的车牌样本图像的数量int N = 1545, c = 0;int width, height, i;String filename;Mat posimage;for(i=0;i<N;i++) //遍历所有车牌图像{filename = to_string(i) + ".jpg"; //得到当前车牌图像的文件名posimage = imread("pos\\" + filename); //打开当前车牌图像if ( posimage.empty() ){cout<<"can not open "+ filename +" file!"<<endl;continue;}width = posimage.size().width; //当前车牌图像的宽height = posimage.size().height; //当前车牌图像的高//如果当前车牌图像的宽小于60,或高小于20,则剔除该车牌图像if(width < 60 || height < 20){cout<<filename +" too small!"<<endl;continue;}//把当前车牌图像的信息写入pos.txt文件内postxt<<"pos/" + filename + " 1 0 0 " + to_string(width) + " " + to_string(height)<<endl;c++; //累计}cout<<c; //终端输出c值postxt.close(); //关闭pos.txt文件return 0;}执行完该程序后,在终端输出得到的c值为1390,这说明有155(1545-1390)个车牌图像由于尺寸过小而被剔除。
Opencv之斑点(Blob)检测--SimpleBlobDetector_create转载:斑点(Blob)的定义: 图像特征点检测包括⾓点和斑点,斑点是指⼆维图像中和周围颜⾊有颜⾊差异和灰度差异的区域,因为斑点代表的是⼀个区域,所以其相对于单纯的⾓点,具有更好的稳定性和更好的抗⼲扰能⼒.斑点通常是指与周围有着颜⾊和灰度差别的区域。
在实际地图中,往往存在着⼤量这样的斑点,如⼀颗树是⼀个斑点,⼀块草地是⼀个斑点,⼀栋房⼦也可以是⼀个斑点。
由于斑点代表的是⼀个区域,相⽐单纯的⾓点,它的稳定性要好,抗噪声能⼒要强,所以它在图像配准上扮演了很重要的⾓⾊。
同时有时图像中的斑点也是我们关⼼的区域,⽐如在医学与⽣物领域,我们需要从⼀些X光照⽚或细胞显微照⽚中提取⼀些具有特殊意义的斑点的位置或数量。
SimpleBlobDetector_create的原理:1. 阈值:通过使⽤以minThreshold开始的阈值对源图像进⾏阈值处理,将源图像转换为多个⼆进制图像。
这些阈值以thresholdStep递增,直到maxThreshold。
因此,第⼀个阈值为minThreshold,第⼆个阈值为minThreshold + thresholdStep,第三个阈值为minThreshold + 2 x thresholdStep,依此类推;2. 分组:在每个⼆进制图像中,连接的⽩⾊像素被分组在⼀起。
我们称这些⼆进制blob;3. 合并:计算⼆进制图像中⼆进制斑点的中⼼,并合并⽐minDistBetweenBlob更近的斑点;4. 中⼼和半径计算:计算并返回新合并的Blob的中⼼和半径。
SimpleBlobDetector_create可按颜⾊,⼤⼩和形状来过滤斑点类型:1. 按颜⾊:⾸先需要设置filterByColor =True。
设置blobColor = 0可选择较暗的blob,blobColor = 255可以选择较浅的blob。
Opencv2.4.9源码分析——RandomTrees一、原理随机森林(Random Forest)的思想最早是由Ho于1995年首次提出,后来Breiman完整系统的发展了该算法,并命名为随机森林,而且他和他的博士学生兼同事Cutler把Random Forest注册成了商标,这可能也是OpenCV把该算法命名为Random Trees的原因吧。
一片森林是由许多棵树木组成,森林中的每棵树可以说是彼此不相关,也就是说每棵树木的生长完全是由自身条件决定的,只有保持森林的多样性,森林才能更好的生长下去。
随机森林算法与真实的森林相类似,它是由许多决策树组成,每棵决策树之间是不相关的。
而随机森林算法的独特性就体现在“随机”这两个字上:通过随机抽取得到不同的样本来构建每棵决策树;决策树每个节点的最佳分叉属性是从由随机得到的特征属性集合中选取。
下面就详细介绍这两次随机过程。
虽然在生成每棵决策树的时候,使用的是相同的参数,但使用的是不同的训练集合,这些训练集合是从全体训练样本中随机得到的,这一过程称之为bootstrap过程,得到的随机子集称之为bootstrap集合,而在bootstrap集合的基础上聚集得到的学习模型的过程称之为Bagging (Bootstrap aggregating),那些不在bootstrap集合中的样本称之为OOB(Out Of Bag)。
Bootstrap过程为:从全部N个样本中,有放回的随机抽取S次(在Opencv中,S=N),由于是有放回的抽取,所以肯定会出现同一个样本被抽取多次的现象,因此即使S=N,也会存在OOB。
我们可以计算OOB样本所占比率:每个样本被抽取的概率为1/N,未被抽取的概率为(1-1/N),抽取S次仍然没有被抽到的概率就为(1-1/N)S,如果S和N都趋于无穷大,则(1-1/N)S≈e-1=0.368,即OOB样本所占全部样本约为36.8%,被抽取到的样本为63.2%。
Opencv2.4.9源码分析——NeuralNetworks一、原理神经网络(Neural Networks)是一种模仿生物神经系统的机器学习算法。
该算法的提出最早可追述至上个世纪四十年代,这几乎与电子计算机的历史同步。
但它的发展并非一帆风顺,也经历了初创阶段—黄金阶段—停滞阶段—复兴阶段,直到目前的高速发展阶段。
年初由Google公司开发的神经网络围棋——AlphaGo击败世界围棋冠军李世石,使神经网络技术更是受到世人的注目,因为它的意义要远大于1997年IBM的超级计算机——深蓝击败国际象棋大师卡斯帕罗夫。
与生物神经系统相似,人工神经网络也是由若干个神经元构成。
如图1所示,x1、x2、…xn 为该神经元的输入,y为该神经元的输出。
显然,不同的输入对神经元的作用是不同的,因此用权值w1、w2、…wn来表示这种影响程度的不同。
神经元内部包括两个部分,第一个部分是对输入的加权求和,第二个部分是对求和的结果进行“激活”,得到输出。
加权求和的公式为:对于MLP,我们可以用Backprop(backward propagation oferrors,误差的反向传播,简称BP)算法实现它的建模,该算法具有结构简单、易于实现等特点。
Backprop算法是一种监督的机器学习算法,输入层的神经元数量一般为样本的特征属性的数量,输出层的神经元的数量一般为样本的所有的可能目标值的数量,如果是分类问题,则为样本的分类数量,因此,与其他机器学习算法不同,在MLP中,样本对应的响应值应该是一个相量,相量的维数与输出层的神经元的数量一致。
而隐含层的层数以及各层神经元的数量则根据实际情况进行选取。
Backprop算法的核心思想是:通过前向通路(箭头的方向)得到误差,再把该误差反向传播实现权值w的修正。
MLP的误差可以用平方误差函数来进行表示。
设某个样本x对应的目标值为t,样本x有n 个特征属性,即x={x1, x2,…,xn},目标值t有J种可能的值,即t={t1, t2,…,tJ},因此该MLP 的输入层(即第一层)一共有n个神经元,输出层(即第L层,设MLP一共有L层)一共有J个神经元。
OpenCV⼈脸识别LBPH算法源码分析1 背景及理论基础⼈脸识别是指将⼀个需要识别的⼈脸和⼈脸库中的某个⼈脸对应起来(类似于指纹识别),⽬的是完成识别功能,该术语需要和⼈脸检测进⾏区分,⼈脸检测是在⼀张图⽚中把⼈脸定位出来,完成的是搜寻的功能。
从OpenCV2.4开始,加⼊了新的类FaceRecognizer,该类⽤于⼈脸识别,使⽤它可以⽅便地进⾏相关识别实验。
原始的LBP算⼦定义为在3*3的窗⼝内,以窗⼝中⼼像素为阈值,将相邻的8个像素的灰度值与其进⾏⽐较,若周围像素值⼤于或等于中⼼像素值,则该像素点的位置被标记为1,否则为0。
这样,3*3邻域内的8个点经⽐较可产⽣8位⼆进制数(通常转换为⼗进制数即LBP码,共256种),即得到该窗⼝中⼼像素点的LBP值,并⽤这个值来反映该区域的纹理特征。
如下图所⽰:原始的LBP提出后,研究⼈员不断对其提出了各种改进和优化。
1.1 圆形LBP算⼦基本的 LBP算⼦的最⼤缺陷在于它只覆盖了⼀个固定半径范围内的⼩区域,这显然不能满⾜不同尺⼨和频率纹理的需要。
为了适应不同尺度的纹理特征,Ojala等对LBP算⼦进⾏了改进,将3×3邻域扩展到任意邻域,并⽤圆形邻域代替了正⽅形邻域,改进后的LBP算⼦允许在半径为R的圆形邻域内有任意多个像素点,从⽽得到了诸如半径为R的圆形区域内含有P个采样点的LBP算⼦,OpenCV中正是使⽤圆形LBP算⼦,下图⽰意了圆形LBP算⼦:1.2 旋转不变模式从LBP的定义可以看出,LBP算⼦是灰度不变的,但却不是旋转不变的,图像的旋转就会得到不同的LBP值。
Maenpaa等⼈⼜将LBP算⼦进⾏了扩展,提出了具有旋转不变性的LBP算⼦,即不断旋转圆形邻域得到⼀系列初始定义的LBP值,取其最⼩值作为该邻域的LBP值。
下图给出了求取旋转不变LBP的过程⽰意图,图中算⼦下⽅的数字表⽰该算⼦对应的LBP值,图中所⽰的8种LBP模式,经过旋转不变的处理,最终得到的具有旋转不变性的LBP值为15。
Opencv2.4.9源码分析——SimpleBlobDetectorOpenCV中提供了SimpleBlobDetector的特征点检测方法,正如它的名称,该算法使用最简单的方式来检测斑点类的特征点。
下面我们就来分析一下该算法。
首先通过一系列连续的阈值把输入的灰度图像转换为一个二值图像的集合,阈值范围为[T1,T2],步长为t,则所有阈值为:T1,T1+t,T1+2t,T1+3t,……,T2 (1)第二步是利用Suzuki提出的算法通过检测每一幅二值图像的边界的方式提取出每一幅二值图像的连通区域,我们可以认为由边界所围成的不同的连通区域就是该二值图像的斑点;第三步是根据所有二值图像斑点的中心坐标对二值图像斑点进行分类,从而形成灰度图像的斑点,属于一类的那些二值图像斑点最终形成灰度图像的斑点,具体来说就是,灰度图像的斑点是由中心坐标间的距离小于阈值Tb的那些二值图像斑点所组成的,即这些二值图像斑点属于该灰度图像斑点;最后就是确定灰度图像斑点的信息——位置和尺寸。
位置是属于该灰度图像斑点的所有二值图像斑点中心坐标的加权和,即公式2,权值q等于该二值图像斑点的惯性率的平方,它的含义是二值图像的斑点的形状越接近圆形,越是我们所希望的斑点,因此对灰度图像斑点位置的贡献就越大。
尺寸则是属于该灰度图像斑点的所有二值图像斑点中面积大小居中的半径长度。
其中,H表示该斑点的凸壳面积在计算斑点的面积,中心处的坐标,尤其是惯性率时,都可以应用图像矩的方法。
下面我们就介绍该方法。
矩在统计学中被用来反映随机变量的分布情况,推广到力学中,它被用来描述空间物体的质量分布。
同样的道理,如果我们将图像的灰度值看作是一个二维的密度分布函数,那么矩方法即可用于图像处理领域。
设f(x,y)是一幅数字图像,则它的矩Mij为:下面给出SimpleBlobDetector的源码分析。
我们先来看看SimpleBlobDetector类的默认参数的设置:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片SimpleBlobDetector::Params::Params(){thresholdStep = 10; //二值化的阈值步长,即公式1的tminThreshold = 50; //二值化的起始阈值,即公式1的T1maxThreshold = 220; //二值化的终止阈值,即公式1的T2//重复的最小次数,只有属于灰度图像斑点的那些二值图像斑点数量大于该值时,该灰度图像斑点才被认为是特征点minRepeatability = 2;//最小的斑点距离,不同二值图像的斑点间距离小于该值时,被认为是同一个位置的斑点,否则是不同位置上的斑点minDistBetweenBlobs = 10;filterByColor = true; //斑点颜色的限制变量blobColor = 0; //表示只提取黑色斑点;如果该变量为255,表示只提取白色斑点filterByArea = true; //斑点面积的限制变量minArea = 25; //斑点的最小面积maxArea = 5000; //斑点的最大面积filterByCircularity = false; //斑点圆度的限制变量,默认是不限制minCircularity = 0.8f; //斑点的最小圆度//斑点的最大圆度,所能表示的float类型的最大值maxCircularity = std::numeric_limits<float>::max();filterByInertia = true; //斑点惯性率的限制变量//minInertiaRatio = 0.6;minInertiaRatio = 0.1f; //斑点的最小惯性率maxInertiaRatio = std::numeric_limits<float>::max(); //斑点的最大惯性率filterByConvexity = true; //斑点凸度的限制变量//minConvexity = 0.8;minConvexity = 0.95f; //斑点的最小凸度maxConvexity = std::numeric_limits<float>::max(); //斑点的最大凸度}我们再来介绍检测二值图像斑点的函数findBlobs。
Core模块
1、基本数据结构
(1)、class DataType
这个类主要目的是把并行时间的类型信息转化为与opencv相符且识别的数据类型。
(2)class Point_
这个类模板是为指定以x、y为坐标的二维点。
它的功能是指定坐标点的数据类型,不同数据类型坐标点的转化,以及坐标点的一些操作(加、减、乘、除)。
也可以与旧版本的CvPoint和CvPoint2D32f兼容。
(3)class Point3_
这个模板类与上面类似,只是它是针对3D的。
(4)class Size_
这个模板类是为了指定一幅图片或矩形框的大小,包括两个成员高和宽,可以与旧版本的CvSize和CvSize2D32f兼容,可以对其中的值进行算术和比较操作。
(5)class Rect_
这个模板类是为二维矩形框设计的,。
OpenCV源码架构讲解1. Core(核心):此模块包含了OpenCV的核心功能,如矩阵操作、数据类型、数组和向量运算等。
该模块提供了基础的数据结构和函数,为其他模块的实现提供支持。
2. Imgproc(图像处理):此模块提供了各种图像处理函数,如滤波、边缘检测、图像变换等。
它包含了大量的图像处理算法,并提供了丰富的工具函数,方便用户进行图像处理操作。
3. Highgui(图形用户界面):该模块提供了图形用户界面相关的函数,如图像显示、鼠标事件处理、键盘事件处理等。
它可以帮助用户在图像处理过程中进行交互操作,方便调试和分析。
4. Video(视频处理):此模块提供了与视频处理相关的函数,如视频捕捉、视频压缩、视频写入等。
它支持从摄像机、文件或其他源中读取视频流,并提供了一系列的视频处理算法。
5. Objdetect(对象检测):该模块提供了对象检测相关的函数,如人脸检测、行人检测等。
它提供了训练好的模型和相应的算法,可以用于识别和跟踪不同种类的对象。
此外,OpenCV还包含了一些辅助模块,如ml(机器学习)、calib3d (相机标定和三维重建)、features2d(特征检测和描述子)、videoio (视频IO)、flann(最近邻)等。
OpenCV的源码采用模块化的结构,使得用户可以灵活地选择所需的模块,以适应不同的应用场景。
每个模块都有自己的命名空间和头文件,以避免命名冲突。
同时,源码还提供了丰富的示例和文档,方便用户使用和理解。
在OpenCV的源码中,各个模块之间存在着一定的依赖关系,需要进行编译和链接才能生成可执行文件。
此外,OpenCV还提供了对Python、Java等语言的接口,使得用户可以在不同的平台上使用OpenCV。
总之,OpenCV的源码架构是基于C++实现的,模块化设计使得用户可以方便地选择所需的功能模块。
各个模块之间存在着一定的依赖关系,通过编译和链接可以生成可执行文件。
blob算法c源代码Blob算法通常用于图像处理中检测图像中的亮点或暗点区域。
以下是一个简单的Blob算法的C语言实现。
注意,这只是一个基本的实现,并且可能需要根据您的特定需求进行修改和优化。
c复制代码#include <stdio.h>#include <stdlib.h>#include <opencv2/opencv.hpp>#define MIN_SIZE 50 // 最小Blob大小#define MAX_ITERATION 5 // 迭代次数int process_image(cv::Mat& img) {cv::Mat img_copy = img.clone();cv::Mat result;cv::Mat element = cv::getStructuringElement(c v::MORPH_RECT, cv::Size(15, 15));for (int i = 0; i < MAX_ITERATION; i++) {cv::dilate(img_copy, result, element);cv::erode(result, img_copy, element);}cv::dilate(img_copy, result, element);cv::erode(result, img_copy, element);cv::Mat temp;img_copy.copyTo(temp);cv::inpaint(img, result, temp, 3, cv::INPAINT _TELEA);cv::imshow("Result", temp);cv::waitKey();return 0;}int main(int argc, char** argv) {if (argc != 2) {printf("Usage: %s <image path>\n", argv[0]);return -1;}cv::Mat img = cv::imread(argv[1], cv::IMREAD_ GRAYSCALE);if (img.empty()) {printf("Error: Could not load image file.\n ");return -1;}process_image(img);return 0;}这个程序首先读取一个灰度图像,然后通过连续的膨胀和腐蚀操作来生成一个标记所有亮点的掩模。
opencv实现静态手势识别 opencv实现剪刀石头布游戏这篇文章主要为大家详细介绍了opencv实现静态手势识别,opencv实现剪刀石头布游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下本文实例为大家分享了opencv实现静态手势识别的具体代码,供大家参考,具体内容如下要想运行该代码,请确保安装了:python 2.7,opencv 2.4.9效果如下:算法如下:把图片先进行处理,处理过程:1.用膨胀图像与腐蚀图像相减的方法获得轮廓。
2.用二值化获得图像3. 反色经过如上的处理之后,图片为:这之后就简单了,设计一个办法把三种图像区分开来即可。
代码如下:# -*- coding: cp936 -*-import cv2import numpyimport timeimport randomimport osdef judge( ):#构造一个3×3的结构元素# return 0 stone ,1 jiandao, 2 buimg = cv2.imread("wif.jpg",0)element = cv2.getStructuringElement(cv2.MORPH_RECT,(11,11))dilate = cv2.dilate(img, element)erode = cv2.erode(img, element)#将两幅图像相减获得边,第一个参数是膨胀后的图像,第二个参数是腐蚀后的图像result = cv2.absdiff(dilate,erode);#上面得到的结果是灰度图,将其二值化以便更清楚的观察结果retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);#反色,即对二值图每个像素取反result = cv2.bitwise_not(result);result =cv2.medianBlur(result,23)a=[]posi =[]width =[]count = 0area = 0for i in range(result.shape[1]):for j in range(result.shape[0]):if(result[j][i]==0):area+=1for i in range(result.shape[1]):if(result[5*result.shape[0]/16][i]==0 and result[5*result.shape[0]/16][i-1]!=0 ):count+=1width.append(0)posi.append(i)if(result[5*result.shape[0]/16][i]==0):width[count-1]+=1"""print 'the pic width is ',result.shape[1],'\n'for i in range(count):print 'the ',i,'th',' ','is';print 'width ',width[i]print 'posi ',posi[i],'\n'print count,'\n'print 'area is ',area,'\n'cv2.line(result,(0,5*result.shape[0]/16),(214,5*result.shape[0]/16),(0,0,0))dWindow("fcuk")cv2.imshow("fcuk",result)cv2.waitKey(0)"""#判定时间width_length=0width_jiandao = Truefor i in range(count):if width[i]>45:#print 'bu1';return 2;if width[i]<=20 or width[i]>=40:width_jiandao= Falsewidth_length += width[i]if width_jiandao==True and count==2:return 1;if(area <8500):#print 'shi tou';return 0;print "width_leng",width_lengthif(width_length<35):#这个时候说明照片是偏下的,所以需要重新测定。
Opencv2.4.9源码分析——GradientBoosted Trees一、原理梯度提升树(GBT,Gradient Boosted Trees,或称为梯度提升决策树)算法是由Friedman 于1999年首次完整的提出,该算法可以实现回归、分类和排序。
GBT的优点是特征属性无需进行归一化处理,预测速度快,可以应用不同的损失函数等。
从它的名字就可以看出,GBT包括三个机器学习的优化算法:决策树方法、提升方法和梯度下降法。
前两种算法在我以前的文章中都有详细的介绍,在这里我只做简单描述。
决策树是一个由根节点、中间节点、叶节点和分支构成的树状模型,分支代表着数据的走向,中间节点包含着训练时产生的分叉决策准则,叶节点代表着最终的数据分类结果或回归值,在预测的过程中,数据从根节点出发,沿着分支在到达中间节点时,根据该节点的决策准则实现分叉,最终到达叶节点,完成分类或回归。
提升算法是由一系列“弱学习器”构成,这些弱学习器通过某种线性组合实现一个强学习器,虽然这些弱学习器的分类或回归效果可能仅仅比随机分类或回归要好一点,但最终的强学习器却可以得到一个很好的预测结果。
二、源码分析下面介绍OpenCV的GBT源码。
首先给出GBT算法所需参数的结构体CvGBTreesParams:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片CvGBTreesParams::CvGBTreesParams( int _loss_function_type, int _weak_count,float _shrinkage, float _subsample_portion,int _max_depth, bool _use_surrogates ): CvDTreeParams( 3, 10, 0, false, 10, 0, false, false, 0 ){loss_function_type = _loss_function_type;weak_count = _weak_count;shrinkage = _shrinkage;subsample_portion = _subsample_portion;max_depth = _max_depth;use_surrogates = _use_surrogates;}loss_function_type表示损失函数的类型,CvGBTrees::SQUARED_LOSS为平方损失函数,CvGBTrees::ABSOLUTE_LOSS为绝对值损失函数,CvGBTrees::HUBER_LOSS为Huber损失函数,CvGBTrees::DEVIANCE_LOSS为偏差损失函数,前三种用于回归问题,后一种用于分类问题weak_count表示GBT的优化迭代次数,对于回归问题来说,weak_count也就是决策树的数量,对于分类问题来说,weak_count×K为决策树的数量,K表示类别数量shrinkage表示收缩因子vsubsample_portion表示训练样本占全部样本的比例,为不大于1的正数max_depth表示决策树的最大深度use_surrogates表示是否使用替代分叉节点,为true,表示使用替代分叉节点CvDTreeParams结构详见我的关于决策树的文章CvGBTrees类的一个构造函数:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片CvGBTrees::CvGBTrees( const cv::Mat& trainData, int tflag,const cv::Mat& responses, const cv::Mat& varIdx,const cv::Mat& sampleIdx, const cv::Mat& varType,const cv::Mat& missingDataMask,CvGBTreesParams _params ){data = 0; //表示样本数据集合weak = 0; //表示一个弱学习器default_model_name = "my_boost_tree";// orig_response表示样本的响应值,sum_response表示拟合函数Fm(x),sum_response_tmp表示Fm+1(x)orig_response = sum_response = sum_response_tmp = 0;// subsample_train和subsample_test分别表示训练样本集和测试样本集subsample_train = subsample_test = 0;// missing表示缺失的特征属性,sample_idx表示真正用到的样本的索引missing = sample_idx = 0;class_labels = 0; //表示类别标签class_count = 1; //表示类别的数量delta = 0.0f; //表示Huber损失函数中的参数δclear(); //清除一些全局变量和已有的所有弱学习器//GBT算法的学习train(trainData, tflag, responses, varIdx, sampleIdx, varType, missingDataMask, _params, false);}GBT算法的学习构建函数:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片boolCvGBTrees::train( const CvMat* _train_data, int _tflag,const CvMat* _responses, const CvMat* _var_idx,const CvMat* _sample_idx, const CvMat* _var_type,const CvMat* _missing_mask,CvGBTreesParams _params, bool /*_update*/ ) //update is not supported//_train_data表示样本数据集合//_tflag表示样本矩阵的存储格式//_responses表示样本的响应值//_var_idx表示要用到的特征属性的索引//_sample_idx表示要用到的样本的索引//_var_type表示特征属性的类型,是连续值还是离散值//_missing_mask表示缺失的特征属性的掩码//_params表示构建GBT模型的一些必要参数{CvMemStorage* storage = 0; //开辟一块内存空间params = _params; //构建GBT模型所需的参数bool is_regression = problem_type(); //表示该GBT模型是否用于回归问题clear(); //清空一些全局变量和已有的所有弱学习器/*n - count of samplesm - count of variables*/int n = _train_data->rows; //n表示训练样本的数量int m = _train_data->cols; //m表示样本的特征属性的数量//如果参数_tflag为CV_ROW_SAMPLE,则表示训练样本以行的形式储存的,即_train_data矩阵的每一行为一个样本,那么n和m无需交换;否则如果_tflag为CV_COL_SAMPLE,则表示样本是以列的形式储存的,那么n和m就需要交换。
simpleblobdetector detect 解析-回复SimpleBlobDetector是OpenCV的一个函数,用于检测二进制图像中的斑点(blob)。
斑点是一些具有相似颜色或亮度的连通区域,它们在图像中形成了一个单独的实体。
在计算机视觉领域,斑点通常代表了一些重要的信息,比如目标物体或图像中的关键点。
本文将详细解析SimpleBlobDetector的工作机制及其应用场景。
SimpleBlobDetector是一个基于简单阈值和形态学运算的算法,用于检测斑点。
它的实现依赖于OpenCV库中的一些运算方法,如二值化、形态学操作和连通区域分析。
下面将详细介绍SimpleBlobDetector的工作步骤。
首先,SimpleBlobDetector需要输入一幅二值图像作为算法的输入。
二值图像是指每个像素只有两个可能的取值,比如黑白图像或者经过阈值处理得到的图像。
因此,在使用SimpleBlobDetector之前,需要对原始图像进行二值化处理。
二值化将图像中的亮度信息转换为0和255两个离散的值,方便后续处理。
接下来,SimpleBlobDetector使用形态学运算来增强斑点的检测结果。
形态学运算是一组针对二值图像中的形状和结构进行处理的数学方法。
在SimpleBlobDetector中,常用的形态学运算有腐蚀和膨胀操作。
腐蚀操作将斑点的边界进行腐蚀,去除不完整的斑点;而膨胀操作则将斑点的边界进行膨胀,填充其中的空洞。
通过腐蚀和膨胀操作,SimpleBlobDetector能够更好地提取斑点的形状和结构信息。
完成形态学运算后,SimpleBlobDetector使用连通区域分析方法来找到图像中的斑点。
连通区域分析是一种将图像中的像素根据其连接关系进行分类的方法。
在SimpleBlobDetector中,它将临近的具有相似颜色或亮度的像素归为一个连通区域,从而得到斑点的位置和大小信息。
连通区域分析可以通过迭代算法或者扫描算法来实现,而SimpleBlobDetector则选择了更简单且高效的算法实现。
Opencv2.4.9源码分析——Extremelyrandomized trees一、原理ET或Extra-Trees(Extremely randomized trees,极端随机树)是由PierreGeurts等人于2006年提出。
该算法与随机森林算法十分相似,都是由许多决策树构成。
但该算法与随机森林有两点主要的区别:1、随机森林应用的是Bagging模型,而ET是使用所有的训练样本得到每棵决策树,也就是每棵决策树应用的是相同的全部训练样本;2、随机森林是在一个随机子集内得到最佳分叉属性,而ET是完全随机的得到分叉值,从而实现对决策树进行分叉的。
对于第2点的不同,我们再做详细的介绍。
我们仅以二叉树为例,当特征属性是类别的形式时,随机选择具有某些类别的样本为左分支,而把具有其他类别的样本作为右分支;当特征属性是数值的形式时,随机选择一个处于该特征属性的最大值和最小值之间的任意数,当样本的该特征属性值大于该值时,作为左分支,当小于该值时,作为右分支。
这样就实现了在该特征属性下把样本随机分配到两个分支上的目的。
然后计算此时的分叉值(如果特征属性是类别的形式,可以应用基尼指数;如果特征属性是数值的形式,可以应用均方误差)。
遍历节点内的所有特征属性,按上述方法得到所有特征属性的分叉值,我们选择分叉值最大的那种形式实现对该节点的分叉。
从上面的介绍可以看出,这种方法比随机森林的随机性更强。
对于某棵决策树,由于它的最佳分叉属性是随机选择的,因此用它的预测结果往往是不准确的,但多棵决策树组合在一起,就可以达到很好的预测效果。
当ET构建好了以后,我们也可以应用全部的训练样本来得到该ET的预测误差。
这是因为尽管构建决策树和预测应用的是同一个训练样本集,但由于最佳分叉属性是随机选择的,所以我们仍然会得到完全不同的预测结果,用该预测结果就可以与样本的真实响应值比较,从而得到预测误差。
如果与随机森林相类比的话,在ET中,全部训练样本都是OOB样本,所以计算ET的预测误差,也就是计算这个OOB误差。
Opencv2.4.9源码分析——SimpleBlobDetectorOpenCV中提供了SimpleBlobDetector的特征点检测方法,正如它的名称,该算法使用最简单的方式来检测斑点类的特征点。
下面我们就来分析一下该算法。
首先通过一系列连续的阈值把输入的灰度图像转换为一个二值图像的集合,阈值范围为[T1,T2],步长为t,则所有阈值为:T1,T1+t,T1+2t,T1+3t,……,T2 (1)第二步是利用Suzuki提出的算法通过检测每一幅二值图像的边界的方式提取出每一幅二值图像的连通区域,我们可以认为由边界所围成的不同的连通区域就是该二值图像的斑点;第三步是根据所有二值图像斑点的中心坐标对二值图像斑点进行分类,从而形成灰度图像的斑点,属于一类的那些二值图像斑点最终形成灰度图像的斑点,具体来说就是,灰度图像的斑点是由中心坐标间的距离小于阈值Tb的那些二值图像斑点所组成的,即这些二值图像斑点属于该灰度图像斑点;最后就是确定灰度图像斑点的信息——位置和尺寸。
位置是属于该灰度图像斑点的所有二值图像斑点中心坐标的加权和,即公式2,权值q等于该二值图像斑点的惯性率的平方,它的含义是二值图像的斑点的形状越接近圆形,越是我们所希望的斑点,因此对灰度图像斑点位置的贡献就越大。
尺寸则是属于该灰度图像斑点的所有二值图像斑点中面积大小居中的半径长度。
其中,H表示该斑点的凸壳面积在计算斑点的面积,中心处的坐标,尤其是惯性率时,都可以应用图像矩的方法。
下面我们就介绍该方法。
矩在统计学中被用来反映随机变量的分布情况,推广到力学中,它被用来描述空间物体的质量分布。
同样的道理,如果我们将图像的灰度值看作是一个二维的密度分布函数,那么矩方法即可用于图像处理领域。
设f(x,y)是一幅数字图像,则它的矩Mij为:下面给出SimpleBlobDetector的源码分析。
我们先来看看SimpleBlobDetector类的默认参数的设置:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片SimpleBlobDetector::Params::Params(){thresholdStep = 10; //二值化的阈值步长,即公式1的tminThreshold = 50; //二值化的起始阈值,即公式1的T1maxThreshold = 220; //二值化的终止阈值,即公式1的T2//重复的最小次数,只有属于灰度图像斑点的那些二值图像斑点数量大于该值时,该灰度图像斑点才被认为是特征点minRepeatability = 2;//最小的斑点距离,不同二值图像的斑点间距离小于该值时,被认为是同一个位置的斑点,否则是不同位置上的斑点minDistBetweenBlobs = 10;filterByColor = true; //斑点颜色的限制变量blobColor = 0; //表示只提取黑色斑点;如果该变量为255,表示只提取白色斑点filterByArea = true; //斑点面积的限制变量minArea = 25; //斑点的最小面积maxArea = 5000; //斑点的最大面积filterByCircularity = false; //斑点圆度的限制变量,默认是不限制minCircularity = 0.8f; //斑点的最小圆度//斑点的最大圆度,所能表示的float类型的最大值maxCircularity = std::numeric_limits<float>::max();filterByInertia = true; //斑点惯性率的限制变量//minInertiaRatio = 0.6;minInertiaRatio = 0.1f; //斑点的最小惯性率maxInertiaRatio = std::numeric_limits<float>::max(); //斑点的最大惯性率filterByConvexity = true; //斑点凸度的限制变量//minConvexity = 0.8;minConvexity = 0.95f; //斑点的最小凸度maxConvexity = std::numeric_limits<float>::max(); //斑点的最大凸度}我们再来介绍检测二值图像斑点的函数findBlobs。
Opencv2.4.9源码分析——MSCR前面我们介绍了MSER方法,但该方法不适用于对彩色图像的区域检测。
为此,Forssen于2007年提出了针对彩色图像的最大稳定极值区域的检测方法——MSCR(Maximally Stable Colour Regions)。
MSCR的检测方法是基于凝聚聚类(AgglomerativeClustering)算法,它把图像中的每个像素作为对象,通过某种相似度准则,依次逐层的进行合并形成簇,即先合并相似度大的对象,再合并相似度小的对象,直到满足某种终止条件为止。
这一过程在MSCR中被称为进化过程,即逐步合并图像中的像素,从而形成斑点区域。
MSCR中所使用的相似度准则是卡方距离(Chi-squared distance):其中,x和y分别为彩色图像中的两个不同像素,下标k表示不同的通道,例如红、绿、蓝三个颜色通道。
因此公式1是一种颜色相似度的度量。
MSCR通过邻域像素之间的颜色相似度来进行聚类合并,邻域关系可以是水平垂直间邻域,也可以是还包括对角线间邻域。
OpenCV使用的是水平垂直间邻域,即当前像素与其右侧像素通过公式1得到一个相似度值,再与其下面像素通过公式1得到另一个相似度值。
所以一般来说,每个像素都有两个相似度值,但图像的最右侧一列和最下面一行只有一个相似度值。
因此对于一个大小为L×M的彩色图像来说,一共有2×L×M-L-M个相似度值。
我们把这些相似度值放入一个列表中,由于该相似度是邻域之间的相似度,类似于求图像的边缘,所以该列表也称为边缘列表。
在凝聚聚类算法中,是需要逐层进行合并的。
在MSCR中合并的层次也称为进化步长,用t 来表示,t∈[0…T],根据经验值,T一般为200,即一共进行200步的进化过程。
在每一层,都对应一个不同的颜色相似度阈值dthr,在该层只选取那些颜色相似度小于该阈值的像素进行合并。
每一层的阈值是不同,并且随着t的增加,阈值也增加,因此达到了合并的区域面积逐步增加的目的。
(5条消息)Opencv2.4.9源码分析相机镜头所呈现出的景物要比人类的视觉系统所看到的景物要狭小得多,因此一幅图像不可能捕获到我们所看到的整个景物。
全景图像拼接给出了这个问题的解决办法,它是把图像间重叠部分拿出来拼接起来,从而得到一幅更大的图像。
这种算法也可以用于把一幅图像插入到另一幅图像中。
图1 图像拼接执行过程及方法要想完成图像拼接,所要用到的算法较多,Opencv把这些算法用一张图呈现了处理,如图1所示。
下面我们就详细图像拼接算法。
1、计算特征点1.1 原理图像拼接的第一步是计算图像的特征,并得到它们的描述符。
好的特征检测的算法有SIFT和SURF,但它们都受到专利的保护,使用上有一定的限制。
而ORB算法是一种很好的替代方法,它是利用FAST检测特征,并生成BRIEF描述符。
关于局部特征检测方面的内容,我前面的文章中有一些算法的介绍,在这里就不再做过多的讲解。
1.2 源码Opencv中应用的是SURF算法和ORB算法,详细的内容为:class detail::FeaturesFinder表示寻找图像特征的类:1.class CV_EXPORTS FeaturesFinder2.{3.public:4.virtual ~FeaturesFinder() {}5.//寻找给定图像的特征6.void operator ()(const Mat &image, ImageFeatures &features);7.void operator ()(const Mat &image, ImageFeatures &features, const std::vector<cv::Rect> &rois);8.virtual void collectGarbage() {} //释放已被分配、但还没有被使用的内存9.10.protected:11.//虚函数,根据用户所选取的特征类别,调用不同子类的find函数,目前只实现了SURF特征和ORB特征12.virtual void find(const Mat &image, ImageFeatures &features) = 0;13.};在FeaturesFinder类中,重载( )运算符主要的任务是调用find函数来检测图像特征。
Opencv2.4.9源码分析——SimpleBlobDetectorOpenCV中提供了SimpleBlobDetector的特征点检测方法,正如它的名称,该算法使用最简单的方式来检测斑点类的特征点。
下面我们就来分析一下该算法。
首先通过一系列连续的阈值把输入的灰度图像转换为一个二值图像的集合,阈值范围为[T1,T2],步长为t,则所有阈值为:T1,T1+t,T1+2t,T1+3t,……,T2 (1)第二步是利用Suzuki提出的算法通过检测每一幅二值图像的边界的方式提取出每一幅二值图像的连通区域,我们可以认为由边界所围成的不同的连通区域就是该二值图像的斑点;第三步是根据所有二值图像斑点的中心坐标对二值图像斑点进行分类,从而形成灰度图像的斑点,属于一类的那些二值图像斑点最终形成灰度图像的斑点,具体来说就是,灰度图像的斑点是由中心坐标间的距离小于阈值Tb的那些二值图像斑点所组成的,即这些二值图像斑点属于该灰度图像斑点;最后就是确定灰度图像斑点的信息——位置和尺寸。
位置是属于该灰度图像斑点的所有二值图像斑点中心坐标的加权和,即公式2,权值q等于该二值图像斑点的惯性率的平方,它的含义是二值图像的斑点的形状越接近圆形,越是我们所希望的斑点,因此对灰度图像斑点位置的贡献就越大。
尺寸则是属于该灰度图像斑点的所有二值图像斑点中面积大小居中的半径长度。
其中,H表示该斑点的凸壳面积在计算斑点的面积,中心处的坐标,尤其是惯性率时,都可以应用图像矩的方法。
下面我们就介绍该方法。
矩在统计学中被用来反映随机变量的分布情况,推广到力学中,它被用来描述空间物体的质量分布。
同样的道理,如果我们将图像的灰度值看作是一个二维的密度分布函数,那么矩方法即可用于图像处理领域。
设f(x,y)是一幅数字图像,则它的矩Mij为:下面给出SimpleBlobDetector的源码分析。
我们先来看看SimpleBlobDetector类的默认参数的设置:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片SimpleBlobDetector::Params::Params(){thresholdStep = 10; //二值化的阈值步长,即公式1的tminThreshold = 50; //二值化的起始阈值,即公式1的T1maxThreshold = 220; //二值化的终止阈值,即公式1的T2//重复的最小次数,只有属于灰度图像斑点的那些二值图像斑点数量大于该值时,该灰度图像斑点才被认为是特征点minRepeatability = 2;//最小的斑点距离,不同二值图像的斑点间距离小于该值时,被认为是同一个位置的斑点,否则是不同位置上的斑点minDistBetweenBlobs = 10;filterByColor = true; //斑点颜色的限制变量blobColor = 0; //表示只提取黑色斑点;如果该变量为255,表示只提取白色斑点filterByArea = true; //斑点面积的限制变量minArea = 25; //斑点的最小面积maxArea = 5000; //斑点的最大面积filterByCircularity = false; //斑点圆度的限制变量,默认是不限制minCircularity = 0.8f; //斑点的最小圆度//斑点的最大圆度,所能表示的float类型的最大值maxCircularity = std::numeric_limits<float>::max();filterByInertia = true; //斑点惯性率的限制变量//minInertiaRatio = 0.6;minInertiaRatio = 0.1f; //斑点的最小惯性率maxInertiaRatio = std::numeric_limits<float>::max(); //斑点的最大惯性率filterByConvexity = true; //斑点凸度的限制变量//minConvexity = 0.8;minConvexity = 0.95f; //斑点的最小凸度maxConvexity = std::numeric_limits<float>::max(); //斑点的最大凸度}我们再来介绍检测二值图像斑点的函数findBlobs。
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片//image为输入的灰度图像//binaryImage为二值图像//centers表示该二值图像的斑点void SimpleBlobDetector::findBlobs(const cv::Mat &image, const cv::Mat &binaryImage, vector<Center> ¢ers) const{(void)image;centers.clear(); //斑点变量清零vector < vector<Point> > contours; //定义二值图像的斑点的边界像素变量Mat tmpBinaryImage = binaryImage.clone(); //复制二值图像//调用findContours函数,找到当前二值图像的所有斑点的边界findContours(tmpBinaryImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);#ifdef DEBUG_BLOB_DETECTOR// Mat keypointsImage;// cvtColor( binaryImage, keypointsImage, CV_GRAY2RGB );//// Mat contoursImage;// cvtColor( binaryImage, contoursImage, CV_GRAY2RGB );// drawContours( contoursImage, contours, -1, Scalar(0,255,0) );// imshow("contours", contoursImage );#endif//遍历当前二值图像的所有斑点for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++){//结构类型Center代表着斑点,它包括斑点的中心位置,半径和权值Center center; //斑点变量//初始化斑点中心的置信度,也就是该斑点的权值center.confidence = 1;//调用moments函数,得到当前斑点的矩Moments moms = moments(Mat(contours[contourIdx]));if (params.filterByArea) //斑点面积的限制{double area = moms.m00; //零阶矩即为二值图像的面积//如果面积超出了设定的范围,则不再考虑该斑点if (area < params.minArea || area >= params.maxArea)continue;}if (params.filterByCircularity) //斑点圆度的限制{double area = moms.m00; //得到斑点的面积//计算斑点的周长double perimeter = arcLength(Mat(contours[contourIdx]), true);//由公式3得到斑点的圆度double ratio = 4 * CV_PI * area / (perimeter * perimeter);//如果圆度超出了设定的范围,则不再考虑该斑点if (ratio < params.minCircularity || ratio >= params.maxCircularity)continue;}if (params.filterByInertia) //斑点惯性率的限制{//计算公式13中最右侧等式中的开根号的值double denominator = sqrt(pow(2 * moms.mu11, 2) + pow(moms.mu20 - moms.mu02, 2));const double eps = 1e-2; //定义一个极小值double ratio;if (denominator > eps){//cosmin和sinmin用于计算图像协方差矩阵中较小的那个特征值λ2double cosmin = (moms.mu20 - moms.mu02) / denominator;double sinmin = 2 * moms.mu11 / denominator;//cosmin和sinmin用于计算图像协方差矩阵中较大的那个特征值λ1double cosmax = -cosmin;double sinmax = -sinmin;//imin为λ2乘以零阶中心矩μ00double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;//imax为λ1乘以零阶中心矩μ00double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;ratio = imin / imax; //得到斑点的惯性率}else{ratio = 1; //直接设置为1,即为圆}//如果惯性率超出了设定的范围,则不再考虑该斑点if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)continue;//斑点中心的权值定义为惯性率的平方center.confidence = ratio * ratio;}if (params.filterByConvexity) //斑点凸度的限制{vector < Point > hull; //定义凸壳变量//调用convexHull函数,得到该斑点的凸壳convexHull(Mat(contours[contourIdx]), hull);//分别得到斑点和凸壳的面积,contourArea函数本质上也是求图像的零阶矩double area = contourArea(Mat(contours[contourIdx]));double hullArea = contourArea(Mat(hull));double ratio = area / hullArea; //公式5,计算斑点的凸度//如果凸度超出了设定的范围,则不再考虑该斑点if (ratio < params.minConvexity || ratio >= params.maxConvexity)continue;}//根据公式7,计算斑点的质心center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);if (params.filterByColor) //斑点颜色的限制{//判断一下斑点的颜色是否正确if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)continue;}//compute blob radius{vector<double> dists; //定义距离队列//遍历该斑点边界上的所有像素for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++){Point2d pt = contours[contourIdx][pointIdx]; //得到边界像素坐标//计算该点坐标与斑点中心的距离,并放入距离队列中dists.push_back(norm(center.location - pt));}std::sort(dists.begin(), dists.end()); //距离队列排序//计算斑点的半径,它等于距离队列中中间两个距离的平均值center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;}centers.push_back(center); //把center变量压入centers队列中#ifdef DEBUG_BLOB_DETECTOR// circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );#endif}#ifdef DEBUG_BLOB_DETECTOR// imshow("bk", keypointsImage );// waitKey();#endif}最后介绍检测特征点的函数detectImpl。