1. 程式人生 > >在ubuntu16.04+opencv3.0環境下使用mnist手寫體資料集編寫相關程式

在ubuntu16.04+opencv3.0環境下使用mnist手寫體資料集編寫相關程式

 因為最近可能做專案需要,因此搜尋了手寫體數字檢測部落格,在查看了大量部落格後總結了一些自己的學習小體會。但是但是-----敲黑板、劃重點了。呵呵,就是還是先把參考的幾篇好部落格的分享給大家。
 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讀出來的圖片是黑底白字)。