1. 程式人生 > >caffe學習系列一——影象預處理

caffe學習系列一——影象預處理

1.批量讀取資料夾內檔案:(windows下)

1)讀取某給定路徑下所有資料夾與檔名稱,並帶完整路徑,寫入txt檔案。程式碼如下:

 1 void getAllFiles(string path, vector<string>& files) {
 2     //檔案控制代碼
 3     long hFile = 0;
 4     //檔案資訊
 5     struct _finddata_t fileinfo;  //很少用的檔案資訊讀取結構
 6     string p;  //string類很有意思的一個賦值函式:assign(),有很多過載版本
 7     if ((hFile = _findfirst(p.assign(path).append("\\*"
).c_str(),&fileinfo)) != -1) { 8 do { 9 if ((fileinfo.attrib & _A_SUBDIR)) { //比較檔案型別是否是資料夾 10 if (strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0) { 11 files.push_back(p.assign(path).append("\\").append(fileinfo.name)); 12
getAllFiles(p.assign(path).append("\\").append(fileinfo.name), files); 13 } 14 } else { 15 files.push_back(p.assign(path).append("\\").append(fileinfo.name)); 16 } 17 } while (_findnext(hFile, &fileinfo) == 0); //尋找下一個,成功返回0,否則-1
18 _findclose(hFile); 19 } 20 }

2)只讀取某給定路徑下的當前資料夾名(以下類似,只給出函式,呼叫案例同上):

1 void getJustCurrentDir(string path, vector<string>& files) {
 2     //檔案控制代碼
 3     long hFile = 0;
 4     //檔案資訊 
 5     struct _finddata_t fileinfo;
 6     string p;
 7     if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1) {
 8         do {  
 9             if ((fileinfo.attrib & _A_SUBDIR)) {  
10                 if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
11                     files.push_back(fileinfo.name);
12                     //files.push_back(p.assign(path).append("\\").append(fileinfo.name));
13                 }
14             }
15         } while (_findnext(hFile, &fileinfo) == 0);
16         _findclose(hFile);
17     }
18 }

3)只讀取某給定路徑下的當前檔名:

 1 void getJustCurrentFile(string path, vector<string>& files) {
 2     //檔案控制代碼
 3     long hFile = 0;
 4     //檔案資訊
 5     struct _finddata_t fileinfo;
 6     string p;
 7     if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
 8         do {
 9             if ((fileinfo.attrib & _A_SUBDIR)) {
10                 ;
11             } else {
12                 files.push_back(fileinfo.name);
13                 //files.push_back(p.assign(path).append("\\").append(fileinfo.name));
14             }
15         } while (_findnext(hFile, &fileinfo) == 0);
16         _findclose(hFile);
17     }
18 }

4)只讀取某給定路徑下的所有檔名(即包含當前目錄及子目錄的檔案):

1 void getFilesAll(string path, vector<string>& files) {
 2     //檔案控制代碼
 3     long hFile = 0;
 4     //檔案資訊
 5     struct _finddata_t fileinfo;
 6     string p;  
 7     if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
 8         do {
 9             if ((fileinfo.attrib & _A_SUBDIR)) {
10                 if (strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0) {
11                     //files.push_back(p.assign(path).append("\\").append(fileinfo.name));
12                     getFilesAll(p.assign(path).append("\\").append(fileinfo.name), files);
13                 }
14             } else {  
15                 files.push_back(p.assign(path).append("\\").append(fileinfo.name));
16             }
17         } while (_findnext(hFile, &fileinfo) == 0);
18         _findclose(hFile);
19     }
20 }

5)提取資料夾下特定型別的檔案:

void GetAllFormatFiles( string path, vector<string>& files,string format)    
{    
    //檔案控制代碼    
    long   hFile   =   0;    
    //檔案資訊    
    struct _finddata_t fileinfo;    
    string p;    
    if((hFile = _findfirst(p.assign(path).append("\\*" + format).c_str(),&fileinfo)) !=  -1)    
    {    
        do    
        {      
            if((fileinfo.attrib &  _A_SUBDIR))    
            {    
                if(strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0)    
                {  
                    //files.push_back(p.assign(path).append("\\").append(fileinfo.name) );  
                    GetAllFormatFiles( p.assign(path).append("\\").append(fileinfo.name), files,format);   
                }  
            }    
            else    
            {    
                files.push_back(p.assign(path).append("\\").append(fileinfo.name) );    
            }    
        }while(_findnext(hFile, &fileinfo)  == 0);    

        _findclose(hFile);   
    }   
}   

