Opencv之图像分割 --- KMeans方法_数据聚类&图像分割

2023-11-04

一、 KMeans方法概述

1. 无监督学习方法(不需要人为的干预)

2. 分类问题,输入分类数目,初始化中心位置

3. 硬分类方法,以距离度量
(    硬分类:以距离为度量,距离离哪个中心点越近,他就被标记为哪个分类的编号;
    以距离度量:计算两个点之间的距离,如平面上x,y;空间上x,y,z;对RGB图像来说就是R、G、B三个通道,每两个像素点之间的差值
 )

4. 迭代分类为聚类

二、基本流程

1. 根据输入的分类数目定义K个分类,每个分类选择一个中心点

2. 对DS(Data Set)中每个数据点做如下操作:

        -计算它与K个中心点之间的距离

        -把数据点指定属于K个中心点中距离最近的中心点所属的分类

3. 对K个分类中每个数据点计算平均值得到新的K个中心点

4. 比较新K个中心点之间与第一步中已经存在的K个中心差值

        -当两者之间的差值没有变化或者小于指定阈值,或者迭代次数小于指定的次数,就结束分类

        -当两者之间的差值或者条件不满足时候,用新计算的中心点值做为K个分类的新中心点,继续执行2~4步。直到条件满足退出。

        从数学的角度来说KMeans就是要找到K个分类而且他们的中心点到各个分类中各个数据的之间差值平方和最小化,而实现这个过程就是要通过上述2~4步不断的迭代执行,直到收敛为止。公式表示如下:

 图解:

 

 

注意:
1. 初始的K个分类中每个分类的中心点选择,多数的算法实现都是支持随机选择与人工指定两种方式,OpenCV中的KMeans实现同样支持这两种方式。

2. 多维数据支持,多数时候我们要分类的特征对象的描述数据不止一个数据特征,而是一个特征向量来表示,OpenCV中通过Mat对象构建实现对多维数据KMeans分类支持。

3. 收敛条件 - 一般情况下在达到指定的迭代次数或者两次RSS差值小于给定阈值的情况下,结束执行分类处理,输出最终分类结果。

三、Opencv中相关API

double kmeans( InputArray data,	// 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);

					int K, //用来分割集合的集群数。常见的K = 2
					InputOutputArray bestLabels, //输入、输出整数数组,用于存储每个样本的聚类索引。
					TermCriteria criteria,  //算法终止标准,即最大迭代次数和/或所需精度。精度被指定为criteria.epsilon。
											一旦每个聚类中心在某个迭代上移动的距离小于criteria.epsilon,该算法就会停止。
					int attempts, //用于指定使用不同的初始标签执行算法的次数的标志。一般设定为2-3次,
								  该算法返回产生最佳紧凑性的标签(请参见最后一个功能参数)。
					int flags,  //可以采用以下值的标志
    							 KMEANS_RANDOM_CENTERS - 在每次尝试中选择随机的初始中心。
    						     KMEANS_PP_CENTERS - 使用Arthur和Vassilvitskii进行的kmeans ++中心初始化。
      						  KMEANS_USE_INITIAL_LABELS - 在第一次(可能也是唯一的)尝试期间,请使用用户提供的标签,而不要从初始中心进行
					OutputArray centers=noArray()
					)

 对矩阵Mat填充随机数:

void cv::RNG::fill( 
		InputOutputArray mat,
		int      distType, // 类型为RNG::UNIFORM,则表示产生均匀分布的随机数,如果为RNG::NORMAL则表示产生高斯分布的随机数
		InputArray      a, // 如果随机数产生模型为均匀分布,则参数a表示均匀分布的下限,参数b表示上限。
		InputArray      b, // 如果随机数产生模型为高斯模型,则参数a表示均值,参数b表示方差。
		bool   saturateRange = false // 只有当随机数产生方式为均匀分布时才有效,表示的是是否产生的数据要布满整个范围
	)

将原数组(矩阵)打乱 :

randShuffle(			
			InputOutputArray dst,    // 输入输出数组(一维)
			double iterFactor=1. ,   // 表示随机交换元素的数量的缩放因子,总的交换次数dst.rows*dst.cols*iterFactor
			RNG* rng=0               //(可选)随机数产生器,0表示使用默认的随机数产生器,即seed=-1。
									rng决定了打乱的方法
	)
	

 四、代码展示

1. 数据分类

