1. 程式人生 > >QT 下用opencv實現影象分類(1)

QT 下用opencv實現影象分類(1)

一.概述

1.按影象中的內容給影象分類是計算機視覺中比較適合初學者的專案,我見過好多手機相簿都有這一個功能,比如把美食歸為一個標籤,藍天白雲歸為一個標籤等等。還有我之前做過的車牌識別的專案都用到影象分類。

2.我做這個專案的環境是QT加opencv3.2,專案在MAC上跑過,在win7也跑過,關於QT加opencv庫的配置,各個系統網上有非常詳細的教程,有興趣的可以試試。

3.這個專案所涉及到的知識點,QT的讀寫檔案與資料夾,opencv的特徵提取,BOW,SVM等,C++的幾個關聯容器等。

二.主要功能

  專案的是作用是先對一些已經手動歸類好的圖進行輸入並訓練,然後對未知的影象進行預測判斷並分類,主要是為了代替人的視覺完成對未知影象的歸類。

大體流程圖:

三.資料準備

1.在寫專案之前,必須先準備好要訓練的圖片集,就是把擁有相同內容的影象放在一個資料夾下,格式如下:

    

2.影象不能太大,也不能太小,試過在訓練集放了全是幾M以上的影象,跑特徵聚類時跑了一天沒有跑出來,如果影象太小隻有幾K,到特徵提取那裡有可能會報錯,認為是空的影象矩陣。

3.要用來訓練的影象可以到那些專業桌布的網站下載,一般那些網站都分好類了。

四.基本流程與步驟

1.讀取訓練集

   1.1 讀取給定有影象訓練集,遍歷得到訓練集的種類。如果之前沒有訓練過,按訓練集的種類新建相關的資料夾。

   1.2 從訓練集中隨機提取一張影象做為模板,並重命名成與影象內容相關的檔名,儲存。

   1.3 遍歷模板資料夾,把相關的模板影象存到容器裡。

    程式碼示例:

//建立相關的資料夾
void categorizer::initFolder(QString path)
{
    QStringList rootFolder;
    rootFolder <<"SVM_file" << "Data_TXT" << "template_image" << "result_image"<<"test_image";
    createFolder(path,rootFolder);
    QStringList cateFolder = getFolder(path+"train_images");
    for(int i = 0;i < cateFolder.size(); i++)
    {
        QStringList fileList = getFileNames(path+"train_images/"+cateFolder.at(i));
        if(!QFile::exists(path+"template_image/"+cateFolder.at(i)+".jpg"))
        {
            QFile::copy(path+"train_images/"+cateFolder.at(i)+"/"+fileList.at(1),
                        path+"template_image/"+cateFolder.at(i)+".jpg");
            QFile::remove(path+"train_images/"+cateFolder.at(i)+"/"+fileList.at(1));
        }
        else
        {
            qDebug() << "當前檔案已存在";
        }

    }
}
    //開始遍歷模板資料資料夾
    QStringList fileList = traversalFolderImage(TEMPLATE_FOLDER);
    if(fileList.empty())
    {
        qDebug() << "模板目錄為空!" ;
        return;
    }
    for(int i = 0; i < fileList.count(); i++)
    {
        QString fileName = fileList.at(i);
        //獲取字元在字串中的位置
        int k = fileName.indexOf('.',1);
        //獲取字串某位置的值
        QString str = fileName.mid(0,k);
        string className = str.toStdString();

        //讀入模板圖片
        Mat image = imread(TEMPLATE_FOLDER+fileName.toStdString());
        //儲存模板圖片
        sm_result_objects[className] = image;
    }

2.構造訓練集

    2.1 遍歷訓練集資料夾下的所有影象檔案,儲存multimap容器。

    2.2  multimap的儲存方式如下圖:

           

    程式碼部分:

//Subdirectories列出所有子目錄中的條目
    QDirIterator iter(TRAIN_FOLDER, QDirIterator::Subdirectories);

    //如果目錄中至少有一個條目,則返回true,否則將返回false。
    while (iter.hasNext())
    {
        iter.next();

        //返回當前目錄下檔案的檔名,不帶路徑
        QFileInfo info = iter.fileInfo();

        //判斷是否是目錄
        if(info.isDir())
        {
            //下層目錄或者下下層目錄,跳出迴圈
            if(info.fileName()=="." || info.fileName() == "..")
            {
                continue;
            }
            //得到檔名並轉成C++字串
            categor = info.fileName().toStdString();
            //加到容器中
            s_category_name.push_back(categor);
        }
        else
        {
            //得到絕對路徑名
            QString file_name =  info.absoluteFilePath();
            //轉成C++字串
            string file_name_std = file_name.toStdString();
            //讀取圖片,以灰度影象讀取
            Mat temp = imread(file_name_std,CV_LOAD_IMAGE_GRAYSCALE);
            //將資料夾名與資料夾下的圖片名的形式組合儲存
            pair<string,Mat> p(categor,temp);
            i++;
            qDebug() <<"開始讀取訓練集的第"<<i<<"張圖片......";

            //加到multimap,鍵名可重複出現
            sm_trains_set.insert(p);
        }
    }
    i_categories_size = s_category_name.size();

3.提取訓練集裡每張影象的特徵,得出詞典

    3.1 提取每張影象的特徵點。

    3.2 得到特徵點描述符。

    3.3 把特徵描述符儲存到一個Mat容器當中去,此時一張影象的特徵描述符在Mat容器中佔一行。

    3.4 聚類得到聚類之後的檔案並儲存。

     程式碼示例:

//進行資料庫儲存的初始化,以可讀的方式
   FileStorage vacab_fs(DATA_FOLDER"surf_vocab.xml",FileStorage::READ);
   //之前已生成好的詞典,不用重新須做聚類計算
   //檢查檔案是否已經開啟
   if(vacab_fs.isOpened())
   {
       qDebug()<< "圖片的聚類詞典已經存在.....";
       //儲存或讀取操作完成後,需要關閉檔案並釋放快取
       vacab_fs.release();
   }
   else
   {
       Mat m_vocab_descriptors;
       //對於每一幅模板,提取SURF運算元,存入到vocab_descriptors中
       //定義一個迭代器
       multimap<string,Mat>::iterator i = sm_trains_set.begin();
       //開始遍歷圖片
       for(;i!=sm_trains_set.end();i++)
       {
           //定義關鍵點
           vector<KeyPoint> key_points;

           //讀取圖片
           Mat m_temp_iamge = (*i).second;

           //detect函式檢測SURF/SIFT特徵的關鍵點,並儲存在vector容器中,最後使用drawKeypoints函式繪製出特徵點。
           s_feature_decter->detect(m_temp_iamge,key_points,Mat());

           Mat m_descrip;
           //得到特徵描述符
           s_descriptor_extractor->compute(m_temp_iamge,key_points,m_descrip);
           /*Mat的每一行都是一個影象提取到的特徵點*/
           m_vocab_descriptors.push_back(m_descrip);
       }
       qDebug() << "訓練圖片開始聚類......";
       //增加描述符到訓練集
       bowtrainer->add(m_vocab_descriptors);
       //根據訓練集進行聚類
       m_vocab = bowtrainer->cluster();
       qDebug() << "聚類完畢,得出詞典......";

       //開啟檔案進行寫操作

       FileStorage file_stor(DATA_FOLDER"surf_vocab.xml",FileStorage::WRITE);
       file_stor<<"vocabulary"<<m_vocab;
       file_stor.release();
   }