opencv_c++学习(二十九)

一、监督学习的聚类方法

K近邻的方法:
在这里插入图片描述
首先给出一个阈值k,后寻找距离黑色圆点最近的k个元素,占据比例大的元素即为黑色所属的类别。如图所示,即k=3时,黑色圆属于三角形,k=5时圆点属于正方形。
支持向量机方法:
在这里插入图片描述

virtual bool cv::mliStatModel::train ( const Ptr< TrainData > &trainData, int flags = 0)

trainData:训练时使用的数据,数据为Ptr类型。
flags:构建模型方法标志,例如UPDATE_MODEL表示对使用新数据对模型进行更新。

virtual float cw::mll::StatModal::predict ( llnputArray samiples, results =, OutputArray inolarrayl, int flags =0, const )

samplcs:输入数据矩阵,矩阵数据类型必须是cV_32。results:对输入数据预测结果的输出矩阵。
flags:模型方法标志,取决于具体模型。
创建训练数据的函数:

static Ptr<TrainData>cv::ml::TrainData::create ( InputArray samples, int layout, InputArray responses, InputArray varldx = noArray(), InputArray sampleldx = noArray(), sampleWelights=,
InputArray noArray(),InputArray varType  moArray())

samples:样本数据矩阵,数据类型必须是CV_32F。
layout:样本数据排列方式的标志,ROW_SAMPLE(简记为0)表示每个样本数据为行排列,COL_SAMPLE(简记为1)表示每个样本数据为列排列。
responses:标签矩阵,如果是标量,存储为单行或者单列的矩阵,矩阵数据类型为CV_32F或CV_32S
varldx:用于指定哪些变量用于训练的向量,数据类型为CV_32S。
sampleldx:用于指定哪些样本用于训练的向量,数据类型为CV_32S。sampleWeights:每个样本数据权重向量,数据类型必须是CV_32F。
varType:声明变量类型的标志,VAR_ORDERED表示有序变量,VAR_CATEGORICAL表示分类变量。
创建K近邻方法:

static Ptr<KNearest> cv:umlKNearest:create( )

K近邻方法实现:

virtual float cv::ml::KNearest::findNearest ( InputArray samples, int k outputArray results, neighborResponses =, OutputArray noArray(), OutputArray dist = noArray(), const)

samples:待根据K近邻算法预测的数据,数据需要行排列且数据类型为CV_32F。
k:最近K近邻的数目。
results:每个新数据的预测结果,数据类型为CV_32F。
ncighborResponses:可以选择输出的每个数据最近邻的k个样本。
dist:可以选择输出的与k个最近邻样本的距离。
本节的应用案例如下:

using namespace cv::ml;

int main() {
    
    

	//读取图片
	Mat src = imread("1.jpg");
	Mat gray;
	if (src.empty())
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	cvtColor(src, gray, COLOR_BGR2GRAY);

	//将图像分割为5000个cells
	Mat images = Mat::zeros(5000, 400, CV_8UC1);
	Mat labels = Mat::zeros(5000, 1, CV_8UC1);

	int index = 0;
	Rect numberImg;
	numberImg.x = 0;
	numberImg.height = 1;
	numberImg.width = 400;
	for (int row = 0; row < 50; row++)
	{
    
    
		//从图像中分割出20*20的图像作为独立数字图像
		int label = row / 5;
		int datay = row * 20;
		for (int col = 0; col < 100; col++)
		{
    
    
			int datax = col * 20;
			Mat number = Mat::zeros(Size(20, 20), CV_8UC1);
			for (int x = 0; x < 20; x++)
			{
    
    
				for (int y = 0; y < 20; y++)
				{
    
    
					number.at<uchar>(x, y) = gray.at<uchar>(x + datay, y + datax);
				}
			}

			//将二维图像转化为行数据
			Mat row = number.reshape(1, 1);
			numberImg.y = index;

			//添加到总数据中
			row.copyTo(images(numberImg));

			//记录每个图像对应的数字标签
			labels.at<uchar>(index, 0) = label;
			index++;				 
		}
	}

	imwrite("结果:", images);
	imwrite("标签:", labels);

	//加载训练数据集
	images.convertTo(images, CV_32FC1); 
	labels.convertTo(labels, CV_32SC1);

	Ptr<ml::TrainData> tdata = ml::TrainData::create(images, ml::ROW_SAMPLE, labels);
	// 创建区近邻类

	Ptr<KNearest> knn = KNearest::create();

	// 每个类别拿出5个数据
	knn->setDefaultK(5); 
	
	// 进行分类
	knn->setIsClassifier(true); 

	//开始训练
	knn->train(tdata);

	//保存训练结果
	knn->save("knn_model.yml");

	waitKey(0);
	return true;
}

其中输入的数据样例为:
在这里插入图片描述
该图片共有5000个数字,每个数字为20*20像素大小,因此需要分割处理。