代码:

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	Mat img(500, 500, CV_8UC3);
	RNG rng(12345);

	//五个颜色,聚类之后的颜色随机从这里面选择
	Scalar colorTab[] = {
		Scalar(0,0,255),
		Scalar(0,255,0),
		Scalar(255,0,0),
		Scalar(0,255,255), //红+绿 == 黄
		Scalar(255,0,255)  //蓝+红 == 粉
	};

	int numCluster = rng.uniform(2, 10);  //随机分类总数
	cout << "number of clusters:" << numCluster << endl;

	int sampleCount = rng.uniform(2, 1000); //随机生成样本总数

	//样本矩阵为points
	Mat points(sampleCount, 1, CV_32FC2);  //创建samplecount行1列的矩阵,因为待分类的数据只有一种类型,一维的,所以用1列 
											//双通道是存储坐标(x,y),其实就是元素类型为point2f的数据

	Mat labels; //存放表示每个簇的标签,是一个整数,从0开始的索引整数
				//行数跟points一样,每一行中储存对于Points行数中的数据是属于哪一种的分类

	Mat centers;//存放的是kmeans算法结束后每个簇的中心位置

	//随机生成样本
	//样本总数为sampleCount, 类别总数为numCluster
	//样本矩阵为points,先按类别分成numCluster份 每一份数据的个数为sampleCount / numCluster
	//然后按类别随机矩阵填充样本矩阵,第一类填充矩阵的0~sampleCount/numCluster行
	//第二类填充样本矩阵的sampleCount/numCluster~(sample_count/cluster_count)*2 行,
	//以此类推直到填充满所有矩阵,每一类的样本个数是一样的
	for (int k = 0; k < numCluster; k++)
	{
		//随机出中心点
		Point center;
		center.x = rng.uniform(0, img.cols);
		center.y = rng.uniform(0, img.rows);


		// Mat.rowRange 函数从 Mat 中抽取 startrow行 到 endrow 行的数据并返回(类型与mat一致),索取的位置是左闭右开,不包含endrow
		// Mat.rowRange 的返回值只是浅拷贝,指针指向还是原Mat,所以下面的 rng.fill 是对 points 的填充

		//返回数组在一定跨度的行
		//points为输入数组,pointChunk返回数组
		//开始行为 k*sampleCount/numCluster
		//结束行为 当k== numCluster - 1时 为 第sampleCount行 否则为(k + 1) * sampleCount / numCluster
		
		//得到不同小块
		Mat pointChunk = points.rowRange(k * sampleCount / numCluster, 
			k == numCluster - 1 ? sampleCount : (k + 1) * sampleCount / numCluster);
		
		//用随机数对小块点进行填充
		//在center坐标周围上下左右 img.cols*0.05, img.rows*0.05 的方差内生成高斯分布的随机数,最后赋值给pointChunk
		rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
	}
	
	//打乱points中的顺序
	randShuffle(points, 1, &rng);


	//使用KMeans
	kmeans(points, numCluster, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1), 3,
		KMEANS_PP_CENTERS, centers); 
		//TermCriteria类是用来作为迭代算法的终止条件的,参数:类型(EPS表示迭代到阈值终止),第二个参数为迭代的最大次数,最后一个是特定的阈值

	//用不同颜色显示分类
	img = Scalar::all(255); //初始化
	for (int i = 0; i < sampleCount; i++)
	{
		int index = labels.at<int>(i);
		Point p = points.at<Point2f>(i);
		circle(img, p, 2, colorTab[index], -1, 8);
	}

	//每个聚类的中心来绘制圆
	for (int i = 0; i < centers.rows; i++)
	{
		int x = centers.at<float>(i, 0);
		int y = centers.at<float>(i, 1);

		cout << "c.x = " << x << "c.y = " << y << endl;
		circle(img, Point(x, y), 40, colorTab[i], 1, LINE_AA);
	}

	imshow("KMeans-Data-Demo:", img);

	waitKey(0);
	destroyAllWindows();
	return 0;
	 
}

 结果:

2. 图像分割

代码:

