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名称为:\n";
	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 << "各层参数的维度信息为:\n";  
        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层的粒度与最终分割的精度产生关联。

posted @ 2018-03-09 17:45  wishchin  阅读(769)  评论(0编辑  收藏  举报