在ubuntu16.04+opencv3.0环境下使用mnist手写体数据集编写相关程序

xiaoxiao2021-02-28  76

因为最近可能做项目需要,因此搜索了手写体数字检测博客,在查看了大量博客后总结了一些自己的学习小体会。但是但是-----敲黑板、划重点了。呵呵,就是还是先把参考的几篇好博客的分享给大家。 http://www.itnose.net/detail/6525586.html,这篇文章里面最可取的就是分析了mnist数据集存在的坑。很多坑我也不是太明白,哈哈。 http://blog.csdn.net/xuan_zizizi/article/details/71102018,这篇博客,对就是这篇,上一个网址我就是在这里找到的。所以这篇博客挺好的。 首先介绍下mnist数据集吧,这个数据集至少在现在看来在手写体方面使用的还是比较多的。下载它可以在这里`http://yann.lecun.com/exdb/mnist/`,打开后你可以看到四个可以下载的包,但是你会发现网传的训练60000张图片,测试10000张图片,下载下来的包怎么这么小,解压下来一看,我靠居然是二进制文件。不着急,因为很多前辈已经给了很多详细的参考,特别是在http://www.itnose.net/detail/6525586.html下,写的非常详细,如果感兴趣可以认真看下。 有3个函数需要先贴出来. 1.大小端转换`int reverseInt(int i)