#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	Mat src = imread("E:/技能学习/opencv图像分割/test.jpg");

	if (src.empty())
	{
		cout << "could not load image !" << endl;
		return -1;
	}

	namedWindow("input image", WINDOW_AUTOSIZE);
	imshow("input image",src);

	Scalar colorTab[] = {
		Scalar(0,0,255),
		Scalar(0,255,0),
		Scalar(255,0,0),
		Scalar(0,255,255),
		Scalar(255,0,255),
	};

	//获取图像的宽、高和通道数
	int width = src.cols;
	int height = src.rows;
	int dims = src.channels();

	//初始化定义
	int sampleCount = width * height; //样本总数
	int clusterCount = 4; //类别
	Mat points(sampleCount, dims, CV_32F, Scalar(10)); //输入数组
	Mat labels; //存放表示每个簇的标签
	Mat centers(clusterCount,1,points.type()); //存放的是kmeans算法结束后每个簇的中心位置

	//RGB数据转换到样本数据
	int index = 0;
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			index = row * width + col;
			Vec3b bgr = src.at<Vec3b>(row, col);
			points.at<float>(index, 0) = static_cast<int>(bgr[0]);
			points.at<float>(index, 1) = static_cast<int>(bgr[1]);
			points.at<float>(index, 2) = static_cast<int>(bgr[2]);
		} 
	}

	//运行KMeans
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	kmeans(points, clusterCount, labels, criteria, 3, KMEANS_PP_CENTERS, centers);

	//显示图像分割结果
	Mat result = Mat::zeros(src.size(), src.type());
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			index = row * width + col;
			int label = labels.at<int>(index);
			result.at<Vec3b>(row, col)[0] = colorTab[label][0]; //对结果图中的每一个通道进行赋值
			result.at<Vec3b>(row, col)[1] = colorTab[label][1];
			result.at<Vec3b>(row, col)[2] = colorTab[label][2];
		}
	}

	//显示中心点位置
	for (int i = 0; i < centers.rows; i++)
	{
		int x = centers.at<float>(i, 0);
		int y = centers.at<float>(i, 1);
		cout << "c.x = " << x << " , "<< "c.y = " << y << endl;
		
	}

	imshow("KMeans Image Segmentation Demo", result);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

