最近想学习一下分类算法的内容,恰好opencv有SVM的函数,故先从这个下手。找了许多资料,发现要么是opencv2、3的,要么就没有具体实现代码,学习还是把代码与原理一起结合来看比较好。
其中,我主要参考的是这一篇文章:
学习SVM(一) SVM模型训练与分类的OpenCV实现https://blog.csdn.net/chaipp0607/article/details/68067098写得非常好!但是是2017年发布的文章,其中许多内容都做了更新,我用的是opencv 4.5.1版本,win10系统,vs2019作开发工具。具体opencv配置不说了,我对上面那篇文章的代码进行了更新。
步骤一样.
一、数据准备
首先找到opencv库自带的digits图片,我的电脑上路径在:D:\app\opencv4.5.1\opencv\opencv\sources\samples\data\digits.png
然后在D盘建立如下文件夹:
只需新建命名就好了,不用往里面放东西。接下来建立vs2019项目工程,新建源文件
复制如下代码:
#include <windows.h>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/utils/logger.hpp>
#include <thread>
#include <time.h>
//#include <stdio.h>
#include <string.h>
using namespace std;
using namespace cv;
int main()
{
char ad[128] = { 0 };
int filename = 0, filenum = 0;
Mat img = imread("digits.png");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
int b = 20;
int m = gray.rows / b; //原图为1000*2000
int n = gray.cols / b; //裁剪为5000个20*20的小图块
for (int i = 0; i < m; i++)
{
int offsetRow = i * b; //行上的偏移量
if (i % 5 == 0 && i != 0)
{
filename++;
filenum = 0;
}
for (int j = 0; j < n; j++)
{
int offsetCol = j * b; //列上的偏移量
sprintf_s(ad, "D:\\data\\%d\\%d.jpg", filename, filenum++);
//截取20*20的小块
Mat tmp;
gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
imwrite(ad, tmp);
}
}
return 0;
}
运行结束后,在刚刚新建的文件夹中,0、1文件夹内各有500张分割好的图片。
最后在test_image、train_image分别新建0、1文件夹。
把data\0中的0-399复制到data\test_image\0,399-499复制到data\train_image\0;
把data\1中的0-399复制到data\test_image\1,399-499复制到data\train_image\1。第一步完成。
--D:
--data
--0
--1
--train_image
--0(400张)
--1(400张)
--test_image
--0(100张)
--1(100张)
二、模型训练
再新建一个源文件:SVM模型训练.cpp,将第一步的SVM数据准备文件从项目中移除。
复制上如下代码,其中最主要的就是opencv4中的SVM函数改变很大,配置参数上与原文完全不同
#include <stdio.h>
#include <time.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/ml/ml.hpp>
#include <io.h>
using namespace std;
using namespace cv;
using namespace cv::ml;
void getFiles(string path, vector<string>& files);
void get_1(Mat& trainingImages, vector<int>& trainingLabels);
void get_0(Mat& trainingImages, vector<int>& trainingLabels);
int main()
{
//获取训练数据
Mat classes;
Mat trainingData;
Mat trainingImages;
vector<int> trainingLabels;
get_1(trainingImages, trainingLabels);
//waitKey(2000);
get_0(trainingImages, trainingLabels);
Mat(trainingImages).copyTo(trainingData);
trainingData.convertTo(trainingData, CV_32FC1);
Mat(trainingLabels).copyTo(classes);
//配置SVM训练器参数
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setDegree(0);
svm->setGamma(1);
svm->setCoef0(0);
svm->setC(1);
svm->setNu(0);
svm->setP(0);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 0.01));
//训练
svm->train(trainingData, ROW_SAMPLE, classes );
//保存模型
svm->save("svm.xml");
cout << "训练好了!!!" << endl;
getchar();
return 0;
}
void getFiles(string path, vector<string>& files)
{
long long hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
void get_1(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\data\\train_image\\1";
cout << "获取D:\\data\\1" << endl;
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
SrcImage = SrcImage.reshape(1, 1);
trainingImages.push_back(SrcImage);
trainingLabels.push_back(1);
}
}
void get_0(Mat& trainingImages, vector<int>& trainingLabels)
{
string filePath = "D:\\data\\train_image\\0";
cout << "获取D:\\data\\0" << endl;
vector<string> files;
getFiles(filePath, files);
int number = files.size();
for (int i = 0; i < number; i++)
{
Mat SrcImage = imread(files[i].c_str());
SrcImage = SrcImage.reshape(1, 1);
trainingImages.push_back(SrcImage);
trainingLabels.push_back(0);
}
}
训练完毕后,在这个解决方案文件夹下就生成了一个.xml文件,即是我们训练出来的模型。
训练时还可以选择自动训练,会自己寻找最优参数,效果也很好。
//训练
svm->trainAuto(trainingData, ROW_SAMPLE, classes );
三、加载模型实现分类
同样的,新建一个源文件:
复制如下代码:
#include <stdio.h>
#include <time.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/imgcodecs.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <opencv2/ml/ml.hpp>
#include <io.h>
using namespace std;
using namespace cv;
using namespace cv::ml;
void getFiles(string path, vector<string>& files);
int main()
{
int result = 0;
string filePath = "D:\\data\\test_image\\1";
vector<string> files;
getFiles(filePath, files);
int number = files.size();
cout << number << endl;
string modelpath = "svm.xml";
cv::Ptr<cv::ml::SVM> svm;
svm = cv::Algorithm::load<cv::ml::SVM>(modelpath);
/*CvSVM svm;
svm.clear();
string modelpath = "svm.xml";
FileStorage svm_fs(modelpath, FileStorage::READ);
if (svm_fs.isOpened())
{
svm.load(modelpath.c_str());
}*/
for (int i = 0; i < number; i++)
{
Mat inMat = imread(files[i].c_str());
Mat p = inMat.reshape(1, 1);
p.convertTo(p, CV_32FC1);
int response = (int)svm->predict(p);
if (response == 1)//要预测1,如果用0来做测试集就改成response == 0
{
result++;
}
else
{
cout << files[i] << endl;
}
}
cout << result << endl;
getchar();
return 0;
}
void getFiles(string path, vector<string>& files)
{
long long hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
}
else
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
}
如果想要检测0的分类准确率就让第46行的response == 0。
文章来源:https://www.toymoban.com/news/detail-487017.html
可以看到,100张1有99张被识别出来,有一张452没有识别成功。100张0都识别出来了。文章来源地址https://www.toymoban.com/news/detail-487017.html
到了这里,关于opencv C++ SVM模型训练与分类实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!