{ unsigned char c1, c2, c3, c4; c1 = i & 255; c2 = (i >> 8) & 255;//>>表示右移 c3 = (i >> 16) & 255; c4 = (i >> 24) & 255; return ((int)c1 << 24) + ((int)c2 << 16) + ((int)c3 << 8) + c4;//左移 }` 因为MNIST是大端存储,然而大部分的Intel处理器都是小端存储,所以对于int、long、float这些多字节的数据类型,就要一个一个byte地翻转过来,才能正确显示。 2.读取训练图片

Mat read_mnist_image(const char* fileName) { int magic_number = 0; int number_of_images = 0; int n_rows = 0; int n_cols = 0; Mat DataMat; ifstream file; file.open(fileName, std::ifstream::binary); //fstream file;//(fileName, ios_base::binary); //file.open(fileName, ios_base::binary); if (file.is_open()) { cout << "成功打开图像集\n"; file.read((char*)&magic_number, sizeof(magic_number)); file.read((char*)&number_of_images, sizeof(number_of_images)); file.read((char*)&n_rows, sizeof(n_rows)); file.read((char*)&n_cols, sizeof(n_cols)); cout << "magic_number=/n"<<magic_number << "number_of_images=\n " << number_of_images << " " << n_rows << " " << n_cols << endl; magic_number = reverseInt(magic_number); number_of_images = reverseInt(number_of_images); n_rows = reverseInt(n_rows); n_cols = reverseInt(n_cols); cout << "MAGIC NUMBER = " << magic_number<< " ;NUMBER OF IMAGES = " << number_of_images<< " ; NUMBER OF ROWS = " << n_rows << " ; NUMBER OF COLS = " << n_cols << endl; //-test- //输出第一张和最后一张图,检测读取数据无误 Mat s = Mat::zeros(n_rows, n_rows * n_cols, CV_32FC1); Mat e = Mat::zeros(n_rows, n_rows * n_cols, CV_32FC1); cout << "开始读取Image数据......\n"; DataMat = Mat::zeros(number_of_images, n_rows * n_cols, CV_32FC1); for (int i = 0; i < number_of_images; i++) { for (int j = 0; j < n_rows * n_cols; j++) { unsigned char temp = 0; file.read((char*)&temp, sizeof(temp)); float pixel_value = float((temp + 0.0) / 255.0); DataMat.at<float>(i, j) = pixel_value; //打印第一张和最后一张图像数据 if (i == 0) { s.at<float>(j / n_cols, j % n_cols) = pixel_value; } else if (i == number_of_images - 1) { e.at<float>(j / n_cols, j % n_cols) = pixel_value; } } } //imshow("first image", s); //cout << "first image = " << s << endl; //imshow("last image", e); waitKey(0); } file.close(); return DataMat; }

3.读取标签

Mat read_mnist_label(const char* fileName) { int magic_number; int number_of_items; Mat LabelMat; ifstream file; file.open(fileName, std::ifstream::binary); //fstream file;//(fileName, ios_base::binary); //file.open(fileName, ios_base::binary); if (file.is_open()) { cout << "成功打开标签\n"; file.read((char*)&magic_number, sizeof(magic_number)); file.read((char*)&number_of_items, sizeof(number_of_items)); magic_number = reverseInt(magic_number); number_of_items = reverseInt(number_of_items); cout << "MAGIC NUMBER = " << magic_number << " ; NUMBER OF ITEMS = " << number_of_items << endl; //-test- //number_of_items = testNum; //记录第一个label和最后一个label unsigned int s = 0, e = 0; cout << "开始读取Label数据......\n"; LabelMat = Mat::zeros(number_of_items, 1, CV_32SC1); for (int i = 0; i < number_of_items; i++) { unsigned char temp = 0; file.read((char*)&temp, sizeof(temp)); LabelMat.at<unsigned int>(i, 0) = (unsigned int)temp; //打印第一个和最后一个label if (i == 0) s = (unsigned int)temp; else if (i == number_of_items - 1) e = (unsigned int)temp; } cout << "first label = " << s << endl; cout << "last label = " << e << endl; } file.close(); return LabelMat; }

其中在读图片和读标签函数中做了一点修改,不知道是因为什么原因,当把文件名以string 类型赋值的时候,file.open()一直报错,所以最后只能改成char testImage[] = “t10k-images.idx3-ubyte”。 最后就是在主函数中进行训练数据集和测试数据集。

int main() { //读取训练数据 Mat trainData; Mat labels; trainData = read_mnist_image(trainImage); labels = read_mnist_label(trainLabel); cout << " 训练数据行数:" << trainData.rows << "训练数据列数:" << trainData.cols << endl; cout << " 训练标签行数:" << labels.rows << "训练数据列数: " << labels.cols << endl; cout << "训练数据读取完成" << endl; //训练参数 Ptr<SVM> svm = SVM::create(); svm->setType(SVM::C_SVC); svm->setKernel(SVM::RBF); svm->setGamma(0.01); svm->setC(10.0); svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON)); cout << "参数设置完成" << endl; //训练分类器 cout << "开始训练分类器" << endl; svm->train(trainData, ROW_SAMPLE, labels); cout << "分类器训练完成" << endl; //保存训练器 svm->save("./mnist_svm.xml"); cout << "save as ./mnist_svm.xml" << endl; //下载分类器 //cout << "开始导入SVM文件...\n"; //Ptr<SVM> svm1 = StatModel::load<SVM>("mnist_dataset/mnist_svm.xml"); //cout << "成功导入SVM文件...\n"; //读取测试数据 Mat testData; Mat tLabel; testData = read_mnist_image(testImage); tLabel = read_mnist_label(testLabel); cout << "测试数据读取完成" << endl; float count = 0; for (int i = 0; i < testData.rows; i++) { Mat sample = testData.row(i); float res=0.0; res=svm->predict(sample); res = abs(res - tLabel.at<unsigned int>(i, 0)) <= FLT_EPSILON ? 1.f : 0.f; count += res; } cout << "正确的识别个数: " << count << endl; cout << "错误率为:" << (10000 - count + 0.0) / 10000 * 100.0 << "%\n"; system("pause"); return 0; } 那么最后程序成功运行完成后就会在build文件夹或者项目的根目录下存在一个mnist_svm.xml。这就是我们训练出来的模型了。以后需要预测手写体数字直接加载这个模型就可以了,不用每次都训练数据集。 好了这就是训练模型的程序了。 -------------------------------我是分割线 哈哈,但是此时,如果你要读取自己的视频或者本地图片进行预测,你会发现很多坑的,哈哈。 例如预测结果......en ,都是些什么玩意儿。完全对不上,说好准确率98%以上的,结果错误率。具体原因,请听下回分解。

(mnisit读出来的图片是黑底白字)。

转载请注明原文地址: https://www.6miu.com/read-62784.html

最新回复(0)