之前用caffe做图片分类,对caffe的代码进行了封装。为了让代码看起来尽可能简洁,对分类的类进行了封装,刚开始的封装是这样的:
classification.h如下:
#ifndef CLASSIFICATION_H_
#define CLASSIFICATION_H_
#include <caffe/caffe.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iosfwd>
#include <memory>
#include <utility>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
using namespace caffe;
using std::string;
typedef std::pair<int, float> Prediction;
class ClassifierImpl {
public:
ClassifierImpl(const string& model_file,
const string& trained_file,
const string& mean_file
);
std::vector<std::vector<Prediction> > Classify(const cv::Mat& img, int N = 2);
private:
void SetMean(const string& mean_file);
std::vector<std::vector<float> > Predict(const cv::Mat& img);
void WrapInputLayer(std::vector<cv::Mat>* input_channels);
void Preprocess(const cv::Mat& img,
std::vector<cv::Mat>* input_channels);
private:
shared_ptr<Net<float> > net_;
cv::Size input_geometry_;
int num_channels_;
cv::Mat mean_;
};
#endif
classification.cpp就是类函数的定义。
导出类的内容为:
由于导出类需要用到ClassifierImpl,那么导出类的成员变量就应该:
private:
ClassifierImpl *impl;
这样的话导出类头文件multi_recognition_gpu.h需要包含classification.h,否则ClassifierImpl 类型不明,但是这样使用dll的时候不是还需要classification.h了么?为了去掉classification.h,后来这样做:
导出类头文件multi_recognition_gpu.h
#ifndef MULTI_RECOGNITION_GPU_H_
#define MULTI_RECOGNITION_GPU_H_
#ifdef MULTI_RECOGNITION_API_EXPORTS
#define MULTI_RECOGNITION_API __declspec(dllexport)
#else
#define MULTI_RECOGNITION_API __declspec(dllimport)
#endif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <io.h>
using std::string;
using std::vector;
typedef std::pair<int, float> Prediction;
class MULTI_RECOGNITION_API MultiClassifier
{
public:
MultiClassifier(const string& model_file,
const string& trained_file,
const string& mean_file);
~MultiClassifier();
std::vector<std::vector<Prediction> >Classify(const cv::Mat& img, int N = 2);
void getFiles(std::string path, std::vector<std::string>& files);
};
#endif
#include "multi_recognition_gpu.h"
#include "classification.h"
ClassifierImpl *impl=NULL;
MultiClassifier::MultiClassifier(const string& model_file, const string& trained_file, const string& mean_file)
{
Impl = new ClassifierImpl(model_file, trained_file, mean_file);
}
MultiClassifier::~MultiClassifier()
{
delete Impl;
}
std::vector<std::vector<Prediction> > MultiClassifier::Classify(const cv::Mat& img, int N /* = 2 */)
{
return Impl->Classify(img, N);
}
这样,使用dll的时候,就只需要一个头文件了。
但是,后来发现了很严重的问题。
使用dll时,只声明一个MultiClassifier对象,是没有问题的,但是声明两个或多个,就出问题了。问题就在全局的ClassifierImpl *impl=NULL,对象之间是共享的,new第一个对象时,impl指向了它,new第二个对像时,impl会改变指向,指向第二个对象,这样,第一个对象就丢失了,不但得到的结果不是我们所需的,并且内存泄漏(第一个对象没有释放)。
后来找到了解决办法:
导出类头文件multi_recognition_gpu.h修改为:
#ifndef MULTI_RECOGNITION_GPU_H_这里,不需要include classification.h,只需要加上class ClassifierImpl;告诉编译器ClassifierImpl是一个类就行。
#define MULTI_RECOGNITION_GPU_H_
#ifdef MULTI_RECOGNITION_API_EXPORTS
#define MULTI_RECOGNITION_API __declspec(dllexport)
#else
#define MULTI_RECOGNITION_API __declspec(dllimport)
#endif
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <io.h>
class ClassifierImpl;
using std::string;
using std::vector;
typedef std::pair<int, float> Prediction;
class MULTI_RECOGNITION_API MultiClassifier
{
public:
MultiClassifier(const string& model_file,
const string& trained_file,
const string& mean_file);
~MultiClassifier();
std::vector<std::vector<Prediction> >Classify(const cv::Mat& img, int N = 2);
void getFiles(std::string path, std::vector<std::string>& files);
private:
ClassifierImpl *Impl;
};
#endif
然后导出类的cpp:
#include "multi_recognition_gpu.h"
#include "classification.h"
MultiClassifier::MultiClassifier(const string& model_file, const string& trained_file, const string& mean_file)
{
Impl = new ClassifierImpl(model_file, trained_file, mean_file);
}
MultiClassifier::~MultiClassifier()
{
delete Impl;
}
std::vector<std::vector<Prediction> > MultiClassifier::Classify(const cv::Mat& img, int N /* = 2 */)
{
return Impl->Classify(img, N);
}
就不需要全局变量了。
得到的教训是,尽量不要使用全局变量!!!!!!!!!!!!!!!!!