zoukankan      html  css  js  c++  java
  • Caffe FCN:可视化featureMaps和Weights(C++)、获取FCN结果

         为何不使用C++版本FCN获取最后的分割掩模,何必要使用python呢!因此需要获取网络最后层的featureMaps,featureMaps的结果直接对应了segmentation的最终结果,可以直接用于掩模分析。

            caffe源码给出了提取中间层featureMap的源代码,位置在tools/extract_features.cpp。

            参考文章链接:  caffe模型可视化featureMaps和Weights(C++) ,文章有大量修改,如有不适,请移步原文。


    1. 可视化最后一层featureMap的代码段(稍作修改):

    int Classifier::visualize_featuremap( const cv::Mat& img, string layer_name, std::vector<cv::Mat> &Maps )
    {
    	Maps.resize(0);
    	Blob<float>* input_layer = net_->input_blobs()[0];
    	input_layer->Reshape(1, num_channels_, input_geometry_.height, input_geometry_.width);
    
    	net_->Reshape();
    
    	std::vector<cv::Mat> input_channels;
    	WrapInputLayer(&input_channels);
    
    	Preprocess(img, &input_channels);
    
    	net_->Forward();
    
    	std::cout << "网络中的Blobs名称为:
    ";
    	vector<shared_ptr<Blob<float> > > blobs = net_->blobs();
    	vector<string> blob_names = net_->blob_names();
    	std::cout << blobs.size() << " " << blob_names.size() << std::endl;
    	for (int i = 0; i < blobs.size(); i++){
    		std::cout << blob_names[i] << " " << blobs[i]->shape_string() << std::endl;
    	}
    	std::cout << std::endl;
    
    	assert(net_->has_blob(layer_name));
    	shared_ptr<Blob<float> >  conv1Blob = net_->blob_by_name(layer_name);
    	std::cout << "测试图片的特征响应图的形状信息为:" << conv1Blob->shape_string() << std::endl;
    
    	float maxValue = -10000000, minValue = 10000000;
    	const float* tmpValue = conv1Blob->cpu_data();
    	for (int i = 0; i < conv1Blob->count(); i++){
    		maxValue = std::max(maxValue, tmpValue[i]);
    		minValue = std::min(minValue, tmpValue[i]);
    	}
    
    	int width = conv1Blob->shape(3);  //响应图的高度     
    	int height = conv1Blob->shape(2);  //响应图的宽度    
    	int channel = conv1Blob->shape(1);  //通道数  
    	int num = conv1Blob->shape(0);      //个数   
    	int imgHeight = (int)(1 + sqrt(channel))*height;
    	int imgWidth = (int)(1 + sqrt(channel))*width;
    	cv::Mat img(imgHeight, imgWidth, CV_8UC1, cv::Scalar(0));
    
    	int kk = 0;
    	for (int x = 0; x < imgHeight; x += height){
    		for (int y = 0; y < imgWidth; y += width){
    			if (kk >= channel)
    				continue;
    			cv::Mat roi(height, width, CV_8UC1);
    			//cv::Mat roi = img(cv::Rect(y, x, width, height));
    			for (int i = 0; i < height; i++){
    				for (int j = 0; j < width; j++){
    					float value = conv1Blob->data_at(0, kk, i, j);//速度稍慢,应该有快速复制方法
    					//roi.at<uchar>(i, j) = (value - minValue) / (maxValue - minValue) * 255;
    					value = (value - minValue) / (maxValue - minValue);
    					roi.at<uchar>(i, j) = 255* floor(value / 0.5) ;
    				}
    			}
    			Maps.push_back(roi);
    			kk++;
    		}
    	}
    
    	return Maps.size();
    }
    

    2. 获取FCN的最终输出

    	vector<Blob<float>* >  outBlob = net_->Forward();//得到的结果仍为151个//输出结果为151个模板
    	int channel = outBlob[0]->shape(1);
    	int hi = outBlob[0]->shape(2);
    	int wi = outBlob[0]->shape(3);
    	int area = wi*hi;
    	vector<shared_ptr<Blob<float> > > blobs = net_->blobs();
    	vector<string> blob_names = net_->blob_names();

    获取最大标记

    int Classifier::GetMaxMask( const cv::Mat& img, int layerIdx, double thres,cv::Mat &maskMax )
    {
    	vector<boost::shared_ptr<Blob<float> > > blobs = net_->blobs();
    	vector<string> blob_names = net_->blob_names();
    
    	int num_features = net_->output_blobs()[0]->shape(1);
    	int channel = net_->output_blobs()[0]->shape(1);
    	int hi = net_->output_blobs()[0]->shape(2);
    	int wi = net_->output_blobs()[0]->shape(3);
    	int area = wi*hi;
    	std::vector<int> image_indices(num_features, 0);
    
    	int i = layerIdx;
    	const boost::shared_ptr<Blob<float> > feature_blob
    		= net_->blob_by_name(blob_names[i]);
    	int batch_size = feature_blob->num();
    	int dim_features = feature_blob->count() / batch_size;
    
    	float maxValue = -10000000, minValue = 10000000;
    	const float* tmpValue = feature_blob->cpu_data();
    	for (int i = 0; i < feature_blob->count(); i++){
    		maxValue = std::max(maxValue, tmpValue[i]);
    		minValue = std::min(minValue, tmpValue[i]);
    	}
    
    	std::vector<int> areal(channel);
    	for (int i = 0; i < channel;++i){
    		areal[i] = i*area;
    	}
    	const float* feature_blob_data;
    	const float minv = 10000000;
    	const float maxv = -10000000;
    	int classI = 0;
    	for ( int n = 0; n < batch_size; ++n){
    		feature_blob_data =
    			feature_blob->cpu_data() + feature_blob->offset(n);
    		int img_index = 0;
    		for (int h = 0; h < hi; ++h)
    		{
    			uchar* ptr = (unsigned char*)(maskMax.data + h * maskMax.step);
    			int idxH = h*wi;
    			img_index = idxH;
    			for ( int w = 0; w < wi; ++w)
    			{
    				float valueG = maxv;
    				for ( int c = 0; c < channel; ++c){
    					int datum_index = areal[c] + img_index;// area*c;
    					float value = static_cast<float>(feature_blob_data[datum_index]);
    					if ( valueG < value ){
    						valueG = value;
    						classI = c;
    					}
    				}
    				*ptr = (uchar)classI;
    				++ptr;
    				++img_index;
    			}
    		}
    	} 
    	return 1;
    }

    获取所有标记

    //获取特定的元,使用点数限制
    int Classifier::getAllSeg(cv::Mat &im_inp, cv::Mat  &maskMax, 
    	std::vector<cv::Mat > &segs,std::vector<std::pair<int,float> > &labels, 
    	const int nPointMin)
    {
    	std::vector<int> numsc(m_nClass);
    	int h = maskMax.rows;
    	int w = maskMax.cols;
    
    	for (int i = 0; i < maskMax.rows; ++i)
    	{
    		uchar *ptrm = maskMax.ptr<uchar>(i);
    		for (int j = 0; j < maskMax.cols; ++j)
    		{
    			int c = *ptrm;
    			numsc[c]++;
    			++ptrm;
    		}
    	}
    
    	//添加限制,获取分割图
    	std::map<int, int> maps;
    	int k = 0;
    	for (int i = 0; i < numsc.size();++i){
    		if (numsc[i]>nPointMin){
    			auto idx =make_pair(i,1.0f);
    			labels.push_back(idx);
    			auto idxm = make_pair(i, k);
    			maps.insert(idxm);
    			++k;
    		}
    	}
    
    	//获取图像
    	for (int i = 0; i < labels.size(); ++i){
    		cv::Mat seg(h, w, CV_8UC3);
    		segs.push_back(seg);
    	}
    
    	std::vector<uchar *>  ptres(labels.size());
    	for (int idx = 0; idx < labels.size(); ++idx){
    		ptres[idx] = (uchar *)segs[idx].data;
    	}
    
    	for ( int i = 0; i < maskMax.rows; ++i )
    	{
    		uchar *ptr = im_inp.ptr<uchar>(i);
    		uchar *ptrm = maskMax.ptr<uchar>(i);
    		for (int n = 0; n < labels.size(); ++n) 
    			ptres[n] = (uchar *)segs[n].ptr<uchar>(i);
    
    		for ( int j = 0; j < maskMax.cols; ++j )
    		{
    			int c = *ptrm;
    			int pos;// = maps[c];
    			auto l_it = maps.find(c);
    			if ( l_it == maps.end() )
    				pos = -1;
    			else 
    				pos = l_it->second;
    
    			if ( pos>-1) *(ptres[pos]) = *ptr;
    			++ptr;
    			for (int n = 0; n < labels.size();++n) ++ptres[n];
    			if (pos>-1) *(ptres[pos]) = *ptr;
    			++ptr;
    			for (int n = 0; n < labels.size(); ++n) ++ptres[n];
    			if (pos>-1) *(ptres[pos]) = *ptr;
    			++ptr;
    			for (int n = 0; n < labels.size(); ++n) ++ptres[n];
    			++ptrm;
    		}
    	}
    	int nseg = segs.size();
    	return nseg;
    }


    3.此外,可视化权值的代码段,直接摘抄


        cv::Mat visualize_weights(string prototxt, string caffemodel, int weights_layer_num)  
        {  
          
            ::google::InitGoogleLogging("0");  
        #ifdef CPU_ONLY  
            Caffe::set_mode(Caffe::CPU);  
        #else  
            Caffe::set_mode(Caffe::GPU);  
        #endif  
          
            Net<float> net(prototxt, TEST);  
            net.CopyTrainedLayersFrom(caffemodel);     
            vector<shared_ptr<Blob<float> > > params = net.params();      
            std::cout << "各层参数的维度信息为:
    ";  
            for (int i = 0; i<params.size(); ++i)  
                std::cout << params[i]->shape_string() << std::endl;  
          
            int width = params[weights_layer_num]->shape(3);     //宽度  
            int height = params[weights_layer_num]->shape(2);    //高度  
            int channel = params[weights_layer_num]->shape(1);       //通道数  
            int num = params[weights_layer_num]->shape(0);       //个数  
          
            int imgHeight = (int)(1 + sqrt(num))*height;      
            int imgWidth = (int)(1 + sqrt(num))*width;  
            Mat img(imgHeight, imgWidth, CV_8UC3, Scalar(0, 0, 0));  
          
            float maxValue = -1000, minValue = 10000;  
            const float* tmpValue = params[weights_layer_num]->cpu_data();    
            for (int i = 0; i<params[weights_layer_num]->count(); i++){                 
                maxValue = std::max(maxValue, tmpValue[i]);  
                minValue = std::min(minValue, tmpValue[i]);  
            }  
              
            int kk = 0;                          
            for (int y = 0; y<imgHeight; y += height){  
                for (int x = 0; x<imgWidth; x += width){  
                    if (kk >= num)  
                        continue;  
                    Mat roi = img(Rect(x, y, width, height));  
                    for (int i = 0; i<height; i++){  
                        for (int j = 0; j<width; j++){  
                            for (int k = 0; k<channel; k++){  
                                float value = params[weights_layer_num]->data_at(kk, k, i, j);  
          
                                roi.at<Vec3b>(i, j)[k] = (value - minValue) / (maxValue - minValue) * 255;                    }  
                        }  
                    }  
                    ++kk;  
                }  
            }  
          
            return img;  
        }  

    3.FeatureMap获取结果


    原图:



    分割结果显示:




    参考:经典论文 Fully Convolutional Networks for semantic Segmentation



       

           作者又翻译了一遍

         


    总结:

           pooling层的多层分布,最终用于预测每个点的类别信息,pooling层的粒度与最终分割的精度产生关联。

  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/wishchin/p/9199857.html
Copyright © 2011-2022 走看看