花40分鐘寫一個-CBIR引擎-代碼公開

   瀏覽網頁的時候發現一篇不錯的文章"用Python和OpenCV創建一個圖片搜索引擎的完整指南 " http://python.jobbole.com/80860/. 作者在瀏覽自己旅遊的照片的時候,發現照片太多了分類不過來,一時技癢寫了個分類軟件,雖然簡單但是有用。關鍵的是我發現他在原文中使用了半個小時就寫出來了。【2022年,現在這裏的技術已經過時,應該使用milvus】
       蠻快的嘛,我想。那麼我要用多長時間寫出來了,畢竟對於CBIR也是研究過的。
       那麼立即來做,首先我要找到是圖片。我沒有那麼多旅遊圖片(汗),但是別人的照片也是可以一樣用的。找到了之前專門用於測試CBIR的圖片集,大概是這個樣子

 

 

 
就是各種奇奇怪 怪 的照片。然後搭建opencv的基本框架。我們python用的不熟,但是c++下面自己是有類庫的,所以用起來也不是很複雜
      
 首先是讀入所有的圖片:
//遞歸讀取目錄下全部文件
void getFiles(string path, vector <string > & files,string flag){
     //文件句柄
     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  && flag == "r")
                    getFiles( p.assign(path).append( "\\").append(fileinfo.name), files,flag );
            }
             else{
                files.push_back(p.assign(path).append( "\\").append(fileinfo.name) );
            }
        } while(_findnext(hFile,  &fileinfo)   ==  0);
        _findclose(hFile);
    }
}
//遞歸讀取目錄下全部圖片
void getFiles(string path, vector <Mat > & files,string flag){
    vector <string > fileNames;
    getFiles(path,fileNames,flag);
     for ( int i = 0;i <fileNames.size();i ++){
        Mat tmp  = imread(fileNames[i]);
         if (tmp.rows > 0) //如果是圖片
            files.push_back(tmp);
    }
}
//遞歸讀取目錄下全部圖片和名稱
void getFiles(string path, vector <pair <Mat,string >> & files,string flag){
    vector <string > fileNames;
    getFiles(path,fileNames,flag);
     for ( int i = 0;i <fileNames.size();i ++){
        Mat tmp  = imread(fileNames[i]);
         if (tmp.rows > 0){
               pair <Mat,string > apir;
               apir.first  = tmp;
               apir.second  = fileNames[i];
               files.push_back(apir);
        }
    }
}
然後是編寫hsv距離,這個參考以前的資料
double GetHsVDistance(Mat src_base,Mat src_test1){
    Mat   hsv_base;
    Mat   hsv_test1;
     ///  Convert  to  HSV
    cvtColor(  src_base,  hsv_base,  COLOR_BGR2HSV  );
    cvtColor(  src_test1,  hsv_test1,  COLOR_BGR2HSV  );
     ///  Using  50  bins  for  hue  and  60  for  saturation
     int  h_bins   =   50;   int  s_bins   =   60;
     int  histSize[]   =  {  h_bins,  s_bins  };
     //  hue  varies  from  0  to  179,  saturation  from  0  to  255
     float  h_ranges[]   =  {   0,   180  };
     float  s_ranges[]   =  {   0,   256  };
     const   float *  ranges[]   =  {  h_ranges,  s_ranges  };
     //  Use  the  o-th  and  1-st  channels
     int  channels[]   =  {   0,   1  };
     ///  Histograms
    MatND  hist_base;
    MatND  hist_test1;
     ///  Calculate  the  histograms  for  the  HSV  images
    calcHist(   &hsv_base,   1,  channels,  Mat(),  hist_base,   2,  histSize,  ranges,   true,   false  );
    normalize(  hist_base,  hist_base,   0,   1,  NORM_MINMAX,   - 1,  Mat()  );
    calcHist(   &hsv_test1,   1,  channels,  Mat(),  hist_test1,   2,  histSize,  ranges,   true,   false  );
    normalize(  hist_test1,  hist_test1,   0,   1,  NORM_MINMAX,   - 1,  Mat()  );
     ///  Apply  the  histogram  comparison  methods
     double  base_test1   =  compareHist(  hist_base,  hist_test1,   0  );
     return base_test1;
}

 

封裝成函數。這個函數比原文中作者提出的方法要簡單,我偷懶了。
然後就是要編寫主函數程序,這個比較麻煩的地方就是要比較出最前面的10 個圖片 。我採用比較笨的方法,趕時間嘛:
int _tmain( int argc, _TCHAR * argv[])
{    
    vector <pair <Mat,string >> imagepairs;
    vector < double > dresult;
     double dmax  =  0;
     int imax  =  - 1;
    
     //讀入圖片
    getFiles( "images",imagepairs);
    Mat src  = imread( "images/0.jpg");
     //距離測算
     for ( int i = 0;i <imagepairs.size();i ++){
         double tmp  = GetHsVDistance(src,imagepairs[i].first);
         if (tmp  == 1)
            tmp  = 0; //不能搞自己
         char cbuf[ 1024];
        sprintf_s(cbuf, "dst/%d.jpg",i);
        dresult.push_back(tmp); //推入vecresult中
    }
     //尋找前10個圖片
     for ( int index  =  0;index < 10;index ++){
         for ( int i = 0;i <imagepairs.size();i ++){
             if (dresult[i] >dmax){
                dmax  = dresult[i];
                imax  = i;
            }
        }
         char cbuf[ 1024];
        sprintf_s(cbuf, "dst/%d.jpg",index);
        imwrite(cbuf,imagepairs[imax].first);
        dresult[imax]  =  0; //剔出隊列
        dmax  =  0;
        imax  =  - 1;
    }
    printf( "OK");
    waitKey();
     return  0;
}

 

 
 
 
前後花了40-50分鐘時間,最後的效果不如作者的效果。主要差距在覈心算法上面。看來日常的算法總結重構的確很有價值。
這篇文章先寫到這裏,最近事多,等到閒下來再進行重構。歡迎大家批評指正。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章