结合OpenCV可视化Caffe神经网络中的特征层

Caffe中,卷积的作用是提取抽象特征,很多层卷积逐渐获得一幅图像的抽象特征,为后面分类打下基础。而究竟这些抽象特征是什么,是形状?是纹理?是投影?还是颜色?还是都有,需要直观的去看了。可视化诸层很有必要。Python的源码在caffe官网上有,不再赘述。C++的代码,可以借助OpenCV中的dnn模块获取。本程序是基于OpenCV的dnn模块中的例子改编的。

OpenCV dnn Example 点击

OpenCV3.1中的小点心

  • 关于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;

}