一、监督学习的聚类方法
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;
}