Caffe中,卷積的作用是提取抽象特征,很多層卷積逐漸獲得一幅圖像的抽象特征,為后面分類打下基礎。而究竟這些抽象特征是什么,是形狀?是紋理?是投影?還是顏色?還是都有,需要直觀的去看了。可視化諸層很有必要。Python的源碼在caffe官網上有,不再贅述。C++的代碼,可以借助OpenCV中的dnn模塊獲取。本程序是基於OpenCV的dnn模塊中的例子改編的。
- 關於getPlane的用法:
Mat cv::dnn::Blob::getPlane ( int n,
int cn
)
Returns slice of first two dimensions.
The behaviour is similar to the following numpy code: blob[n, cn, …]
- 代碼如下:
Mat img40(Mat& srcimg)
{
Mat img;
resize(srcimg,img,Size(32,32));
//img.copyTo(binary_img);
Mat in_large=Mat::zeros(Size(40,40),img.type());//建立黑板
float x = in_large.cols / 2 - img.cols / 2;//兩個圖像的中心點差x坐標
float y = in_large.rows / 2 - img.rows / 2;//兩個圖像的中心點差y坐標
//將圖像A(20×20)按照上下左右各空出x或y的像素寬,復制到B(28×28)。
copyMakeBorder(img, in_large, y, y, x, x, BORDER_CONSTANT, Scalar::all(0));
resize(in_large, in_large, Size(40, 40));//由於有個bug,重新標准化
return in_large;
}
//批量處理函數
void createDocList(std::vector<std::string> &doc_list){
int return_code;
DIR *dir;
struct dirent entry;
struct dirent *res;
string real_dir = "./img";//搜索的目錄
if ((dir = opendir(real_dir.c_str())) != NULL) {//打開目錄
for (return_code = readdir_r(dir, &entry, &res);res != NULL && return_code == 0;return_code = readdir_r(dir, &entry, &res)) {
if (entry.d_type != DT_DIR) {//存放到列表中
doc_list.push_back(string(entry.d_name));
}
}
closedir(dir);//關閉目錄
}
}
int main(int argc, char **argv)
{
String modelTxt = "lenet_hanzi_yao_s.prototxt";
String modelBin = "lenet_iter_100000.caffemodel";
// String imageFile = (argc > 1) ? argv[1] : "space_shuttle.jpg";
//String imageFile = (argc > 1) ? argv[1] : "/hanzi/h950.jpg";
string file_path = "./img/";
string search_path = file_path + "*.jpg";
vector<string> file_list;
createDocList(file_list);
//sort(file_list.begin(),file_list.end(),Num);
cout<<"chengxu"<<file_list.size()<<endl;
if (file_list.size()==0)
cout << "open file error!" << endl;
Ptr<dnn::Importer> importer;
try //Try to import Caffe GoogleNet model
{
importer = dnn::createCaffeImporter(modelTxt, modelBin);
}
catch (const cv::Exception &err) //Importer can throw errors, we will catch them
{
std::cerr << err.msg << std::endl;
}
if (!importer)
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << modelTxt << std::endl;
std::cerr << "caffemodel: " << modelBin << std::endl;
std::cerr << "bvlc_googlenet.caffemodel can be downloaded here:" << std::endl;
std::cerr << "http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel" << std::endl;
exit(-1);
}
dnn::Net net;
importer->populateNet(net);
importer.release(); //We don't need importer anymore
for (int i = 0; i < file_list.size(); i++)
{
string image_path = file_path + file_list[i];
Mat srcimg = imread(image_path,0);
Mat img=img40(srcimg);//將圖像轉為40×40大小
if (img.empty())
{
std::cerr << "Can't read image from the file: " << image_path << std::endl;
exit(-1);
}
dnn::Blob inputBlob = dnn::Blob(img); //Convert Mat to dnn::Blob image batch
net.setBlob(".data", inputBlob); //set the network input
net.forward(); //compute output
dnn::Blob prob = net.getBlob("conv1"); //獲取conv1層的Blob
stringstream ss;
stringstream ss;
string s;
for(int a=0;a<1;a++)
{
for(int j=0;j<20;j++)
{
ss<<i<<"["<<a<<","<<j<<"]"<<".jpg";
ss>>s;
Mat featureimg=prob.getPlane(a,j);//conv1層Blob中有20個抽象特征圖層,可以全部輸出。其中,a是指第幾張圖片,j是第幾個特征層。
imwrite(s,featureimg);
ss.clear();
s.clear();
}
}
}
return 0;
}