【Caffe C++】图像分类(非官方Classification.cpp)

Caffe C++接口实现图像分类(非官方Classification.cpp)

属性文件配置,请参考在下的另一篇博客~~关于vs 下caffe属性配置文件的文章。

【更新20190105】增加caffe_layer.h

本文代码使用Caffe C++接口和OpenCVC实现了使用基于Caffe的卷积神经网络的图像分类,将Caffe的模型加载,前向传播等操作封装为CaffeClassification类,main.cpp用了共享内存的方式将结果输出至内存,为了实现进程间通信而做的工作,在这里做个分享和记录:
main.cpp

#include"CaffeClassification.h"
#include <windows.h>  
#include <stdio.h> 
#include <stdlib.h>
cv::Mat GetImgMask(cv::Mat inputImage, cv::Size size);
void drawRect(cv::Mat& image, vector<cv::Rect> rects);
vector<cv::Rect> findRect(vector<vector<cv::Point>> contours);
boost::shared_ptr< Net<float> > caffe_net;
string Transform_Double2String(double number);
#define BUF_SIZE 1025  
char szName[] = "NameOfMappingObject";    // 共享内存的名字  
int main(int argc,char **argv) {
	if (argc < 6){
		cout << "输入参数不足" << endl;
	}
	// 创建共享文件句柄  
	HANDLE hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,    // 物理文件句柄  
		NULL,                    // 默认安全级别  
		PAGE_READWRITE,          // 可读可写  
		0,                       // 高位文件大小  
		BUF_SIZE,                // 地位文件大小  
		szName                   // 共享内存名称  
		);
	char *pBuf = (char *)MapViewOfFile(
		hMapFile,            // 共享内存的句柄  
		FILE_MAP_ALL_ACCESS, // 可读写许可  
		0,
		0,
		BUF_SIZE
		);
	int index = 0;
	char index_c[3];
	char class_id[3];
	_itoa(index, index_c, 10);
	string index_str;
	string message;
	//-------------------------------------------------------------
	string net_file = argv[1];
	string weight_file = argv[2];
	string mean_file = argv[3];
	string label_file = argv[4];
	string image_path = argv[5];
	//string net_file = "VGG16-deploy.prototxt";
	//string weight_file = "VGG_iter_60000.caffemodel";
	//string mean_file = "train_lmdb_128.binaryproto";
	CaffeClassification cc(net_file,weight_file,mean_file, label_file);

	fstream images;
	images.open(image_path, ios::in);
	string line;
	vector<int> inputLayerSize = cc.GetInputLayerDataSize();
	cv::Mat frame;
	cv::Mat image;
	char c = '0';
	while (getline(images,line)) {
		clock_t start, finish;
		double totaltime;
		start = clock();	 
		//cap.read(frame);

		frame = cv::imread(line);
		if (frame.empty()) {
			break;
		}
		image = cc.GetImgMask(frame, cv::Size(inputLayerSize.at(2), inputLayerSize.at(3)));
		cc.Forward(image);
		CCR ccr=cc.GetResult();
		finish = clock();
		totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
		cout << "run time =" << totaltime << "s" << endl;
		//----------------------------------------------------------
		_itoa(index++, index_c, 10);
		index_str = index_c;
		_itoa(ccr.class_id, class_id, 10);
		message = index_str + ": id=" + (string)class_id+" name="+ccr.class_name+" accuracy="+Transform_Double2String(ccr.accuracy);
		strncpy(pBuf, message.data(), BUF_SIZE - 1);
		pBuf[BUF_SIZE - 1] = '\0';
	}
	images.close();
	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
	return 0;
}
void drawRect(cv::Mat& image, vector<cv::Rect> rects) {
	if (rects.size() > 0) {
		for (int i = 0; i < rects.size(); i++) {
			if (rects.at(i).height < 30) {
				continue;
			}
			else {
				rectangle(image, rects.at(i), cv::Scalar(255, 0, 255));
				cv::Mat buf_img(image(rects.at(i)));
			}
		}
	}
}

