Faster R-CNN 純C++版本 + 使用最新的caffe
本方法參照純C++版的Faster-Rcnn(通過caffe自定義RPN層實現) 進行設定更改,略有改動。
核心思想:通過新增自定義層(RPN層)代替python層,實現c++版的Faster R-CNN
Faster R-CNN 純C++版本 融合 最新caffe
(1) 新增自定義層 rpn_layer.hpp ,把它放在 caffe/include/caffe/layers/ 目錄下
#ifndef CAFFE_RPN_LAYER_HPP_
#define CAFFE_RPN_LAYER_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
//#include"opencv2/opencv.hpp"
#define mymax(a,b) ((a)>(b))?(a):(b)
#define mymin(a,b) ((a)>(b))?(b):(a)
namespace caffe {
/**
* @brief implement RPN layer for faster rcnn
*/
template <typename Dtype>
class RPNLayer : public Layer<Dtype> {
public:
explicit RPNLayer(const LayerParameter& param)
: Layer<Dtype>(param) {
m_score_.reset(new Blob<Dtype>());
m_box_.reset(new Blob<Dtype>());
local_anchors_.reset(new Blob<Dtype>());
}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top){}
virtual inline const char* type() const { return "RPN"; }
struct abox{
Dtype batch_ind;
Dtype x1;
Dtype y1;
Dtype x2;
Dtype y2;
Dtype score;
bool operator <(const abox&tmp) const{
return score < tmp.score;
}
};
protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
//virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
//const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom){};
int feat_stride_;
int base_size_;
int min_size_;
int pre_nms_topN_;
int post_nms_topN_;
float nms_thresh_;
vector<int> anchor_scales_;
vector<float> ratios_;
vector<vector<float> > gen_anchors_;
int *anchors_;
int anchors_nums_;
int src_height_;
int src_width_;
float src_scale_;
int map_width_;
int map_height_;
shared_ptr<Blob<Dtype> > m_score_;
shared_ptr<Blob<Dtype> > m_box_;
shared_ptr<Blob<Dtype> >local_anchors_;
void generate_anchors();
vector<vector<float> > ratio_enum(vector<float>);
vector<float> whctrs(vector<float>);
vector<float> mkanchor(float w,float h,float x_ctr,float y_ctr);
vector<vector<float> > scale_enum(vector<float>);
//cv::Mat proposal_local_anchor(int width, int height);
void proposal_local_anchor();
void bbox_tranform_inv();
cv::Mat bbox_tranform_inv(cv::Mat local_anchors, cv::Mat boxs_delta);
void nms(std::vector<abox> &input_boxes, float nms_thresh);
void filter_boxs(cv::Mat& pre_box, cv::Mat& score, vector<abox>& aboxes);
void filter_boxs(vector<abox>& aboxes);
};
} // namespace caffe
#endif // CAFFE_RPN_LAYER_HPP_
(2)然後是原始檔 rpn_layer.cpp, 放在 caffe/src/caffe/layers/ 目錄下
#include <algorithm>
#include <vector>
#include "caffe/layers/rpn_layer.hpp"
#include "caffe/util/math_functions.hpp"
#include <opencv2/opencv.hpp>
int debug = 0;
int tmp[9][4] = {
{ -83, -39, 100, 56 },
{ -175, -87, 192, 104 },
{ -359, -183, 376, 200 },
{ -55, -55, 72, 72 },
{ -119, -119, 136, 136 },
{ -247, -247, 264, 264 },
{ -35, -79, 52, 96 },
{ -79, -167, 96, 184 },
{ -167, -343, 184, 360 }
};
namespace caffe {
template <typename Dtype>
void RPNLayer<Dtype>::LayerSetUp(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
anchor_scales_.clear();
ratios_.clear();
feat_stride_ = this->layer_param_.rpn_param().feat_stride();
base_size_ = this->layer_param_.rpn_param().basesize();
min_size_ = this->layer_param_.rpn_param().boxminsize();
pre_nms_topN_ = this->layer_param_.rpn_param().per_nms_topn();
post_nms_topN_ = this->layer_param_.rpn_param().post_nms_topn();
nms_thresh_ = this->layer_param_.rpn_param().nms_thresh();
int scales_num = this->layer_param_.rpn_param().scale_size();
for (int i = 0; i < scales_num; ++i)
{
anchor_scales_.push_back(this->layer_param_.rpn_param().scale(i));
}
int ratios_num = this->layer_param_.rpn_param().ratio_size();
for (int i = 0; i < ratios_num; ++i)
{
ratios_.push_back(this->layer_param_.rpn_param().ratio(i));
}
//anchors_nums_ = 9;
//anchors_ = new int[anchors_nums_ * 4];
//memcpy(anchors_, tmp, 9 * 4 * sizeof(int));
generate_anchors();
anchors_nums_ = gen_anchors_.size();
anchors_ = new int[anchors_nums_ * 4];
for (int i = 0; i<gen_anchors_.size(); ++i)
{
for (int j = 0; j<gen_anchors_[i].size(); ++j)
{
anchors_[i*4+j] = gen_anchors_[i][j];
}
}
top[0]->Reshape(1, 5, 1, 1);
if (top.size() > 1)
{
top[1]->Reshape(1, 1, 1, 1);
}
}
template <typename Dtype>
void RPNLayer<Dtype>::generate_anchors(){
//generate base anchor
vector<float> base_anchor;
base_anchor.push_back(0);
base_anchor.push_back(0);
base_anchor.push_back(base_size_ - 1);
base_anchor.push_back(base_size_ - 1);
//enum ratio anchors
vector<vector<float> >ratio_anchors = ratio_enum(base_anchor);
for (int i = 0; i < ratio_anchors.size(); ++i)
{
vector<vector<float> > tmp = scale_enum(ratio_anchors[i]);
gen_anchors_.insert(gen_anchors_.end(), tmp.begin(), tmp.end());
}
}
template <typename Dtype>
vector<vector<float> > RPNLayer<Dtype>::scale_enum(vector<float> anchor){
vector<vector<float> > result;
vector<float> reform_anchor = whctrs(anchor);
float x_ctr = reform_anchor[2];
float y_ctr = reform_anchor[3];
float w = reform_anchor[0];
float h = reform_anchor[1];
for (int i = 0; i < anchor_scales_.size(); ++i)
{
float ws = w * anchor_scales_[i];
float hs = h * anchor_scales_[i];
vector<float> tmp = mkanchor(ws, hs, x_ctr, y_ctr);
result.push_back(tmp);
}
return result;
}
template <typename Dtype>
vector<vector<float> > RPNLayer<Dtype>::ratio_enum(vector<float> anchor){
vector<vector<float> > result;
vector<float> reform_anchor = whctrs(anchor);
float x_ctr = reform_anchor[2];
float y_ctr = reform_anchor[3];
float size = reform_anchor[0] * reform_anchor[1];
for (int i = 0; i < ratios_.size(); ++i)
{
float size_ratios = size / ratios_[i];
float ws = round(sqrt(size_ratios));
float hs = round(ws*ratios_[i]);
vector<float> tmp = mkanchor(ws, hs, x_ctr, y_ctr);
result.push_back(tmp);
}
return result;
}
template <typename Dtype>
vector<float> RPNLayer<Dtype>::mkanchor(float w, float h, float x_ctr, float y_ctr){
vector<float> tmp;
tmp.push_back(x_ctr - 0.5*(w - 1));
tmp.push_back(y_ctr - 0.5*(h - 1));
tmp.push_back(x_ctr + 0.5*(w - 1));
tmp.push_back(y_ctr + 0.5*(h - 1));
return tmp;
}
template <typename Dtype>
vector<float> RPNLayer<Dtype>::whctrs(vector<float> anchor){
vector<float> result;
result.push_back(anchor[2] - anchor[0] + 1); //w
result.push_back(anchor[3] - anchor[1] + 1); //h
result.push_back((anchor[2] + anchor[0]) / 2); //ctrx
result.push_back((anchor[3] + anchor[1]) / 2); //ctry
return result;
}
/*template <typename Dtype>
cv::Mat RPNLayer<Dtype>::proposal_local_anchor(int width, int height)
{
Blob<float> shift;
cv::Mat shitf_x(height, width, CV_32SC1);
cv::Mat shitf_y(height, width, CV_32SC1);
for (size_t i = 0; i < width; i++)
{
for (size_t j = 0; j < height; j++)
{
shitf_x.at<int>(j, i) = i * feat_stride_;
shitf_y.at<int>(j, i) = j * feat_stride_;
}
}
shift.Reshape(anchors_nums_, width*height, 4, 1);
float *p = shift.mutable_cpu_diff(), *a = shift.mutable_cpu_data();
for (int i = 0; i < height*width; i++)
{
for (int j = 0; j < anchors_nums_; j++)
{
size_t num = i * 4 + j * 4 * height*width;
p[num + 0] = -shitf_x.at<int>(i / shitf_x.cols, i % shitf_x.cols);
p[num + 2] = -shitf_x.at<int>(i / shitf_x.cols, i % shitf_x.cols);
p[num + 1] = -shitf_y.at<int>(i / shitf_y.cols, i % shitf_y.cols);
p[num + 3] = -shitf_y.at<int>(i / shitf_y.cols, i % shitf_y.cols);
a[num + 0] = anchors_[j * 4 + 0];
a[num + 1] = anchors_[j * 4 + 1];
a[num + 2] = anchors_[j * 4 + 2];
a[num + 3] = anchors_[j * 4 + 3];
}
}
shift.Update();
cv::Mat loacl_anchors(anchors_nums_ * height*width, 4, CV_32FC1);
size_t num = 0;
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
for (int c = 0; c < anchors_nums_; ++c)
{
for (int k = 0; k < 4; ++k)
{
loacl_anchors.at<float>((i*width + j)*anchors_nums_+c, k)= shift.data_at(c, i*width + j, k, 0);
}
}
}
}
return loacl_anchors;
}*/
template <typename Dtype>
void RPNLayer<Dtype>::proposal_local_anchor(){
int length = mymax(map_width_, map_height_);
int step = map_width_*map_height_;
int *map_m = new int[length];
for (int i = 0; i < length; ++i)
{
map_m[i] = i*feat_stride_;
}
Dtype *shift_x = new Dtype[step];
Dtype *shift_y = new Dtype[step];
for (int i = 0; i < map_height_; ++i)
{
for (int j = 0; j < map_width_; ++j)
{
shift_x[i*map_width_ + j] = map_m[j];
shift_y[i*map_width_ + j] = map_m[i];
}
}
local_anchors_->Reshape(1, anchors_nums_ * 4, map_height_, map_width_);
Dtype *a = local_anchors_->mutable_cpu_data();
for (int i = 0; i < anchors_nums_; ++i)
{
caffe_set(step, Dtype(anchors_[i * 4 + 0]), a + (i * 4 + 0) *step);
caffe_set(step, Dtype(anchors_[i * 4 + 1]), a + (i * 4 + 1) *step);
caffe_set(step, Dtype(anchors_[i * 4 + 2]), a + (i * 4 + 2) *step);
caffe_set(step, Dtype(anchors_[i * 4 + 3]), a + (i * 4 + 3) *step);
caffe_axpy(step, Dtype(1), shift_x, a + (i * 4 + 0)*step);
caffe_axpy(step, Dtype(1), shift_x, a + (i * 4 + 2)*step);
caffe_axpy(step, Dtype(1), shift_y, a + (i * 4 + 1)*step);
caffe_axpy(step, Dtype(1), shift_y, a + (i * 4 + 3)*step);
}
}
template<typename Dtype>
void RPNLayer<Dtype>::filter_boxs(cv::Mat& pre_box, cv::Mat& score, vector<abox>& aboxes)
{
float localMinSize=min_size_*src_scale_;
aboxes.clear();
for (int i = 0; i < pre_box.rows; i++)
{
int widths = pre_box.at<float>(i, 2) - pre_box.at<float>(i, 0) + 1;
int heights = pre_box.at<float>(i, 3) - pre_box.at<float>(i, 1) + 1;
if (widths >= localMinSize || heights >= localMinSize)
{
abox tmp;
tmp.x1 = pre_box.at<float>(i, 0);
tmp.y1 = pre_box.at<float>(i, 1);
tmp.x2 = pre_box.at<float>(i, 2);
tmp.y2 = pre_box.at<float>(i, 3);
tmp.score = score.at<float>(i, 0);
aboxes.push_back(tmp);
}
}
}
template<typename Dtype>
void RPNLayer<Dtype>::filter_boxs(vector<abox>& aboxes)
{
float localMinSize = min_size_*src_scale_;
aboxes.clear();
int map_width = m_box_->width();
int map_height = m_box_->height();
int map_channel = m_box_->channels();
const Dtype *box = m_box_->cpu_data();
const Dtype *score = m_score_->cpu_data();
int step = 4 * map_height*map_width;
int one_step = map_height*map_width;
int offset_w, offset_h, offset_x, offset_y, offset_s;
for (int h = 0; h < map_height; ++h)
{
for (int w = 0; w < map_width; ++w)
{
offset_x = h*map_width + w;
offset_y = offset_x + one_step;
offset_w = offset_y + one_step;
offset_h = offset_w + one_step;
offset_s = one_step*anchors_nums_+h*map_width + w;
for (int c = 0; c < map_channel / 4; ++c)
{
Dtype width = box[offset_w], height = box[offset_h];
if (width < localMinSize || height < localMinSize)
{
}
else
{
abox tmp;
tmp.batch_ind = 0;
tmp.x1 = box[offset_x] - 0.5*width;
tmp.y1 = box[offset_y] - 0.5*height;
tmp.x2 = box[offset_x] + 0.5*width;
tmp.y2 = box[offset_y] + 0.5*height;
tmp.x1 = mymin(mymax(tmp.x1, 0), src_width_);
tmp.y1 = mymin(mymax(tmp.y1, 0), src_height_);
tmp.x2 = mymin(mymax(tmp.x2, 0), src_width_);
tmp.y2 = mymin(mymax(tmp.y2, 0), src_height_);
tmp.score = score[offset_s];
aboxes.push_back(tmp);
}
offset_x += step;
offset_y += step;
offset_w += step;
offset_h += step;
offset_s += one_step;
}
}
}
}
template<typename Dtype>
void RPNLayer<Dtype>::bbox_tranform_inv(){
int channel = m_box_->channels();
int height = m_box_->height();
int width = m_box_->width();
int step = height*width;
Dtype * a = m_box_->mutable_cpu_data();
Dtype * b = local_anchors_->mutable_cpu_data();
for (int i = 0; i < channel / 4; ++i)
{
caffe_axpy(2*step, Dtype(-1), b + (i * 4 + 0)*step, b + (i * 4 + 2)*step);
caffe_add_scalar(2 * step, Dtype(1), b + (i * 4 + 2)*step);
caffe_axpy(2*step, Dtype(0.5), b + (i * 4 + 2)*step, b + (i * 4 + 0)*step);
caffe_mul(2 * step, b + (i * 4 + 2)*step, a + (i * 4 + 0)*step, a + (i * 4 + 0)*step);
caffe_add(2 * step, b + (i * 4 + 0)*step, a + (i * 4 + 0)*step, a + (i * 4 + 0)*step);
caffe_exp(2*step, a + (i * 4 + 2)*step, a + (i * 4 + 2)*step);
caffe_mul(2 * step, b + (i * 4 + 2)*step, a + (i * 4 + 2)*step, a + (i * 4 + 2)*step);
}
}
template<typename Dtype>
void RPNLayer<Dtype>::nms(std::vector<abox> &input_boxes, float nms_thresh){
std::vector<float>vArea(input_boxes.size());
for (int i = 0; i < input_boxes.size(); ++i)
{
vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1)
* (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
}
for (int i = 0; i < input_boxes.size(); ++i)
{
for (int j = i + 1; j < input_boxes.size();)
{
float xx1 = std::max(input_boxes[i].x1, input_boxes[j].x1);
float yy1 = std::max(input_boxes[i].y1, input_boxes[j].y1);
float xx2 = std::min(input_boxes[i].x2, input_boxes[j].x2);
float yy2 = std::min(input_boxes[i].y2, input_boxes[j].y2);
float w = std::max(float(0), xx2 - xx1 + 1);
float h = std::max(float(0), yy2 - yy1 + 1);
float inter = w * h;
float ovr = inter / (vArea[i] + vArea[j] - inter);
if (ovr >= nms_thresh)
{
input_boxes.erase(input_boxes.begin() + j);
vArea.erase(vArea.begin() + j);
}
else
{
j++;
}
}
}
}
template <typename Dtype>
void RPNLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
map_width_ = bottom[1]->width();
map_height_ = bottom[1]->height();
//int channels = bottom[1]->channels();
//get boxs_delta,向右。
m_box_->CopyFrom(*(bottom[1]), false, true);
/*cv::Mat boxs_delta(height*width*anchors_nums_, 4, CV_32FC1);
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
for (int k = 0; k < anchors_nums_; ++k)
{
for (int c = 0; c < 4; ++c)
{
boxs_delta.at<float>((i*width + j)*anchors_nums_ + k, c) = bottom[1]->data_at(0, k*4 + c, i, j);
}
}
}
}*/
//get sores 向右,前面anchors_nums_個位bg的得分,後面anchors_nums_為fg得分,我們需要的是後面的。
m_score_->CopyFrom(*(bottom[0]),false,true);
/*cv::Mat scores(height*width*anchors_nums_, 1, CV_32FC1);
for (int i = 0; i < height; ++i)
{
for (int j = 0; j < width; ++j)
{
for (int k = 0; k < anchors_nums_; ++k)
{
scores.at<float>((i*width + j)*anchors_nums_+k, 0) = bottom[0]->data_at(0, k + anchors_nums_, i, j);
}
}
}*/
//get im_info
src_height_ = bottom[2]->data_at(0, 0,0,0);
src_width_ = bottom[2]->data_at(0, 1,0,0);
src_scale_ = bottom[2]->data_at(0, 2, 0, 0);
//gen local anchors 向右
proposal_local_anchor();
//cv::Mat local_anchors = proposal_local_anchor(width, height);
//Convert anchors into proposals via bbox transformations
bbox_tranform_inv();
/*for (int i = 0; i < pre_box.rows; ++i)
{
if (pre_box.at<float>(i, 0) < 0) pre_box.at<float>(i, 0) = 0;
if (pre_box.at<float>(i, 0) > (src_width_ - 1)) pre_box.at<float>(i, 0) = src_width_ - 1;
if (pre_box.at<float>(i, 2) < 0) pre_box.at<float>(i, 2) = 0;
if (pre_box.at<float>(i, 2) > (src_width_ - 1)) pre_box.at<float>(i, 2) = src_width_ - 1;
if (pre_box.at<float>(i, 1) < 0) pre_box.at<float>(i, 1) = 0;
if (pre_box.at<float>(i, 1) > (src_height_ - 1)) pre_box.at<float>(i, 1) = src_height_ - 1;
if (pre_box.at<float>(i, 3) < 0) pre_box.at<float>(i, 3) = 0;
if (pre_box.at<float>(i, 3) > (src_height_ - 1)) pre_box.at<float>(i, 3) = src_height_ - 1;
}*/
vector<abox>aboxes;
filter_boxs(aboxes);
//clock_t start, end;
//start = clock();
std::sort(aboxes.rbegin(), aboxes.rend()); //降序
if (pre_nms_topN_ > 0)
{
int tmp = mymin(pre_nms_topN_, aboxes.size());
aboxes.erase(aboxes.begin() + tmp, aboxes.end());
}
nms(aboxes,nms_thresh_);
//end = clock();
//std::cout << "sort nms:" << (double)(end - start) / CLOCKS_PER_SEC << std::endl;
if (post_nms_topN_ > 0)
{
int tmp = mymin(post_nms_topN_, aboxes.size());
aboxes.erase(aboxes.begin() + tmp, aboxes.end());
}
top[0]->Reshape(aboxes.size(),5,1,1);
Dtype *top0 = top[0]->mutable_cpu_data();
for (int i = 0; i < aboxes.size(); ++i)
{
//caffe_copy(aboxes.size() * 5, (Dtype*)aboxes.data(), top0);
top0[0] = aboxes[i].batch_ind;
top0[1] = aboxes[i].x1;
top0[2] = aboxes[i].y1;
top0[3] = aboxes[i].x2;
top0[4] = aboxes[i].y2;
top0 += top[0]->offset(1);
}
if (top.size()>1)
{
top[1]->Reshape(aboxes.size(), 1,1,1);
Dtype *top1 = top[1]->mutable_cpu_data();
for (int i = 0; i < aboxes.size(); ++i)
{
top1[0] = aboxes[i].score;
top1 += top[1]->offset(1);
}
}
}
#ifdef CPU_ONLY
STUB_GPU(RPNLayer);
#endif
INSTANTIATE_CLASS(RPNLayer);
REGISTER_LAYER_CLASS(RPN);
} // namespace caffe
(3) 新增自定義層 roi pooling layer、smooth_L1_loss_layer(此層僅供訓練時使用)
注意:參照的blog中提到需要新增roi pooling層,其實py-faster-rcnn的caffe-fast-rcnn中已經含有roi pooling layer,故此步驟如果用py-faster-rcnn的caffe-fast-rcnn可以不用新增,保留原始程式碼即可。如果用最新caffe,則需做如下操作:
1、將 py-faster-rcnn/caffe-fast-rcnn/src/caffe/layers/roi_pooling_layer.cpp 、roi_pooling_layer.cu 、smooth_L1_loss_layer.cpp、smooth_L1_loss_layer.cu 四個檔案放入 caffe//src/caffe/layers/ 下
注意: roi pooling layer 用 py-faster-rcnn原本的程式碼也可以,用參照的blog裡的程式碼也可。
2、將 py-faster-rcnn/caffe-fast-rcnn/include/caffe/fast_rcnn_layers.hpp、data_reader.hpp 放入 caffe/include/caffe/ 下
3、將 py-faster-rcnn/caffe-fast-rcnn/src/caffe/data_reader.cpp 放入 caffe//src/caffe/ 下
(4)在caffe/src/caffe/proto/caffe.proto 中宣告這三個類
在message LayerParameter 中新增:
optional ROIPoolingParameter roi_pooling_param = 8266711;
optional SmoothL1LossParameter smooth_l1_loss_param = 8266712;
optional RPNParameter rpn_param = 8266713;
在這個檔案的最末尾,定義具體的引數
message ROIPoolingParameter {
optional uint32 pooled_h = 1 [default = 0];
optional uint32 pooled_w = 2 [default = 0];
optional float spatial_scale = 3 [default = 1];
}
message RPNParameter {
optional uint32 feat_stride = 1;
optional uint32 basesize = 2;
repeated uint32 scale = 3;
repeated float ratio = 4;
optional uint32 boxminsize =5;
optional uint32 per_nms_topn = 9;
optional uint32 post_nms_topn = 11;
optional float nms_thresh = 8;
}
message SmoothL1LossParameter {
// SmoothL1Loss(x) =
// 0.5 * (sigma * x) ** 2 -- if x < 1.0 / sigma / sigma
// |x| - 0.5 / sigma / sigma -- otherwise
optional float sigma = 1 [default = 1];
}
(5)因為自定義層使用了RPN層,為了以後程式中各處都能使用該層,所以得在common.hpp和common.cpp檔案的最末尾,新增對應的程式碼,注意這裡的namespace RPN是和namespace caffe同一級的
1、標頭檔案common.hpp裡新增
namespace RPN{
struct abox
{
float x1;
float y1;
float x2;
float y2;
float score;
bool operator <(const abox&tmp) const{
return score < tmp.score;
}
};
void nms(std::vector<abox>& input_boxes,float nms_thresh);
cv::Mat bbox_tranform_inv(cv::Mat, cv::Mat);
} // namespace RPN
原始檔common.cpp裡,為了防止說找不到cv::Mat型別的錯誤,新增opencv標頭檔案
#include<opencv2/opencv.hpp>
using namespace cv;
在} // namespace caffe後新增:
namespace RPN{
cv::Mat bbox_tranform_inv(cv::Mat local_anchors, cv::Mat boxs_delta){
cv::Mat pre_box(local_anchors.rows, local_anchors.cols, CV_32FC1);
for (int i = 0; i < local_anchors.rows; i++)
{
double pred_ctr_x, pred_ctr_y, src_ctr_x, src_ctr_y;
double dst_ctr_x, dst_ctr_y, dst_scl_x, dst_scl_y;
double src_w, src_h, pred_w, pred_h;
src_w = local_anchors.at<float>(i, 2) - local_anchors.at<float>(i, 0) + 1;
src_h = local_anchors.at<float>(i, 3) - local_anchors.at<float>(i, 1) + 1;
src_ctr_x = local_anchors.at<float>(i, 0) + 0.5 * src_w;
src_ctr_y = local_anchors.at<float>(i, 1) + 0.5 * src_h;
dst_ctr_x = boxs_delta.at<float>(i, 0);
dst_ctr_y = boxs_delta.at<float>(i, 1);
dst_scl_x = boxs_delta.at<float>(i, 2);
dst_scl_y = boxs_delta.at<float>(i, 3);
pred_ctr_x = dst_ctr_x*src_w + src_ctr_x;
pred_ctr_y = dst_ctr_y*src_h + src_ctr_y;
pred_w = exp(dst_scl_x) * src_w;
pred_h = exp(dst_scl_y) * src_h;
pre_box.at<float>(i, 0) = pred_ctr_x - 0.5*pred_w;
pre_box.at<float>(i, 1) = pred_ctr_y - 0.5*pred_h;
pre_box.at<float>(i, 2) = pred_ctr_x + 0.5*pred_w;
pre_box.at<float>(i, 3) = pred_ctr_y + 0.5*pred_h;
}
return pre_box;
}
void nms(std::vector<abox> &input_boxes, float nms_thresh){
std::vector<float>vArea(input_boxes.size());
for (int i = 0; i < input_boxes.size(); ++i)
{
vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1)
* (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
}
for (int i = 0; i < input_boxes.size(); ++i)
{
for (int j = i + 1; j < input_boxes.size();)
{
float xx1 = std::max(input_boxes[i].x1, input_boxes[j].x1);
float yy1 = std::max(input_boxes[i].y1, input_boxes[j].y1);
float xx2 = std::min(input_boxes[i].x2, input_boxes[j].x2);
float yy2 = std::min(input_boxes[i].y2, input_boxes[j].y2);
float w = std::max(float(0), xx2 - xx1 + 1);
float h = std::max(float(0), yy2 - yy1 + 1);
float inter = w * h;
float ovr = inter / (vArea[i] + vArea[j] - inter);
if (ovr >= nms_thresh)
{
input_boxes.erase(input_boxes.begin() + j);
vArea.erase(vArea.begin() + j);
}
else
{
j++;
}
}
}
}
}
(6) 更改caffe/src/caffe/util/blocking_queue.cpp
增加一個頭檔案
#include "caffe/data_reader.hpp"
其次,在最末尾處,將如下程式碼
template class BlockingQueue<Batch<float>*>;
template class BlockingQueue<Batch<double>*>;
更改為:
template class BlockingQueue<Batch<float>*>;
template class BlockingQueue<Batch<double>*>;
template class BlockingQueue<Datum*>;
template class BlockingQueue<shared_ptr<DataReader::QueuePair> >;
(7)重新生成caffe.pb.h
cd $caffe/include/caffe/
mkdir proto
cd $caffe/src/caffe/proto
protoc --cpp_out=$your_home/caffe/include/caffe/proto/ caffe.proto
執行完後會在$caffe/include/caffe/proto/下生成2個檔案,caffe.pb.h和caffe.pb.cc。
(8)編譯caffe
make clean
make -j8
make pycaffe (不需要用python的可以不用,反正編譯耗時也不長)
(9)環境已經配置好了,現在我們再加個類,用來對圖片進行檢測吧!編寫標頭檔案ObjectDetector.hpp
#define OBJECTDETECTOR_H
#define INPUT_SIZE_NARROW 600
#define INPUT_SIZE_LONG 1000
#include <string>
#include <caffe/net.hpp>
#include <caffe/common.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <memory>
#include <map>
using namespace std;
class ObjectDetector
{
public:
ObjectDetector(const std::string &model_file, const std::string &weights_file); //建構函式
//對一張圖片,進行檢測,將結果儲存進map資料結構裡,分別表示每個類別對應的目標框,如果需要分數資訊,則計算分數
map<int,vector<cv::Rect> > detect(const cv::Mat& image, map<int,vector<float> >* score=NULL);
private:
boost::shared_ptr< caffe::Net<float> > net_;
int class_num_; //類別數+1 ,官方給的demo 是20+1類
};
#endif
(10) 原始檔ObjectDetector.cpp
#include "ObjectDetector.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>
#include <fstream>
using std::string;
using std::vector;
using namespace caffe;
using std::max;
using std::min;
ObjectDetector::ObjectDetector(const std::string &model_file,const std::string &weights_file){
#ifdef CPU_ONLY
Caffe::set_mode(Caffe::CPU);
#else