聚类算法使用
原图
kmeans
官方文档:Clustering — OpenCV 3.0.0-dev documentation
查找聚类的中心,并对聚类周围的输入样本进行分组。
实现了一种k-means算法,该算法可找到cluster_count集群的中心,并在集群周围对输入样本进行分组。作为输出, 包含存储在样本矩阵行中的样本的基于0的聚类索引 。
double kmeans(InputArray data, int K, InputOutputArray bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray centers=noArray() )
- data –用于聚类的数据。需要具有浮点坐标的N维点数组。此数组的示例可以是:
- Mat points(count, 2, CV_32F);
- Mat points(count, 1, CV_32FC2);
- Mat points(1, count, CV_32FC2);
- std::vector<cv::Point2f> points(sampleCount);
- K –将集合分离的集群数。
- 标签 –输入/输出整数数组,用于存储每个样本的聚类索引。
- 标准criteria –算法终止标准,即最大迭代次数和/或所需精度。精度被指定为criteria.epsilon。一旦每个聚类中心在某个迭代上移动的距离小于criteria.epsilon,算法就会停止。
-
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1); //迭代停止条件 //---------- //迭代算法的终止准则 //---------- TermCriteria( int type, //type=TermCriteria::MAX_ITER/TermCriteria::COUNT 迭代到最大迭代次数终止 //type= TermCriteria::EPS 迭代到阈值终止 //type= TermCriteria::MAX_ITER+ TermCriteria::EPS 上述两者都作为迭代终止条件 int maxCount, //迭代的最大次数 double epsilon // 阈值(中心位移值) );
- attempts –标记以指定使用不同的初始标签执行算法的次数。该算法返回产生最佳紧凑性的标签。
函数返回计算为
每次尝试后。选择最佳(最小)值,然后函数返回相应的标签和紧密度值。基本上,只能使用函数的核心,将尝试次数设置为1,每次使用自定义算法初始化标签,将其与(标志 = KMEANS_USE_INITIAL_LABELS)标志一起传递,然后选择最佳(最紧凑)聚类。距离的度量,基于欧式距离
- flags –
可以采用以下值的标志:
- KMEANS_RANDOM_CENTERS在每次尝试中选择随机的初始中心。
- KMEANS_PP_CENTERS由Arthur和Vassilvitskii [Arthur2007] 使用kmeans ++中心初始化。【两者最大的区别就是K-means++会选取彼此分布更远的样本作为初始中心】
- KMEANS_USE_INITIAL_LABELS在第一次(可能也是唯一一次)尝试期间,请使用用户提供的标签,而不要从初始中心进行计算。对于第二次和其他尝试,请使用随机或半随机中心。使用 KMEANS _ * _ CENTERS 标志之一来指定确切的方法。
- 中心 –群集中心的输出矩阵,每个群集中心一行。
kmeans(points, clusterCount, labels, criteria, 3, KMEANS_PP_CENTERS, centers);
案例参考:OpenCV学习入门(三):kmeans原理及代码_计算机视觉life的博客-CSDN博客
代码参考
opencv的Kmeans聚类算法应用 - 一颗蘋果 - 博客园
OpenCV之图像分割(一)KMeans方法_数据聚类&图像分割_Jackie035的博客-CSDN博客
运行kmeans.cpp
它生成带有随机点的图像,然后分配一个随机数的群集中心,并使用kmeans将这些群集中心移动到其代表性位置
使用不同的距离度量:K均值聚类的理解和实现_qiao_lili的博客-CSDN博客_k均值聚类实验
算法改进比较:Kmeans、Kmeans++和KNN算法比较_loadstar_kun的博客-CSDN博客
使用了马氏距离【Mahalanobis距离是数据的协方差距离,比起欧式距离考虑了各种特性之间的关系,它是一种有效的计算两个未知样本集的相似度的方法】,结果划分不如欧式距离精细。
Kmeans算法的改进:K-means聚类算法的三种改进(K-means++,ISODATA,Kernel K-means)介绍与对比 - Yixuan-Xu - 博客园
K-means与EM的关系:
k-means是最简单的EM算法。EM算法(1):K-means 算法 - Ccien - 博客园
K-Means算法其实是GMM的EM解法在高斯分量协方差ϵI→0时的一个特例。EM算法(期望最大化)——从EM算法角度理解K-Means与GMM的区别_JpHu2014的博客-CSDN博客_em算法和kmeans区别
K-means算法对于要划分为k类的数据集先随机选取k个样本作为类别中心(初始均值向量),遍历每一个样本计算他们与类别中心点的距离,将其划分到距离最近的类别中心所在的簇中,更新每个簇的类别中心,选取与当前簇内样本距离最小的新均值向量,再次遍历计算,直到中心不再变更,即划分结果稳定。
对于Kmeans算法影响较大的两个因素是距离度量以及初始类别中心的选取。
常用的距离度量是欧氏距离和马氏距离。在opencv提供的K-means函数中,传入原始数据样本集则使用欧氏距离,如果使用马氏距离,就要先对原始样本集计算协方差距离、进行尺度变化,再将计算结果传入函数。
传统的K-means算法是随机选取初始类别中心的,与K-means函数的KMEANS_RANDOM_CENTERS选项对应,而KMEANS_PP_CENTERS则使用改进的K-means++算法选取,两者最大的区别就是K-means++会选取彼此分布更远的样本作为初始类别中心。最后还有KMEANS_USE_INITIAL_LABELS选项由人工指定初始类别中心。
与K-means相比,EM(期望最大化)算法在聚类场景的应用不再以距离作为度量,而是计算概率。具体来说,每一个类都是一个高斯分布,要计算每个样本属于每个类的概率,形成单个样本-多个类别的概率分布(这也是EM的软聚类特性,并不严格要求某个样本必须属于一个类),推断出最优类别。【E】根据E步计算出的每个样本属于其最优类的概率(权重)估计该类的均值和方差,做最大似然估计,即找到生成这些给定的样本概率最大的概率分布,更新类的高斯分布。【M步】EM两步交替进行,直到收敛。K-means可以看作不考虑权重的硬聚类特例。计算距离对应E步,更新中心对应M步。
EM运行时间更长,结果差别真的不大。
EM
期望最大化(EM)算法估计具有指定数量的混合物的高斯混合分布形式的多元概率密度函数的参数。
该算法找到最大似然估计的所有混合物的参数(MLE)
bool cv::ml::EM::trainEM | ( | InputArray | samples, |
OutputArray | logLikelihoods = noArray() , | ||
OutputArray | labels = noArray() , | ||
OutputArray | probs = noArray() | ||
) |
从样本集中估计高斯混合参数。
此变化从“期望”步骤开始。模型参数的初始值将通过k-means算法估算。
与许多ML模型不同,EM是一种无监督的学习算法,它不把response(类标签或函数值)作为输入。相反,它从输入样本集中计算高斯混合参数的最大似然估计,并将所有参数存储在结构内部:probs、means、covs [k]、weights,(可选)计算每个样本的输出“类别标签”
像其他分类器一样,训练后的模型可以进一步用于预测。
sample样本 | 从中估计高斯混合模型的样本。它应该是一个单通道矩阵,其每一行都是一个样本。如果矩阵不具有CV_64F类型,它将被转换为这种类型的内部矩阵以进行进一步计算。 |
logLikelihoods对数似然 | 可选的输出矩阵,其中包含每个样本的似然对数值 |
labels标签 | 每个样本的可选输出“类标签” |
probs概率 | 可选的输出矩阵,包含给定每个样本的每个高斯混合分量的后验概率。 |
trainE是从期望步骤开始需要提供均值或者是权重,trainM是从最大化步骤开始输入初始概率。
笔记参考:OpenCV4.1.1版本的EM算法实现_chenying66的博客-CSDN博客
官方文档:OpenCV: cv::ml::EM Class Reference
代码参考 opencv3中的机器学习算法之:EM算法 - denny402 - 博客园
em.cpp
Grabcut
Grab Cut是对Graph Cut的改进,与Graph Cut相比有三方面的变化:
- 目标和背景由灰度直方图变为RGB三通道的混合高斯模型GMM;
- 分割过程由一次评估变为多次迭代;
- 种子点由目标和背景变为只需要背景区域的像素集,允许不完全的标注。
该程序演示了GrabCut分割-在区域中选择一个对象,然后抓取将尝试将其分割出来。
调用:./grabcut <image_name>
在要分割的对象周围选择一个矩形区域
热键:ESC-退出程序
r-恢复原始图像
n-下一次迭代
鼠标左键-设置矩形
CTRL +鼠标左键-设置GC_BGD像素(背景)
SHIFT +鼠标左键-设置GC_FGD像素(前景)
CTRL +鼠标右键-设置GC_PR_BGD像素(可能的背)
SHIFT +鼠标右键-设置GC_PR_FGD像素(可能的前)
标记好了就按n
不错的教程:基础学习笔记之opencv(16):grabcut使用例程 - tornadomeet - 博客园
dnn/segmentation.cpp
注意把common.hpp也包含进工程
Consider using _dupenv_s instead. 出现这种错误的解决办法:
项目 ->属性 -> c/c++ -> 预处理器 -> 点击预处理器定义,编辑,加入_CRT_SECURE_NO_WARNINGS,即可。
参考:opencv dnn模块 示例(8) 语义分割 segmentation(ENet/fcn8s)_wanggao_1990的博客-CSDN博客_opencv语义分割
https://www.cnblogs.com/jsxyhelu/p/10796460.html
工程的使用是要在命令行中操作的,或者在程序中写明路径
在opencv自带的models.yml文件中说明了运用的模型以及参数。
width 和 height 用于指定blob(二进制大对象,一般是图片或者音频)的大小
mean subtraction for each blue, green and red channels ,类型是标量,在有些分类模型中会用到,这里设置的是0
//! [Create a 4D blob from a frame]
blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight), mean, swapRB, false);
命令行如下:
seg.exe --model=model-cityscapes.net --classes=categories.txt --input=city1.jpg【1.MP4】 --mean="0 0 0" --width=512 --height=256 --rgb=true --scale=0.00392
分割模型model-cityscapes.net 由cityscape数据集训练生成的模型(这个大型数据集包含来自50个不同城市的街道场景中记录的多种立体视频序列)
类别目录,就是训练的20个类别
Unlabeled
Road
Sidewalk
Building
Wall
Fence
Pole
TrafficLight
TrafficSign
Vegetation
Terrain
Sky
Person
Rider
Car
Truck
Bus
Train
Motorcycle
Bicycle
运行后会输出对应的颜色(太长没有截全图,最后还有一个bicycle)
也能够运行视频检测
关于图片,之前有把png直接重命名为jpg(实质上图片还是png格式的,具备透明性(transparency)使图像中某些部分不显示出来,带有α通道)然后输入检测,会发生错误:
getLayerShapesRecursively input[0] = [ 1 4 256 512 ]
OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(4.2.0) Error: Unspecified error (Number of input channels should be multiple of 3 but got 4) in cv::dnn::ConvolutionLayerImpl::getMemoryShapes
如提示所表述的,图片被分解为四个通道,与预期的三个通道不符合,运行失败。
20200609再来补充一个object_detection.cpp的例子
参考教程:https://www.cnblogs.com/jsxyhelu/p/10792731.html
主要是参数的配置问题
为了方便我就放在了和exe同一目录下
objectdetection.exe --config=yolov3-tiny.cfg --model=yolov3-tiny.weights --classes=class.txt --width=416 --height=416 --scale=0.00392 --input=city1.jpg --rgb
yolov3的模型可以去官网(darknet)下载,我这里使用的是tiny 类别和分割用的是一个
有效果,但感觉电脑带不动。。。看看这指狗为羊,指车为鸟。。。