vector<cv::Rect> findRect(vector<vector<cv::Point>> contours) {
	vector<cv::Rect> rects;
	//轮廓 预处理
	for (int i = 0; i < contours.size(); i++) {
		//求最小包围矩形
		cv::Rect rect = boundingRect(contours.at(i));
		if (rect.height > 5 && rect.width > 5) {
			rects.push_back(rect);
		}
	}
	return rects;
}
string Transform_Double2String(double number) {
	stringstream ss;
	ss << number;
	return ss.str();
}


caffe_layer.h

#include<caffe/common.hpp>
#include<caffe/layers/batch_norm_layer.hpp>
#include<caffe/layers/bn_layer.hpp>
#include<caffe/layers/bias_layer.hpp>
#include <caffe/layers/concat_layer.hpp>  
#include <caffe/layers/conv_layer.hpp>
#include <caffe/layers/dropout_layer.hpp>  
#include<caffe/layers/input_layer.hpp>
#include <caffe/layers/inner_product_layer.hpp>   
#include "caffe/layers/lrn_layer.hpp"    
#include <caffe/layers/pooling_layer.hpp>    
#include <caffe/layers/relu_layer.hpp>    
#include "caffe/layers/softmax_layer.hpp"  
#include<caffe/layers/scale_layer.hpp>
#include<caffe/layers/upsample_layer.hpp>
namespace caffe
{
	extern INSTANTIATE_CLASS(BatchNormLayer);
	extern INSTANTIATE_CLASS(BNLayer);
	extern INSTANTIATE_CLASS(BiasLayer);
	extern INSTANTIATE_CLASS(InputLayer);
	extern INSTANTIATE_CLASS(InnerProductLayer);
	extern INSTANTIATE_CLASS(DropoutLayer);
	extern INSTANTIATE_CLASS(ConvolutionLayer);
	REGISTER_LAYER_CLASS(Convolution);
	extern INSTANTIATE_CLASS(ReLULayer);
	REGISTER_LAYER_CLASS(ReLU);
	extern INSTANTIATE_CLASS(PoolingLayer);
	REGISTER_LAYER_CLASS(Pooling);
	extern INSTANTIATE_CLASS(LRNLayer);
	REGISTER_LAYER_CLASS(LRN);
	extern INSTANTIATE_CLASS(SoftmaxLayer);
	REGISTER_LAYER_CLASS(Softmax);
	extern INSTANTIATE_CLASS(ScaleLayer);
	extern INSTANTIATE_CLASS(ConcatLayer);
	extern INSTANTIATE_CLASS(UpsampleLayer);
}

CaffeClassification.hpp

#pragma once
#include<caffe.hpp>
#include <string>
#include <vector>
#include<time.h>
#include<opencv2/opencv.hpp>
#include"caffe_layer.h"
using namespace caffe;
using namespace std;
//using namespace cv;
typedef struct CCR{
	int class_id;
	string class_name;
	double accuracy;
}CCR;
class CaffeClassification
{
public:
	CaffeClassification();
	CaffeClassification(const string& model_file, const string& weight_file,const string& label_file);
	CaffeClassification(const string& model_file, const string& weight_file,const string& mean_file,const string& label_file);
	void Forward(cv::Mat image);
	vector<int> GetInputLayerDataSize();
	CCR GetResult();
	cv::Mat GetImgMask(cv::Mat inputImage, cv::Size size);
	~CaffeClassification();
	std::vector<string> labels;

private:
	boost::shared_ptr< Net<float> > caffe_net;
	//<num channel width height>
	int inputLayerSize[4];
	std::vector<float> outputLayerData;
	int outputLayerSize[4];

	int num_channels_;
	cv::Size input_geometry_;
	//图像均值
	cv::Mat mean_;
	void SetMean(const string& mean_file);
};

CaffeClassification.cpp