參考呼叫主函式:

#include <io.h>  
#include <fstream>  
#include <string>  
#include <vector>  
#include <iostream>  
 using namespace std; 
  int main()
{
    string filePath = "E:\\program-file\\vs_project\\face_pro\\dlib_test\\dlib_test\\crop0";
    vector<string> files;
    char * distAll = "AllFiles.txt";

    //讀取所有的檔案,包括子檔案的檔案
    //GetAllFiles(filePath, files);

    //讀取所有格式為jpg的檔案
    string format = ".png";
    GetAllFormatFiles(filePath, files, format);
    ofstream ofn(distAll);
    int size = files.size();
    ofn << size << endl;
    for (int i = 0; i<size; i++)
    {
        ofn << files[i] << endl;
        cout << files[i] << endl;
    }

    ofn.close();
    return 0;
}

6)按行讀取txt檔案並存入陣列

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
int main(int args, char **argv)
{
std::ifstream fin("split.txt", std::ios::in);//定義讀取的文字檔案
char line[1024]={0};//定義讀取的每行內容的變數
std::string x = "";
std::string y = "";
std::string z = "";
while(fin.getline(line, sizeof(line)))
{
std::stringstream word(line);//stringstream按空格切分每行內容
word >> x;
word >> y;
word >> z;
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
std::cout << "z: " << z << std::endl;
}
fin.clear();//重新整理快取並關閉檔案
fin.close();
return 0;
}

2. dir批量訪問檔案(windows下):

1)bat檔案生成檔名列表

第一步,在要提取檔名的目錄下新建一個txt格式的記事本檔案;
第二步,在記事本檔案中輸入:DIR *.*  /B >LIST.TXT;
第三步,將此記事本檔案後輟名,由txt改為bat。會彈出重新命名對話方塊,單擊“是”;
第四步,雙擊檔案“新建文字文件.bat”即可生成list.txt檔案。開啟txt檔案就可以看到當前資料夾內的所有檔名列表。
(溫馨提示:你也可以把檔案“新建文字文件.bat”放在其他資料夾裡執行,獲取當前資料夾下面的所有檔名哦!)

2)DOS下批量訪問檔名:

步驟:CMD 進入dos,然後進入cd 命令進入資料夾,輸入這個命令 dir /s /b > 1.txt 
**命令詳解**:dir 列出檔案表  
            /s 是指列出當前目錄包含子目錄下的所有檔案。
            /b 是僅列出檔名稱,而日期、大小等資訊不列出;如果不加這個,則是顯示所有資訊。
            >1.txt 將列出的檔名儲存到1.txt。   
              注:>符也可以用>>符代替,
              如果“檔名.txt”檔案不存在,則>>是建立一個新檔案,是沒有區別的;   
              如果“檔名.txt”檔案已存在,則>是往檔案裡追加內容,>>是覆蓋原有內容

總結:本文的提取資料夾內檔名的方法,思路就是將檔案儲存到要提取檔名的目錄下,儲存為.bat(為檔名),然後雙擊執行就OK了。這也是傳送說中的批處理命令還可以對dir命令進行擴充套件,以過濾和篩選檔案。

3.藉助bash檔案生成檔名清單並標籤(linux)

bash檔案示例程式碼:(將cat.jpg和bike.jpg分類並標籤,儲存為train.txt)

# /usr/bin/env sh
DATA=examples/images
echo "Create train.txt..."
rm -rf $DATA/train.txt
find $DATA -name *cat.jpg | cut -d '/' -f3 | sed "s/$/ 1/">>$DATA/train.txt
find $DATA -name *bike.jpg | cut -d '/' -f3 | sed "s/$/ 2/">>$DATA/tmp.txt
cat $DATA/tmp.txt>>$DATA/train.txt
rm -rf $DATA/tmp.txt
echo "Done.."