结果:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Opencv之图像分割 --- KMeans方法_数据聚类&图像分割 的相关文章

  • 旋转矩阵openCV

    我想知道如何找到框架中一组特征的旋转矩阵 我会更具体 我有 2 个具有 20 个特征的帧 假设第 1 帧和第 2 帧 我可以估计两个帧中特征的位置 例如 假设位置 x y 处的某个第 1 帧特征 并且我确切地知道它在哪里 所以假设为 x y
  • OpenCV Python 和 SIFT 功能

    我知道有很多关于Python and OpenCV但我没有找到有关这个特殊主题的帮助 我想提取SIFT关键点来自 python OpenCV 中的图像 我最近安装了 OpenCV 2 3 可以访问 SURF 和 MSER 但不能访问 SIF
  • 将图像加载到现有 Mat 中

    有没有办法将图像加载到现有的 Mat 中 如果没有 有没有办法控制 OpenCV 在调用 cv imread 时分配内存的位置 我只是为您的类创建一个构造函数 该构造函数接受 imread 的输入参数并将图像直接加载 并分配 到您的类中 所
  • 如何用OpenCV解决图像处理相机IO延迟

    我有一个 OpenCV 程序 其工作原理如下 VideoCapture cap 0 Mat frame while true cap gt gt frame myprocess frame 问题是如果myprocess耗时较长 超过相机的I
  • 如何选择图像插值方法? (Emgu/OpenCV)

    Emgu OpenCV的 net包装器 提供的图像调整大小功能可以使用四种插值方法中的任意一种 http www emgu com wiki files 1 4 0 0 html 596dd03d 301e d3c6 4c53 c42855
  • 在 opencv 中一次性将旋转和平移结合起来

    我有一段用于旋转和平移图像的代码 Point2f pt 0 in rows double angle atan trans c trans b 180 M PI Mat r getRotationMatrix2D pt angle 1 0
  • OpenCV Sobel 滤波器 - 为什么它看起来这么糟糕,尤其是与 Gimp 相比?

    我正在尝试使用 OpenCV 重建一些我之前在 Gimp 中完成的预处理 第一级是用于边缘检测的 Sobel 滤波器 它在 Gimp 中运行得很好 现在这是我对 OpenCV 的尝试 opencv imgproc Sobel src sca
  • 将 CvSeq 保存到数组

    我对 OpenCV 文档有点迷失 我想将 cvFindContours 返回的 CvSeq 保存到一个数组中 据我了解它将返回 CvContour 的 seq 但我找不到它包含的内容 我应该保存其中的哪些部分 稍后我可以迭代它并说调用 cv
  • 在openCV内部调用Gstreamer

    我需要在 openCV 代码中调用 Gstremaer 本质上是打开摄像机 当我查看源代码时 modules highgui src cap gstreamer cpp似乎是我正在寻找的文件 我用 Gstreamer 标志编译了 OpenC
  • CV_MAT_ELEM 中的编译错误

    调用estimateRigidTransform 的结果是我得到一个名为 trans 的cv Mat 对象 为了检索其包含的矩阵 我尝试以这种方式访问 其元素 for i 0 i lt 2 i for j 0 j lt 3 j mtx j
  • 从基本矩阵中查找单应矩阵

    我正在尝试计算单应性矩阵H给定一组对应关系和基本矩阵F 根据对极几何原理 我知道这可以通过对极线和对极线的叉积来完成F from 极点几何 http www cs unc edu marc tutorial node44 html e ij
  • opencv不失真图像有一个奇怪的圆圈

    我尝试使用 opencv 针孔模型来计算校准参数 然后使图像不失真 问题是 未失真的图像中有一个奇怪的圆圈 如下所示 代码 原始图像和结果图像是here https github com wennycooper A004 pinhole 任
  • OpenCV InRange 参数

    我在 Android 上使用 OpenCV 来实时查找特定颜色的圆圈 我的第一步是仅保留与我正在寻找的定义颜色相对应的像素 在本例中为红色或绿色 示例图像 https i stack imgur com CIozU jpg 为此 我正在使用
  • IplImage 内的 IplImage

    是否可以使用 OpenCv JavaCv 将图像放置在图像内 例如我有一个 1000x1000 图像和一个 100x100 图像 在 600x600 的位置 我想将较小的图像放置在较大的图像内 假设蓝色框是 1000x1000 IplIma
  • 使用 Xcode 为 OS X Lion / Mountain Lion 编译 OpenCV (2.3.1+)

    谁能给我提供一些如何使用 Xcode 在 OS X Lion 上编译 OpenCV 2 3 1 的详细指南 我对此很生气 我得到了源代码 使用 cmake 创建 Xcode 模板并尝试构建它 但它失败并出现大约 200 个错误 提前致谢 多
  • 如何使用 Python 3 在 OpenCV 3 上正确加载 cv2.KeyPoint 和描述符?

    有一天 我不得不恢复一个使用 OpenCV 3 和 Python 2 7 的旧项目 在此代码中 要加载 cv2 KeyPoint 我执行以下操作 import numpy as np import cPickle import cv2 ke
  • caffe安装:opencv libpng16.so.16链接问题

    我正在尝试在 Ubuntu 14 04 机器上使用 python 接口编译 caffe 我已经安装了 Anaconda 和 opencvconda install opencv 我还安装了咖啡中规定的所有要求 并更改了注释块makefile
  • OpenCV非旋转图像拼接

    我正在 OpenCV 中进行图像拼接 从不同位置拍摄平面场景的照片并尝试构图全景图 我修改了缝合示例以满足我的需要 openCV 拼接管道的问题是 它假设相机纯粹旋转 但对我来说情况并非如此 当拍摄的照片与场景完全正交时 没有相机旋转 只是
  • 跟踪白色背景中的白球(Python/OpenCV)

    我在 Python 3 中使用 OpenCV 来检测白场上的白 黑球 并给出它的精确 x y 半径 和颜色 我使用函数 cv2 Canny 和 cv2 findContours 来找到它 但问题是 cv2 Canny 并不总是检测到圆的完整
  • 有没有办法检测图像是否模糊? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想知道是否有一种方法可以通过分析图像数据来确定图像是否模糊 估计图像清晰度的另一种非常简单的方法是使用拉普拉斯 或 LoG 滤波器并

