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;
}