读取已经保存的模型,并进行预测:

int main() {
    
    

	//加载KNN分类器
	Mat data = imread("所有数据按行排列结果.png", IMREAD_ANYDEPTH);
	Mat labels = imread("标签.png", IMREAD_ANYDEPTH);
	data.convertTo(data, CV_32FC1);
	labels.convertTo(labels, CV_32SC1);
	Ptr<KNearest> knn = Algorithm::load<KNearest>("knn_madel.yml");

	//查看分类结果
	Mat result;
	knn->findNearest(data, 5, result);
	// 统计分类结果与真实结果相同的数目
	int count = 0;
	for (int row = 0; row < result.rows; row++)
	{
    
    
		int predict = result.at<float>(row, 0); 
		if (labels.at<int>(row, 0) == predict)
		{
    
    
			count = count + 1;
		}
	}
	float rate = 1.0*count / result.rows;

	cout << "分类的正确性:" << rate << endl;

	//测试新图像是否能够识别数字
	Mat testImgl = imread("handrite01.png",IMREAD_GRAYSCALE);
	Mat testImg2 = imread("handrite02.png",IMREAD_GRAYSCALE);
	imshow("testIng1", testImgl) ;
	imshow("testImng2",testImg2);

	//缩放到20×20的尺寸
	resize(testImgl, testImgl, Size(20, 20));
	resize(testImg2, testImg2, Size(20, 20));
	Mat testdata = Mat::zeros(2, 400, CV_8UC1);

	//将数据改写成行的形式
	Rect rect;
	rect.x = 0;
	rect.y = 0;
	rect.height = 1; 
	rect.width = 400;
	Mat oneDate = testImgl.reshape(1, 1);
	Mat twoData = testImg2.reshape(1, 1);
	oneDate.copyTo(testdata(rect));

	rect.y = 1;
	twoData.copyTo(testdata(rect));
	
	//数据类型转换
	testdata.convertTo(testdata, CV_32F);

	//进行估计识别
	Mat result2;
	knn->findNearest(testdata, 5, result2);

	//查看预测结果
	for (int i = 0; i < result2.rows; i++)
	{
    
    
		int predict = result2.at<float>(i, 0);
		cout << "第" << i + 1 << "图像预测结果:" << predict << "真实结果:" << i + 1 << endl;
	}

	waitKey(0);
	return 0;
}

二、K均值聚类

K均值聚类的步骤:
Step1:指定将数据聚类成k类,并随机生成k个中心点。
Step2:遍历所有数据,根据数据与中心的位置关系将每个数据归类到不同的中心里。
Step3:计算每个聚类的平均值,并将均值作为新的中心点。
Step4:重复Step2和Step3,直到每个聚类中心点的坐标收敛,输出聚类结果。

double cv::kmeans ( InputArray data, int K, InputOutputArray bestLabels,TermCriteria criteria, int attempts, int flags, OutputArray centers = noArray())

data:需要聚类的输入数据,数据必须行排列,即每一行是一个单独的数据。
K:将数据聚类的种类数目。
bestLabels:存储每个数据聚类结果索引的矩阵或向量。
criteria:迭代算法终止条件。
attempts:表示采样不同初始化标签尝试的次数。
flags:每类中心坐标初始化方法标志。
centers:最终聚类后的每个类的中心位置坐标。
使用聚类对图像进行分割的样例如下:

int main() {
    
    

	//读取图片
	Mat src = imread("1.png");
	if (src.empty())
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	//定义五种图像的颜色
	Vec3b colorLut2[5] = {
    
    
		Vec3b(0,0,255),
		Vec3b(0,255,0),
		Vec3b(255,0,0),
		Vec3b(0,255,255),
		Vec3b(255,0,255)
	};

	//图像的尺寸,用于计算图像中像素点的数目
	int width = src.cols ;
	int height = src.rows;

	//初始化定义
	int sampleCount = width * height;

	//将图像矩阵数据转换成每行一个数据的形式
	Mat sample_data = src.reshape(3, sampleCount); 
	Mat data;
	sample_data.convertTo(data, CV_32F);

	// KMean函数将像素值进行分类
	// 分割后的颜色种类
	int number2 = 3;	
	Mat labels2;

	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
	kmeans(data, number2, labels2, criteria, number2, KMEANS_PP_CENTERS);

	//显示图像分割结果
	Mat result = Mat::zeros(src.size(), src.type()); 
	for (int row = 0; row < height; row++)
	{
    
    
		for (int col = 0; col < width; col++)
		{
    
    
			int index = row * width + col;
			int label = labels2.at<int>(index, 0);
			result.at<Vec3b>(row, col) = colorLut2[label];
		}
	}
	namedWindow("原图", WINDOW_NORMAL);
	imshow("原图", src);

	namedWindow("分割", WINDOW_NORMAL);
	imshow("分割", result);

	waitKey(0);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_52302919/article/details/130931869