SVM实现分类识别及参数调优(一)
前言
项目有一个模块需要将不同类别的图片进行分类,共有三个类别,使用SVM实现分类。
实现步骤:
1.创建训练样本库;
2.训练、测试SVM模型;
3.SVM的数据要求;
实现系统:
windows_x64、opencv2.4.10、 VS2013
实现过程:
1.创建训练样本库;
1)将图片以包含类别的名称进行命名,比如0(1).jpg等等;
2)将所有已命名正确的训练样本保存在同一个文件夹中;
3)在训练样本库的文件夹目录下创建python源文件;
python代码:

import sys import os import string import re if __name__=='__main__': print('Begin generate path and label.') path_file=open('train_path.txt','w') path='E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model' pic_type='.png' pat=re.compile(r'^(\d+)') files=os.listdir(path) files_tmp=[] for i in files: if pic_type in i and not os.path.isdir(path+'/'+i): files_tmp.append(i) files=files_tmp for file in range(len(files)): ret=pat.match(files[file]) path_file.write(path+'/'+files[file]+'\n') if file<len(files)-1: path_file.write(ret.group(1)+'\n') else: path_file.write(ret.group(1)) path_file.close() print('finish......')
4)运行代码,即可,生成包含图片名称和类别的文本文件,用于SVM训练过程中读物图片获取相应的类别标签;
1 2 3 4 | E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model/0 (1).png 0 E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model/0 (10).png 0 |
奇数行表示训练样本图片的路径名称;偶数行表示该图片的类别标签;
2.训练、测试SVM模型;
1)image.h,主要实现过程的代码;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | #include <fstream> #include <vector> #include<direct.h> #include <opencv2\core\core.hpp> //红牌事件检测头文件 #include <opencv2\opencv.hpp> using namespace std; using namespace cv; #define ON_STUDY 0 #define Num 3 //类别数目 #define STANDARD_ROW 65 #define STANDARD_COL 85 #define STANDARD_ROW_CHOOSE 65 #define STANDARD_COL_CHOOSE 85 #define CHANELS 1 class NumTrainData { public : NumTrainData() { memset (data, 0, sizeof (data)); result = -1; } public : float data[CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE]; int result; }; vector<string> img_path; //输入文件名变量 vector<string> img_test_path; //输入文件名变量 vector< int > img_catg; vector< int > img_test_catg; int nLine = 0; string buf; unsigned long n; vector<NumTrainData> buffer; int featureLen = CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE; char * test_path = "./test_path.txt" ; char * train_path = "./train_path.txt" ; //存放输出结果 char * save_path = "./SVM_DATA_train_0.5_0.2.xml" ; ofstream matrix_config( "./fusion_matrix_0.5_0.2.txt" ); //存放混淆矩阵 string save_wrong_results = "./wrong_0.5_0.2" ; //存放识别错误的结果 void ReadTrainData() { ifstream svm_data(train_path); //训练样本图片的路径都写在这个txt文件中,使用python可以得到这个txt文件 while (svm_data) //将训练样本文件依次读取进来 { if (getline(svm_data, buf)) { nLine++; if (nLine % 2 == 0) //注:奇数行是图片全路径,偶数行是标签 { img_catg.push_back( atoi (buf.c_str())); //atoi将字符串转换成整型,标志(0,1,2,...,9),注意这里至少要有两个类别,否则会出错 } else { img_path.push_back(buf); //图像路径 } } } svm_data.close(); //关闭文件 } void ReadTestData() { ifstream svm_data(test_path); //训练样本图片的路径都写在这个txt文件中,使用python可以得到这个txt文件 while (svm_data) //将训练样本文件依次读取进来 { if (getline(svm_data, buf)) { nLine++; if (nLine % 2 == 0) //注:奇数行是图片全路径,偶数行是标签 { img_test_catg.push_back( atoi (buf.c_str())); //atoi将字符串转换成整型,标志(0,1,2,...,9),注意这里至少要有两个类别,否则会出错 } else { img_test_path.push_back(buf); //图像路径 } } } svm_data.close(); //关闭文件 } void LoadTrainData() { Mat src; //= Mat::zeros(rows, cols, CV_8UC1); Mat dst; NumTrainData rtd; cout << "Begin load training data...." << endl; for ( int i = 0; i < img_path.size(); i++) { rtd.result = img_catg[i]; int k = 0; if (CHANELS == 1) // gray image { src = imread(img_path[i].c_str(), 0); dst = src; Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1); //尺寸归一化 resize(dst, temp, temp.size()); float m[CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE]; for ( int i = 0; i<STANDARD_ROW; i++) { for ( int j = 0; j<STANDARD_COL; j++) { rtd.data[i * STANDARD_COL + j] = temp.at<uchar>(i, j); } } } else if (CHANELS == 3) // 3-channel image { src = imread(img_path[i].c_str(), 1); dst = src; Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1); //大小归一化 resize(dst, temp, temp.size()); //cout << temp.channels() << endl; for ( int i = 0; i < STANDARD_ROW_CHOOSE; i++) { for ( int j = 0; j < STANDARD_COL_CHOOSE; j++) { Vec3b& mp = temp.at<Vec3b>(i, j); float B = mp.val[0]; //cout << "B=" << B << endl; float G = mp.val[1]; //cout << "G=" << B << endl; float R = mp.val[2]; //cout << "R=" << B << endl; rtd.data[k++] = B; //R rtd.data[k++] = G; //G rtd.data[k++] = R; //B } } } buffer.push_back(rtd); //cout << i << "th Image is loaded!" << endl; } cout << "Loading image finished!" << endl; } void SVMPredict() { int x = 0; //_mkdir(save_test_preprocess.c_str()); _mkdir(save_wrong_results.c_str()); int fusion_matrix[Num][Num] = { 0 }; CvSVM svm; svm.load(save_path); Mat src,dst; Mat m = Mat::zeros(1, featureLen, CV_32FC1); NumTrainData rtd; int label = -1; int right = 0, error = 0; save_wrong_results += "/%d_true_%d_false_%d.png" ; // double ptrue_rtrue = 0; double ptrue = 0; double rtrue = 0; // for ( int i = 0; i < img_test_path.size(); i++) { label = img_test_catg[i]; rtd.result = label; if (CHANELS == 1) { src = imread(img_test_path[i].c_str(), 0); dst = src; Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1); //大小归一化 resize(dst, temp, temp.size()); for ( int i = 0; i<STANDARD_ROW; i++) { for ( int j = 0; j<STANDARD_COL; j++) { m.at< float >(0, j + i * STANDARD_COL) = temp.at<uchar>(i, j); } } normalize(m, m); } else if (CHANELS == 3) // 3-channel image { src = imread(img_test_path[i].c_str(), 1); dst = src; Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1); //大小归一化 resize(dst, temp, temp.size()); int k = 0; for ( int i = 0; i < STANDARD_ROW_CHOOSE; i++) { for ( int j = 0; j < STANDARD_COL_CHOOSE; j++) { Vec3b& mp = temp.at<Vec3b>(i, j); float B = mp.val[0]; float G = mp.val[1]; float R = mp.val[2]; m.at< float >(0, k++) = B; //R m.at< float >(0, k++) = G; //G m.at< float >(0, k++) = R; //B } } } int ret = svm.predict(m); //if (ret == 3) // ret = 1; cout << "Picture->" << img_test_path[i].c_str() << " : \nTrue label is [" << label << "] Predicted label is [" << ret << "]" << endl; // //计算FSCORE指标各个参数 if (label == 0 && ret == 0) ptrue_rtrue++; //识别为红牌且实际为红牌; if (ret == 0) ptrue++; //识别为红牌的个数 if (label == 0) rtrue++; //实际为红牌的个数 // //存储错误图片 if (label != ret) { x++; char filename[200]; src = imread(img_test_path[i].c_str(), 1); sprintf (filename, save_wrong_results.c_str(), x, label, ret); imwrite(filename, src); } //计算混淆矩阵 //fusion_matrix[label][ret] = fusion_matrix[label][ret] + 1; } // //FSCORE std::cout << "count_all: " << img_test_path.size() << std::endl; std::cout << "ptrue_rtrue: " << ptrue_rtrue << std::endl; std::cout << "ptrue: " << ptrue << std::endl; std::cout << "rtrue: " << rtrue << std::endl; //precise double precise = 0; if (ptrue != 0) { precise = ptrue_rtrue / ptrue; std::cout << "precise: " << precise << std::endl; } else { std::cout << "precise: " << "NA" << std::endl; } //recall double recall = 0; if (rtrue != 0) { recall = ptrue_rtrue / rtrue; std::cout << "recall: " << recall << std::endl; } else { std::cout << "recall: " << "NA" << std::endl; } //FSCORE double FScore = 0; if (precise + recall != 0) { FScore = 2 * (precise * recall) / (precise + recall); std::cout << "FScore: " << FScore << std::endl; } else { std::cout << "FScore: " << "NA" << std::endl; } // //for (size_t i = 0; i < Num; i++) //{ // for (size_t j = 0; j < Num; j++) // { // matrix_config << fusion_matrix[i][j] << " "; // } // matrix_config << endl; //} //matrix_config.close(); cout << "Task finished!output_matix" << endl; getchar (); } void SVMTrain(vector<NumTrainData>& trainData) { int testCount = trainData.size(); Mat m = Mat::zeros(1, featureLen, CV_32FC1); Mat data = Mat::zeros(testCount, featureLen, CV_32FC1); //Mat res = Mat::zeros(testCount, 1, CV_32SC1); Mat res = Mat::zeros(testCount, 1, CV_32SC1); for ( int i = 0; i< testCount; i++) { NumTrainData td = trainData.at(i); memcpy (m.data, td.data, featureLen * sizeof ( float )); normalize(m, m); memcpy (data.data + i*featureLen * sizeof ( float ), m.data, featureLen * sizeof ( float )); cout << td.result << endl; res.at< int >(i, 0) = td.result; } /////////////START SVM TRAINNING////////////////// //CvSVM svm = CvSVM(); CvSVM svm; CvSVMParams param; CvTermCriteria criteria; criteria = cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON); param = CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.5, 1.0, 0.2, 0.5, 0.1, NULL, criteria); //gamma=2;C=3 cout << "Begin to train model using given train data.....\n Total training sample count is " << testCount << endl; svm.train(data, res, Mat(), Mat(), param); svm.save(save_path); cout << "Finish" << endl; } |
2)主要函数说明;
2.1)SVMTrain函数主要实现模型的训练,其中训练参数使用RBF核,主要调整gamma和C这两个参数,固定一个参数调整另一个参数,最后确定模型参数分别为0.5/0.2;
2.2)SVMPredict函数主要实现对测试样本库的测试,并使用FScore指标测试SVM模型的性能;也可以使用混淆矩阵测试性能;
2.3)ReadTrainData/ ReadTestData函数分别用于获取训练和测试样本库图片的名称和类别标签;
2.4)LoadTrainData函数用于读取训练数据,并进行图像处理;
2.5)代码中使用整张图片的信息进行归一化之后作为特征;
3)主函数入口
#include "image.h" int main(int argc, char *argv[]) { #if (ON_STUDY) ReadTrainData(); LoadTrainData(); SVMTrain(buffer); #else ReadTestData(); SVMPredict(); #endif getchar(); }
参数ON_STUDY表示选择进行训练或者测试的标志位;
3.SVM的数据要求;
需要说明的是就是SVM对于输入的数据类型是有要求的,即mTrainData(训练数据矩阵)以及mFlagPosNeg(标签矩阵)都必须为CV_32FC1类型(我的环境标签矩阵是CV_32SC1类型的),因此需要进行类型转换,而且必须保证转换完之后数值都不能大于1,这就给我们了两点启示:1)不能直接用下采样后的图像像素作为训练数据的输入,需要进行类型的归一化。2)类型转换时要使用normlize函数,保证其数值范围不大于1,而不能简单的使用Mat的成员函数coverto,只变类型不变数值范围。( 需要注意!)
问题:
该实现过程需要人工调整参数,比较繁琐,可以思考一下,是否还存在其他问题;
参考:
1.http://blog.csdn.net/firefight/article/details/6452188
完
心正意诚,做自己该做的事情,做自己喜欢做的事情,安静做一枚有思想的技术媛。
版权声明,转载请注明出处:https://www.cnblogs.com/happyamyhope/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】