随机推荐

  • C++学习笔记3:静态与名字空间

    why静态 静态数据成员 静态成员函数 静态对象 类作用域及对象的生存期 命名空间 whiy静态 在函数体内定义一个变量时 每次函数调用时编译器会为这些内部变量分配内存 如果这个变量有一个初始化表达式 那么每当程序运行到初始化表达式中 变量
  • 预测概率一直不变/损失和精确度一直不变

    预测概率一直不变 损失和精确度一直不变 这个问题困扰了好久 今天2022 03 01找到了解决办法 原理未懂 参考链接 https blog csdn net weixin 39614561 article details 11076301
  • MyBatis3 传递参数种类

    第一种 单个参数 基本类型 Mapper 接口定义 T selectByPrimaryKey String sid Mapper 映射文件
  • Java 多线程编程基础(详细)

    Java多线程编程基础 1 进程与线程 2 多线程实现 2 1 Thread类实现多线程 2 2 Runnable接口实现多线程 2 3 Callable接口实现多线程 2 3 多线程运行状态 3 多线程常用操作方法 3 1 线程的命名和获
  • SerializeField和Serializable

    Serialize功能 Unity3D 中提供了非常方便的功能可以帮助用户将 成员变量 在Inspector中显示 并且定义Serialize关系 简单的说 在没有自定义Inspector的情况下所有显示在Inspector 中的属性都同时
  • TortoiseGit(小乌龟)记住登录的账户

    使用小乌龟有时候需要使用http的方式添加代码 每次都需要填写账号密码 只需要添加两行配置即可记住账号密码 代码如下 credential helper store
  • 值传递与引用传递详解

    1 关于值传递 值传递 是指在调用函数时 将实际参数复制一份传递到函数中 这样在函数中如果对参数进行修改 就不会影响到实际参数 如下图所示 当传递参数之前会将参数进行复制 函数中修改了参数 不会影响实际参数 值传递是对于是对基本数据而言 例
  • RDTSC指令介绍与使用

    一 了解RDTSC指令 rdtsc指令 该指令返回CPU自启动以来的时钟周期数 该时钟周期数 即处理器的时间戳 在CPU通电启动后 首先会重置EDX和EAX 在每个时钟周期上升或下降沿到来时 会自动累计周期数 并被记录到EDX和EAX寄存器
  • 深度学习笔记(六) cnn 卷积神经网络

    1 卷积神经网络是一种前馈神经网络 前面讲的网络包括full NN 感知器都是前馈网络 BP是一种前馈网络的训练方法 2 卷积神经网络主要是降维 有点类似PBA CNN的基本结构包括两层 其一为特征提取层 每个神经元的输入与前一层的局部接受
  • CMOS门电路详解

    MOS管的开关特性 在CMOS集成电路中 以金属 氧化物 半导体场效应晶体管 Metal Oxide Semiconductor 简称MOS 作为开关器件 MOS管的结构和工作原理 MOS管的输入 输出特性 对于共源极接法电路 闪击和衬底之
  • 【ACM】9. 回文数

    package com LeetCode ACM import java util public class PalindromeNumber public static void main
  • Spring+SpringMVC+mybatis+Quartz整合

    Quartz与SpringMVC的整合 简介 Quartz是一个完全由java编写的开源作业调度框架 为在Java应用程序中进行作业调度提供了简单却强大的机制 Quartz允许开发人员根据时间间隔来调度作业 它实现了作业和触发器的多对多的关
  • 高并发、大用户量的服务器架构方案

    http hi baidu com qiaobinbin item 604261dbd7d5eef092a97442 一 前言 二 编译安装 三 安装MySQL memcache 四 安装Apache PHP eAccelerator ph
  • 使用 json_in_java

    java in json Table of Contents 1 Java 使用 Json 1 1 下载地址 1 2 构造 json 字符串 1 3 解析 json 字符串 1 4 进一步使用 查看文档 1 Java 使用 Json 1 1
  • Linux网络编程socket错误分析

    转自 http aigo iteye com blog 1911134 socket错误码 EINTR 4 阻塞的操作被取消阻塞的调用打断 如设置了发送接收超时 就会遇到这种错误 只能针对阻塞模式的socket 读 写阻塞的socket时
  • Android开发过程中遇到的错误汇总及其解决方法

    2016 11 24 16 31 22 问题一 Cannot solve Android Error Execution failed for task app compileDebugJavaWithJavac 解决方法一 http st
  • es6简单介绍

    一 ECMAScript6简介 1 ES6是JavaScript的最新版本 ES6是2015年发布的新一版本的JavaScript 又称为 ES2015 2 ES6与ES5之间的关系 ES6是ES5 的语法糖 ES6的所有功能 使用ES5都
  • 华为OD机试真题-最小传输时延-2023年OD统一考试(B卷)

    题目描述 某通信网络中有N个网络结点 用1到N进行标识 网络通过一个有向无环图表示 其中图的边的值表示结点之间的消息传递时延 现给定相连节点之间的时延列表times i u v w 其中u表示源结点 v表示目的结点 w表示u和v之间的消息传
  • 扫雷游戏(优化版)

    目录 实现扫雷游戏 第一步 具体操作如下 第二步 test c 源文件 第三步 game h 游戏函数声明 第四步 game c 游戏实现 首先 其次 然后 最后 如下为game c的全部实现集合 以上为扫雷基础实现 可能有遗漏操作 敬请谅
  • Opencv之图像分割 --- KMeans方法_数据聚类&图像分割

    一 KMeans方法概述 1 无监督学习方法 不需要人为的干预 2 分类问题 输入分类数目 初始化中心位置 3 硬分类方法 以距离度量 硬分类 以距离为度量 距离离哪个中心点越近 他就被标记为哪个分类的编号 以距离度量 计算两个点之间的距离