鑒於用caffe做實驗的時候,里面牽扯到一個問題是必須將訓練集和測試集都轉成256*256的圖像,而官網給出的代碼又不會用,所以我用opencv轉了。其實opencv只轉一幅圖會很簡單,關鍵在於“批量”二字,因此本博客應運而生了。
本博客詳細講解了如何使用opencv批量處理圖像,使某一個文件夾內全部圖像都resize成256*256的。如果理解了本次博客內容,則不光可以對圖像進行resize操作,還可以用來對圖像做其它批處理操作。
1 先用opencv轉一幅圖像試試
1 #include "cv.h" 2 #include "highgui.h" 3 #include "math.h" 4 #include <iostream> 5 using namespace std; 6 7 void main() 8 { 9 IplImage *src; 10 IplImage *dst; 11 src=cvLoadImage("C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\0101.jpg", 0);//載入源圖像 12 dst=cvCreateImage(cvSize(256,256),src->depth,src->nChannels);//分配一個256*256的目標圖像,resize后的結果將放在這里 13 if(src==0) 14 { 15 printf("打開圖片失敗!"); 16 exit(0); 17 } 18 cvNamedWindow("src",CV_WINDOW_AUTOSIZE);//分配一個用以承載圖片的窗口 19 cvNamedWindow("dst",CV_WINDOW_AUTOSIZE);//分配一個用以承載圖片的窗口 20 cvShowImage("src",src);//顯示src 21 cvShowImage("dst",dst);//顯示dst 22 cvWaitKey(0); 23 24 //此處插入resize 25 cvResize(src,dst); 26 //此處插入resize 27 cvSaveImage("C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\result\\0101.jpg",dst);//保存dst 28 cvNamedWindow("dst",CV_WINDOW_AUTOSIZE);//分配一個用以承載圖片的窗口 29 cvShowImage("dst",dst);//顯示dst 30 cvWaitKey(0); 31 }
這一部分怎么來的太簡單了就不多說了,直接翻一下opencv中的cvResize函數聲明,需要什么參數就填什么。
【實驗結果】
src:
dst:
2 FindFirstFile函數與FindNextFile函數
使用這兩個函數可以遍歷整個圖像文件夾。以下例子來自於百度百科,按照自己理解注釋了。
1 #include <iostream> 2 #include <windows.h> 3 using namespace std; 4 5 void main() 6 { 7 WIN32_FIND_DATA p; //p是一個用於保存文件信息的結構體 8 HANDLE h=FindFirstFile("C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\*.jpg",&p); 9 10 /*FindFirstFile的返回值是一個句柄,第二個參數p是采用引用的方式,也就是說當這句話執行完畢后p就指向該文件*.jpg*/ 11 12 cout<<p.cFileName<<endl;//打印被找到的第一個*.jpg的文件名 13 while(FindNextFile(h,&p)) //p不斷后移,尋找下一個、下下一個*.jpg 14 { 15 cout<<p.cFileName<<endl; 16 } 17 }
這一部分是用來遍歷整個文件夾里全部jpg類型的圖像的。為了方便觀察,我們的p指針每指向一個新的jpg文件,都將文件名打印出來。
【實驗結果】
3 批量resize一個文件夾里的所有圖像
這一部分結合了以上兩部分內容。除此之外還需要一點點C++的string類的知識,知道string類實例化出的對象能夠方便地進行字符串的拼接。
3-1 string類對象的正確用法
需要注意的一點是如果用string類實例化出一個對象,那么這個對象不是字符串,因此像C中用來將字符串轉成整數的函數atoi呀,以及字符串里填上全路徑用作文件地址索引呀,都是不能直接用這個對象的。
以“字符串-整數轉換”為例:
1 string label; 2 3 label_int=atoi(label); //錯誤用法 4 5 label_int=atoi(label.c_str());//正確用法
同理在這里把string類實例化出的對象用作路徑索引時,也是不能直接給一個對象,得給出“對象.c_str()”。
3-2 路徑頭+文件名=全路徑
這里要談到將一個文件夾內所有jpg文件批量處理的核心思想。即以下幾步:
(1)在第2節中的結構體p僅能給出它索引到的jpg文件的文件名,而沒有給出它們的路徑。
這些文件名如第2節所述,使用p.cFileName即可獲取。
(2)每一幅圖像的路徑是相同的,只有文件名不同,鑒於這一點,我們應該自行給出這些圖像的路徑頭——即“全路徑-文件名”。
例如對圖像C:\Users\LJJ\Desktop\測試圖\caffe實驗\resize\0000.jpg,其中“0000.jpg”是它的文件名,“C:\Users\LJJ\Desktop\測試圖\caffe實驗\resize\”就是它的路徑頭。在寫成字符串時,應該對反斜杠\進行轉義字符處理(即寫成\\):
”C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\”
(3)如前述,string類對象可以方便地進行字符串拼接,如:
1 string label1=”one”; 2 3 string label2=”two”; 4 5 string label=label1+label2; //如果cout<<label,則輸出字符串”onetwo”
因此我們可以方便地完成對路徑頭和文件名的拼接。其中路徑頭是我們自己給的,文件名則由p.cFileName獲取,隨着指針p后移,將會索引該文件夾內全部的jpg文件。
3-3 批量resize代碼清單
1 #include "cv.h" 2 #include "highgui.h" 3 #include "math.h" 4 #include <iostream> 5 #include <string> 6 #include <windows.h> 7 using namespace std; 8 9 void main() 10 { 11 IplImage *src; 12 IplImage *dst; 13 WIN32_FIND_DATA p; //指向一個用於保存文件信息的結構體 14 HANDLE h=FindFirstFile("C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\*.jpg",&p); //FindFirstFile的返回值是一個句柄,第二個參數p是采用引用的方式,也就是說當這句話執行完畢后p就指向該文件*.jpg 15 16 //由於p的成員變量只有文件名,而無文件路徑,所以必須加上路徑頭 17 string src_route_head="C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\"; //源圖像的路徑頭 18 string dst_route_head="C:\\Users\\LJJ\\Desktop\\測試圖\\caffe實驗\\resize\\result\\";//目標圖像的路徑頭 19 string SourceRoute=src_route_head+p.cFileName; //包含了路徑頭和文件名的全路徑 20 string DestRoute=dst_route_head+p.cFileName; 21 22 src=cvLoadImage(SourceRoute.c_str(), 0);//載入源圖像 23 dst=cvCreateImage(cvSize(256,256),src->depth,src->nChannels);//分配一個256*256的目標圖像,resize后的結果將放在這里 24 25 cvResize(src,dst); 26 cvSaveImage(DestRoute.c_str(),dst);//保存dst 27 28 //到目前為止,我們就已經完成了對目標文件夾中第一幅圖像的resize處理與保存,接下來讓該文件夾中其余圖像也被處理 29 30 while(FindNextFile(h,&p)) //p指針不斷后移,尋找下一個、下下一個*.jpg 31 { 32 SourceRoute=src_route_head+p.cFileName; 33 src=cvLoadImage(SourceRoute.c_str(), 0);//載入源圖像 34 35 cvResize(src,dst); 36 37 DestRoute=dst_route_head+p.cFileName; 38 cvSaveImage(DestRoute.c_str(),dst);//保存dst 39 } 40 }
2016.5.11
by 悠望南山