程式碼解釋:用到了rm,find, cut, sed,cat等linux命令。
rm: 刪除檔案
find: 尋找檔案
cut: 擷取路徑
sed: 在每行的最後面加上標註。本例中將找到的*cat.jpg檔案加入標註為1,找到的*bike.jpg檔案加入標註為2
cat: 將兩個類別合併在一個檔案裡。
注:重定向符號>與>>,前者表示新建重定向檔案或者截斷,後者表示向檔案中接著新增或注入內容;
類比上面程式碼,生成相應的val.txt和test.txt檔案,可以作為caffe自帶的convert_imageset.cpp檔案第三個引數,進而生成caffe支援的db檔案:
convert_imageset.cpp的使用:

convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME

需要帶四個引數:

FLAGS: 圖片引數組,後面詳細介紹

ROOTFOLDER/: 圖片存放的絕對路徑,從linux系統根目錄開始

LISTFILE: 圖片檔案列表清單,一般為一個txt檔案,一行一張圖片

DB_NAME: 最終生成的db檔案存放目錄

如果圖片已經下載到本地電腦上了,那麼我們首先需要建立一個圖片列表清單,儲存為txt;

其中FLAGS引數介紹

-gray: 是否以灰度圖的方式開啟圖片。程式呼叫opencv庫中的imread()函式來開啟圖片,預設為false

-shuffle: 是否隨機打亂圖片順序。預設為false

-backend:需要轉換成的db檔案格式,可選為leveldb或lmdb,預設為lmdb

-resize_width/resize_height: 改變圖片的大小。在執行中,要求所有圖片的尺寸一致,因此需要改變圖片大小。 程式呼叫opencv庫的resize()函式來對圖片放大縮小,預設為0,不改變

-check_size: 檢查所有的資料是否有相同的尺寸。預設為false,不檢查

-encoded: 是否將原圖片編碼放入最終的資料中,預設為false

-encode_type: 與前一個引數對應,將圖片編碼為哪一個格式:‘png’,’jpg’……

好了,知道這些引數後,我們就可以呼叫命令來生成最終的lmdb格式資料了

由於引數比較多,因此我們可以編寫一個sh指令碼來執行命令:vim create_lmdb.sh

#!/usr/bin/en sh
DATA=examples/images
rm -rf $DATA/img_train_lmdb
build/tools/convert_imageset --shuffle \
--resize_height=256 --resize_width=256 \
/home/xxx/caffe/examples/images/ $DATA/train.txt  $DATA/img_train_lmdb

註釋:設定引數-shuffle,打亂圖片順序。
設定引數-resize_height和-resize_width將所有圖片尺寸都變為256*256.
/home/xxx/caffe/examples/images/ 為圖片儲存的絕對路徑
執行指令碼檔案,就會在examples/images/ 目錄下生成一個名為 img_train_lmdb的資料夾,裡面的檔案就是我們需要的db檔案了。

4.caffe.proto檔案自帶的檔案與處理功能

1)levelDB,LMDB,hdf5絕大部分資料層在設定時,都可以先對資料進行一定的預處理,包括歸一化scale,去中心化(減去平均值),水平映象flip,隨機裁剪crop等四種預處理方式。
該四種預處理方式可以靠該Layer的transform_params屬性(HDF5 Layer沒有該屬性。。。)來指定。指定方式如下:

transform_param {
  # randomly horizontally mirror the image
  mirror: 1
  # crop a `crop_size` x `crop_size` patch:
  # - at random during training
  # - from the center during testing
  crop_size: 227
  # substract mean value(RGB three channel): these mean_values can equivalently be replaced with a mean.binaryproto file as
  # mean_file: name_of_mean_file.binaryproto
  mean_value: 104
  mean_value: 117
  mean_value: 123
}

(2)caffe關於資料不同讀取方式下的資料層設定:caffe.proto
資料型別levelDB和lmdb,從資料庫讀取資料,資料層設定:
資料型別hdf5(支援向量形式):
圖片格式檔案,資料層設定,參見如下連線:
http://blog.csdn.net/u012177034/article/details/52134205