#include "CaffeClassification.h"
CaffeClassification::CaffeClassification()
{
}
CaffeClassification::CaffeClassification(const string& model_file, const string& weight_file, const string& label_file) {
#ifdef CPU_ONLY
	Caffe::set_mode(Caffe::CPU);
#else
	Caffe::set_mode(Caffe::GPU);
#endif
	/* Load the network. */
	caffe_net.reset(new Net<float>(model_file, TEST));
	caffe_net->CopyTrainedLayersFrom(weight_file);
	CHECK_EQ(caffe_net->num_inputs(), 1) << "Network should have exactly one input.";
	CHECK_EQ(caffe_net->num_outputs(), 1) << "Network should have exactly one output.";
	Blob<float>* input_layer = caffe_net->input_blobs()[0];
	inputLayerSize[0] = input_layer->num();
	inputLayerSize[1] = input_layer->channels();
	inputLayerSize[2] = input_layer->width();
	inputLayerSize[3] = input_layer->height();
	cout << "Data Layer Size:" << endl;
	cout << inputLayerSize[0] << " " << inputLayerSize[1] << " " << inputLayerSize[2] << " " << inputLayerSize[3] << endl;

	Blob<float>* output_layer = caffe_net->output_blobs()[0];
	outputLayerSize[0] = output_layer->num();
	outputLayerSize[1] = output_layer->channels();
	outputLayerSize[2] = output_layer->width();
	outputLayerSize[3] = output_layer->height();
	cout << "Out Layer Size:" << endl;
	cout << outputLayerSize[0] << " " << outputLayerSize[1] << " " << outputLayerSize[2] << " " << outputLayerSize[3] << endl;
}
CaffeClassification::CaffeClassification(const string& model_file, const string& weight_file, const string& mean_file, const string& label_file) {
#ifdef CPU_ONLY
	Caffe::set_mode(Caffe::CPU);
#else
	Caffe::set_mode(Caffe::GPU);
#endif

	/* Load the network. */
	caffe_net.reset(new Net<float>(model_file, TEST));
	caffe_net->CopyTrainedLayersFrom(weight_file);
	CHECK_EQ(caffe_net->num_inputs(), 1) << "Network should have exactly one input.";
	CHECK_EQ(caffe_net->num_outputs(), 1) << "Network should have exactly one output.";

	Blob<float>* input_layer = caffe_net->input_blobs()[0];
	num_channels_ = input_layer->channels();
	input_geometry_ = cv::Size(input_layer->width(), input_layer->height());
	inputLayerSize[0] = input_layer->num();
	inputLayerSize[1] = input_layer->channels();
	inputLayerSize[2] = input_layer->width();
	inputLayerSize[3] = input_layer->height();
	cout << "Data Layer Size:" << endl;
	cout << inputLayerSize[0] << " " << inputLayerSize[1] << " " << inputLayerSize[2] << " " << inputLayerSize[3] << endl;
	SetMean(mean_file);

	Blob<float>* output_layer = caffe_net->output_blobs()[0];
	outputLayerSize[0] = output_layer->num();
	outputLayerSize[1] = output_layer->channels();
	outputLayerSize[2] = output_layer->width();
	outputLayerSize[3] = output_layer->height();
	cout << "Out Layer Size:" << endl;
	cout << outputLayerSize[0] << " " << outputLayerSize[1] << " " << outputLayerSize[2] << " " << outputLayerSize[3] << endl;
	//读取label
	fstream f_label_file;
	f_label_file.open(label_file,ios::in);
	string line;
	while (getline(f_label_file,line)) {
		labels.push_back(line);
	}
}
void CaffeClassification::Forward(cv::Mat image) {
	if (image.rows != inputLayerSize[2] || image.cols != inputLayerSize[3]) {
		cout << "图片尺寸归一化,背景填充【0,0,0】" << endl;
		image = GetImgMask(image, cv::Size(inputLayerSize[2], inputLayerSize[3]));
	}
	//这个其实是为了获得net_网络的输入层数据的指针,然后后面我们直接把输入图片数据拷贝到这个指针里面
	Blob<float>* input_layer = caffe_net->input_blobs()[0];
	std::vector<cv::Mat> input_channels;
	int width = input_layer->width();
	int height = input_layer->height();
	float* input_data = input_layer->mutable_cpu_data();
	for (int i = 0; i < input_layer->channels(); ++i) {
		cv::Mat channel(height, width, CV_32FC1, input_data);
		input_channels.push_back(channel);
		input_data += width * height;
	}
	cv::Mat sample_float;
	if (input_layer->channels() == 3)
		image.convertTo(sample_float, CV_32FC3);
	//sample_float = sample_float*0.00390625;
	cv::Mat sample_normalized;
	cv::subtract(sample_float, mean_, sample_normalized);
	//imshow("sample_float", sample_float);
	cv::split(sample_normalized, input_channels);
	caffe_net->Forward();
	//把最后一层输出值,保存到vector中,结果就是返回每个类的概率  
	Blob<float>* output_layer = caffe_net->output_blobs()[0];
	const float* begin = output_layer->cpu_data();
	const float* end = begin + output_layer->channels()*output_layer->width()*output_layer->height();
	outputLayerData = std::vector<float>(begin, end);
}
CCR CaffeClassification::GetResult() {
	cv::Mat result(outputLayerSize[2], outputLayerSize[3], CV_8UC3, cv::Scalar(0, 0, 0));
	double max = 0.0;
	double min = 0.0;
	int index = 0;
	for (int i = 0; i < outputLayerData.size(); i++) {
		if (max < outputLayerData.at(i)) {
			max = outputLayerData.at(i);
			index = i;
		}
	}
	CCR ccr{ index,labels.at(index),max };
	cout << "Class ID:" << ccr.class_id << " Label Name:"<< ccr.class_name << " Accuracy:" << ccr.accuracy << endl;
	return ccr;
}
vector<int> CaffeClassification::GetInputLayerDataSize() {
	vector<int> size;
	for (int i = 0; i < 4; i++) {
		size.push_back(inputLayerSize[i]);
	}
	return size;
}
cv::Mat CaffeClassification::GetImgMask(cv::Mat inputImage, cv::Size size) {
	int s;
	if (inputImage.rows > inputImage.cols) {
		s = inputImage.rows;
	}
	else {
		s = inputImage.cols;
	}
	cv::Mat image_temp_ep(s, s, CV_8UC3, cv::Scalar(0, 0, 0));
	if (inputImage.channels() == 1) {
		cvtColor(image_temp_ep, image_temp_ep, CV_BGR2GRAY);
	}
	cv::Mat image_temp_ep_roi = image_temp_ep(cv::Rect((s - inputImage.cols) / 2, (s - inputImage.rows) / 2, inputImage.cols, inputImage.rows));
	cv::Mat dstNormImg;
	addWeighted(image_temp_ep_roi, 0., inputImage, 1.0, 0., image_temp_ep_roi);
	resize(image_temp_ep, dstNormImg, size, 0, 0, 1);    //大小归一化
	return dstNormImg;
}
CaffeClassification::~CaffeClassification()
{
}
/* Load the mean file in binaryproto format. */
void CaffeClassification::SetMean(const string& mean_file) {
	BlobProto blob_proto;
	ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);

	/* Convert from BlobProto to Blob<float> */
	Blob<float> mean_blob;
	mean_blob.FromProto(blob_proto);
	//CHECK_EQ(mean_blob.channels(), num_channels_)
	//	<< "Number of channels of mean file doesn't match input layer.";

	/* The format of the mean file is planar 32-bit float BGR or grayscale. */
	std::vector<cv::Mat> channels;
	float* data = mean_blob.mutable_cpu_data();
	for (int i = 0; i < num_channels_; ++i) {
		/* Extract an individual channel. */
		cv::Mat channel(mean_blob.height(), mean_blob.width(), CV_32FC1, data);
		channels.push_back(channel);
		data += mean_blob.height() * mean_blob.width();
	}

	/* Merge the separate channels into a single image. */
	cv::Mat mean;
	cv::merge(channels, mean);

	/* Compute the global mean pixel value and create a mean image
	* filled with this value. */
	cv::Scalar channel_mean = cv::mean(mean);
	mean_ = cv::Mat(input_geometry_, mean.type(), channel_mean);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穆友航

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值