这里介绍的是通过添加自定义层(RPN层)代替python层,实现c++版的faster-rcnn,因为去掉python了,所以部署时不会因为牵扯到python库等其它的莫名其妙的错误,使用起来就跟单纯的caffe一样,更简单方便。 核心代码,借鉴的是这篇博客,这里的话,我们不扣具体的代码细节(比如rpn层是怎么产出候选框啊,非极大值抑制是具体怎么实现的等等),有兴趣的可以自己查下资料,所以主要是走一个步骤,从而完成c++版faster-rcnn的配置。

http://blog.csdn.net/u010327085/article/details/54342070

步入正题,步骤和上面那篇博客大致一样,但它有一些细节地方直接忽略了,代码也有几处小bug,所以我把具体的流程给说下。

(1) 添加自定义层 rpn_layer.hpp  把它放在 caffe/include/caffe/layers/  目录下

[cpp] view plaincopy
  1. #ifndef CAFFE_RPN_LAYER_HPP_
  2. #define CAFFE_RPN_LAYER_HPP_
  3. #include <vector>
  4. #include "caffe/blob.hpp"
  5. #include "caffe/layer.hpp"
  6. #include "caffe/proto/caffe.pb.h"
  7. //#include"opencv2/opencv.hpp"
  8. #define mymax(a,b) ((a)>(b))?(a):(b)
  9. #define mymin(a,b) ((a)>(b))?(b):(a)
  10. namespace caffe {
  11. /**
  12. * @brief implement RPN layer for faster rcnn
  13. */
  14. template <typename Dtype>
  15. class RPNLayer : public Layer<Dtype> {
  16. public:
  17. explicit RPNLayer(const LayerParameter& param)
  18. : Layer<Dtype>(param) {
  19. m_score_.reset(new Blob<Dtype>());
  20. m_box_.reset(new Blob<Dtype>());
  21. local_anchors_.reset(new Blob<Dtype>());
  22. }
  23. virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
  24. const vector<Blob<Dtype>*>& top);
  25. virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
  26. const vector<Blob<Dtype>*>& top){}
  27. virtual inline const char* type() const { return "RPN"; }
  28. struct abox{
  29. Dtype batch_ind;
  30. Dtype x1;
  31. Dtype y1;
  32. Dtype x2;
  33. Dtype y2;
  34. Dtype score;
  35. bool operator <(const abox&tmp) const{
  36. return score < tmp.score;
  37. }
  38. };
  39. protected:
  40. virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
  41. const vector<Blob<Dtype>*>& top);
  42. //virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
  43. //const vector<Blob<Dtype>*>& top);
  44. virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
  45. const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom){};
  46. int feat_stride_;
  47. int base_size_;
  48. int min_size_;
  49. int pre_nms_topN_;
  50. int post_nms_topN_;
  51. float nms_thresh_;
  52. vector<int> anchor_scales_;
  53. vector<float> ratios_;
  54. vector<vector<float> > gen_anchors_;
  55. int *anchors_;
  56. int anchors_nums_;
  57. int src_height_;
  58. int src_width_;
  59. float src_scale_;
  60. int map_width_;
  61. int map_height_;
  62. shared_ptr<Blob<Dtype> > m_score_;
  63. shared_ptr<Blob<Dtype> > m_box_;
  64. shared_ptr<Blob<Dtype> >local_anchors_;
  65. void generate_anchors();
  66. vector<vector<float> > ratio_enum(vector<float>);
  67. vector<float> whctrs(vector<float>);
  68. vector<float> mkanchor(float w,float h,float x_ctr,float y_ctr);
  69. vector<vector<float> > scale_enum(vector<float>);
  70. //cv::Mat proposal_local_anchor(int width, int height);
  71. void proposal_local_anchor();
  72. void bbox_tranform_inv();
  73. cv::Mat bbox_tranform_inv(cv::Mat local_anchors, cv::Mat boxs_delta);
  74. void nms(std::vector<abox> &input_boxes, float nms_thresh);
  75. void filter_boxs(cv::Mat& pre_box, cv::Mat& score, vector<abox>& aboxes);
  76. void filter_boxs(vector<abox>& aboxes);
  77. };
  78. }  // namespace caffe
  79. #endif  // CAFFE_RPN_LAYER_HPP_

然后是源文件 rpn_layer.cpp  放在 caffe/src/caffe/layers/  目录下

[cpp] view plaincopy
  1. #include <algorithm>
  2. #include <vector>
  3. #include "caffe/layers/rpn_layer.hpp"
  4. #include "caffe/util/math_functions.hpp"
  5. #include <opencv2/opencv.hpp>
  6. int debug = 0;
  7. int  tmp[9][4] = {
  8. { -83, -39, 100, 56 },
  9. { -175, -87, 192, 104 },
  10. { -359, -183, 376, 200 },
  11. { -55, -55, 72, 72 },
  12. { -119, -119, 136, 136 },
  13. { -247, -247, 264, 264 },
  14. { -35, -79, 52, 96 },
  15. { -79, -167, 96, 184 },
  16. { -167, -343, 184, 360 }
  17. };
  18. namespace caffe {
  19. template <typename Dtype>
  20. void RPNLayer<Dtype>::LayerSetUp(
  21. const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  22. anchor_scales_.clear();
  23. ratios_.clear();
  24. feat_stride_ = this->layer_param_.rpn_param().feat_stride();
  25. base_size_ = this->layer_param_.rpn_param().basesize();
  26. min_size_ = this->layer_param_.rpn_param().boxminsize();
  27. pre_nms_topN_ = this->layer_param_.rpn_param().per_nms_topn();
  28. post_nms_topN_ = this->layer_param_.rpn_param().post_nms_topn();
  29. nms_thresh_ = this->layer_param_.rpn_param().nms_thresh();
  30. int scales_num = this->layer_param_.rpn_param().scale_size();
  31. for (int i = 0; i < scales_num; ++i)
  32. {
  33. anchor_scales_.push_back(this->layer_param_.rpn_param().scale(i));
  34. }
  35. int ratios_num = this->layer_param_.rpn_param().ratio_size();
  36. for (int i = 0; i < ratios_num; ++i)
  37. {
  38. ratios_.push_back(this->layer_param_.rpn_param().ratio(i));
  39. }
  40. //anchors_nums_ = 9;
  41. //anchors_ = new int[anchors_nums_ * 4];
  42. //memcpy(anchors_, tmp, 9 * 4 * sizeof(int));
  43. generate_anchors();
  44. anchors_nums_ = gen_anchors_.size();
  45. anchors_ = new int[anchors_nums_ * 4];
  46. for (int i = 0; i<gen_anchors_.size(); ++i)
  47. {
  48. for (int j = 0; j<gen_anchors_[i].size(); ++j)
  49. {
  50. anchors_[i*4+j] = gen_anchors_[i][j];
  51. }
  52. }
  53. top[0]->Reshape(1, 5, 1, 1);
  54. if (top.size() > 1)
  55. {
  56. top[1]->Reshape(1, 1, 1, 1);
  57. }
  58. }
  59. template <typename Dtype>
  60. void RPNLayer<Dtype>::generate_anchors(){
  61. //generate base anchor
  62. vector<float> base_anchor;
  63. base_anchor.push_back(0);
  64. base_anchor.push_back(0);
  65. base_anchor.push_back(base_size_ - 1);
  66. base_anchor.push_back(base_size_ - 1);
  67. //enum ratio anchors
  68. vector<vector<float> >ratio_anchors = ratio_enum(base_anchor);
  69. for (int i = 0; i < ratio_anchors.size(); ++i)
  70. {
  71. vector<vector<float> > tmp = scale_enum(ratio_anchors[i]);
  72. gen_anchors_.insert(gen_anchors_.end(), tmp.begin(), tmp.end());
  73. }
  74. }
  75. template <typename Dtype>
  76. vector<vector<float> > RPNLayer<Dtype>::scale_enum(vector<float> anchor){
  77. vector<vector<float> > result;
  78. vector<float> reform_anchor = whctrs(anchor);
  79. float x_ctr = reform_anchor[2];
  80. float y_ctr = reform_anchor[3];
  81. float w = reform_anchor[0];
  82. float h = reform_anchor[1];
  83. for (int i = 0; i < anchor_scales_.size(); ++i)
  84. {
  85. float ws = w * anchor_scales_[i];
  86. float hs = h *  anchor_scales_[i];
  87. vector<float> tmp = mkanchor(ws, hs, x_ctr, y_ctr);
  88. result.push_back(tmp);
  89. }
  90. return result;
  91. }
  92. template <typename Dtype>
  93. vector<vector<float> > RPNLayer<Dtype>::ratio_enum(vector<float> anchor){
  94. vector<vector<float> > result;
  95. vector<float> reform_anchor = whctrs(anchor);
  96. float x_ctr = reform_anchor[2];
  97. float y_ctr = reform_anchor[3];
  98. float size = reform_anchor[0] * reform_anchor[1];
  99. for (int i = 0; i < ratios_.size(); ++i)
  100. {
  101. float size_ratios = size / ratios_[i];
  102. float ws = round(sqrt(size_ratios));
  103. float hs = round(ws*ratios_[i]);
  104. vector<float> tmp = mkanchor(ws, hs, x_ctr, y_ctr);
  105. result.push_back(tmp);
  106. }
  107. return result;
  108. }
  109. template <typename Dtype>
  110. vector<float> RPNLayer<Dtype>::mkanchor(float w, float h, float x_ctr, float y_ctr){
  111. vector<float> tmp;
  112. tmp.push_back(x_ctr - 0.5*(w - 1));
  113. tmp.push_back(y_ctr - 0.5*(h - 1));
  114. tmp.push_back(x_ctr + 0.5*(w - 1));
  115. tmp.push_back(y_ctr + 0.5*(h - 1));
  116. return tmp;
  117. }
  118. template <typename Dtype>
  119. vector<float> RPNLayer<Dtype>::whctrs(vector<float> anchor){
  120. vector<float> result;
  121. result.push_back(anchor[2] - anchor[0] + 1); //w
  122. result.push_back(anchor[3] - anchor[1] + 1); //h
  123. result.push_back((anchor[2] + anchor[0]) / 2); //ctrx
  124. result.push_back((anchor[3] + anchor[1]) / 2); //ctry
  125. return result;
  126. }
  127. /*template <typename Dtype>
  128. cv::Mat RPNLayer<Dtype>::proposal_local_anchor(int width, int height)
  129. {
  130. Blob<float> shift;
  131. cv::Mat shitf_x(height, width, CV_32SC1);
  132. cv::Mat shitf_y(height, width, CV_32SC1);
  133. for (size_t i = 0; i < width; i++)
  134. {
  135. for (size_t j = 0; j < height; j++)
  136. {
  137. shitf_x.at<int>(j, i) = i * feat_stride_;
  138. shitf_y.at<int>(j, i) = j * feat_stride_;
  139. }
  140. }
  141. shift.Reshape(anchors_nums_, width*height, 4,  1);
  142. float *p = shift.mutable_cpu_diff(), *a = shift.mutable_cpu_data();
  143. for (int i = 0; i < height*width; i++)
  144. {
  145. for (int j = 0; j < anchors_nums_; j++)
  146. {
  147. size_t num = i * 4 + j * 4 * height*width;
  148. p[num + 0] = -shitf_x.at<int>(i / shitf_x.cols, i % shitf_x.cols);
  149. p[num + 2] = -shitf_x.at<int>(i / shitf_x.cols, i % shitf_x.cols);
  150. p[num + 1] = -shitf_y.at<int>(i / shitf_y.cols, i % shitf_y.cols);
  151. p[num + 3] = -shitf_y.at<int>(i / shitf_y.cols, i % shitf_y.cols);
  152. a[num + 0] = anchors_[j * 4 + 0];
  153. a[num + 1] = anchors_[j * 4 + 1];
  154. a[num + 2] = anchors_[j * 4 + 2];
  155. a[num + 3] = anchors_[j * 4 + 3];
  156. }
  157. }
  158. shift.Update();
  159. cv::Mat loacl_anchors(anchors_nums_ * height*width, 4, CV_32FC1);
  160. size_t num = 0;
  161. for (int i = 0; i < height; ++i)
  162. {
  163. for (int j = 0; j < width; ++j)
  164. {
  165. for (int c = 0; c < anchors_nums_; ++c)
  166. {
  167. for (int k = 0; k < 4; ++k)
  168. {
  169. loacl_anchors.at<float>((i*width + j)*anchors_nums_+c, k)= shift.data_at(c, i*width + j, k, 0);
  170. }
  171. }
  172. }
  173. }
  174. return loacl_anchors;
  175. }*/
  176. template <typename Dtype>
  177. void RPNLayer<Dtype>::proposal_local_anchor(){
  178. int length = mymax(map_width_, map_height_);
  179. int step = map_width_*map_height_;
  180. int *map_m = new int[length];
  181. for (int i = 0; i < length; ++i)
  182. {
  183. map_m[i] = i*feat_stride_;
  184. }
  185. Dtype *shift_x = new Dtype[step];
  186. Dtype *shift_y = new Dtype[step];
  187. for (int i = 0; i < map_height_; ++i)
  188. {
  189. for (int j = 0; j < map_width_; ++j)
  190. {
  191. shift_x[i*map_width_ + j] = map_m[j];
  192. shift_y[i*map_width_ + j] = map_m[i];
  193. }
  194. }
  195. local_anchors_->Reshape(1, anchors_nums_ * 4, map_height_, map_width_);
  196. Dtype *a = local_anchors_->mutable_cpu_data();
  197. for (int i = 0; i < anchors_nums_; ++i)
  198. {
  199. caffe_set(step, Dtype(anchors_[i * 4 + 0]), a + (i * 4 + 0) *step);
  200. caffe_set(step, Dtype(anchors_[i * 4 + 1]), a + (i * 4 + 1) *step);
  201. caffe_set(step, Dtype(anchors_[i * 4 + 2]), a + (i * 4 + 2) *step);
  202. caffe_set(step, Dtype(anchors_[i * 4 + 3]), a + (i * 4 + 3) *step);
  203. caffe_axpy(step, Dtype(1), shift_x, a + (i * 4 + 0)*step);
  204. caffe_axpy(step, Dtype(1), shift_x, a + (i * 4 + 2)*step);
  205. caffe_axpy(step, Dtype(1), shift_y, a + (i * 4 + 1)*step);
  206. caffe_axpy(step, Dtype(1), shift_y, a + (i * 4 + 3)*step);
  207. }
  208. }
  209. template<typename Dtype>
  210. void RPNLayer<Dtype>::filter_boxs(cv::Mat& pre_box, cv::Mat& score, vector<abox>& aboxes)
  211. {
  212. float localMinSize=min_size_*src_scale_;
  213. aboxes.clear();
  214. for (int i = 0; i < pre_box.rows; i++)
  215. {
  216. int widths = pre_box.at<float>(i, 2) - pre_box.at<float>(i, 0) + 1;
  217. int heights = pre_box.at<float>(i, 3) - pre_box.at<float>(i, 1) + 1;
  218. if (widths >= localMinSize || heights >= localMinSize)
  219. {
  220. abox tmp;
  221. tmp.x1 = pre_box.at<float>(i, 0);
  222. tmp.y1 = pre_box.at<float>(i, 1);
  223. tmp.x2 = pre_box.at<float>(i, 2);
  224. tmp.y2 = pre_box.at<float>(i, 3);
  225. tmp.score = score.at<float>(i, 0);
  226. aboxes.push_back(tmp);
  227. }
  228. }
  229. }
  230. template<typename Dtype>
  231. void RPNLayer<Dtype>::filter_boxs(vector<abox>& aboxes)
  232. {
  233. float localMinSize = min_size_*src_scale_;
  234. aboxes.clear();
  235. int map_width = m_box_->width();
  236. int map_height = m_box_->height();
  237. int map_channel = m_box_->channels();
  238. const Dtype *box = m_box_->cpu_data();
  239. const Dtype *score = m_score_->cpu_data();
  240. int step = 4 * map_height*map_width;
  241. int one_step = map_height*map_width;
  242. int offset_w, offset_h, offset_x, offset_y, offset_s;
  243. for (int h = 0; h < map_height; ++h)
  244. {
  245. for (int w = 0; w < map_width; ++w)
  246. {
  247. offset_x = h*map_width + w;
  248. offset_y = offset_x + one_step;
  249. offset_w = offset_y + one_step;
  250. offset_h = offset_w + one_step;
  251. offset_s = one_step*anchors_nums_+h*map_width + w;
  252. for (int c = 0; c < map_channel / 4; ++c)
  253. {
  254. Dtype width = box[offset_w], height = box[offset_h];
  255. if (width < localMinSize || height < localMinSize)
  256. {
  257. }
  258. else
  259. {
  260. abox tmp;
  261. tmp.batch_ind = 0;
  262. tmp.x1 = box[offset_x] - 0.5*width;
  263. tmp.y1 = box[offset_y] - 0.5*height;
  264. tmp.x2 = box[offset_x] + 0.5*width;
  265. tmp.y2 = box[offset_y] + 0.5*height;
  266. tmp.x1 = mymin(mymax(tmp.x1, 0), src_width_);
  267. tmp.y1 = mymin(mymax(tmp.y1, 0), src_height_);
  268. tmp.x2 = mymin(mymax(tmp.x2, 0), src_width_);
  269. tmp.y2 = mymin(mymax(tmp.y2, 0), src_height_);
  270. tmp.score = score[offset_s];
  271. aboxes.push_back(tmp);
  272. }
  273. offset_x += step;
  274. offset_y += step;
  275. offset_w += step;
  276. offset_h += step;
  277. offset_s += one_step;
  278. }
  279. }
  280. }
  281. }
  282. template<typename Dtype>
  283. void RPNLayer<Dtype>::bbox_tranform_inv(){
  284. int channel = m_box_->channels();
  285. int height = m_box_->height();
  286. int width = m_box_->width();
  287. int step = height*width;
  288. Dtype * a = m_box_->mutable_cpu_data();
  289. Dtype * b = local_anchors_->mutable_cpu_data();
  290. for (int i = 0; i < channel / 4; ++i)
  291. {
  292. caffe_axpy(2*step, Dtype(-1), b + (i * 4 + 0)*step, b + (i * 4 + 2)*step);
  293. caffe_add_scalar(2 * step, Dtype(1), b + (i * 4 + 2)*step);
  294. caffe_axpy(2*step, Dtype(0.5), b + (i * 4 + 2)*step, b + (i * 4 + 0)*step);
  295. caffe_mul(2 * step, b + (i * 4 + 2)*step, a + (i * 4 + 0)*step, a + (i * 4 + 0)*step);
  296. caffe_add(2 * step, b + (i * 4 + 0)*step, a + (i * 4 + 0)*step, a + (i * 4 + 0)*step);
  297. caffe_exp(2*step, a + (i * 4 + 2)*step, a + (i * 4 + 2)*step);
  298. caffe_mul(2 * step, b + (i * 4 + 2)*step, a + (i * 4 + 2)*step, a + (i * 4 + 2)*step);
  299. }
  300. }
  301. template<typename Dtype>
  302. void RPNLayer<Dtype>::nms(std::vector<abox> &input_boxes, float nms_thresh){
  303. std::vector<float>vArea(input_boxes.size());
  304. for (int i = 0; i < input_boxes.size(); ++i)
  305. {
  306. vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1)
  307. * (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
  308. }
  309. for (int i = 0; i < input_boxes.size(); ++i)
  310. {
  311. for (int j = i + 1; j < input_boxes.size();)
  312. {
  313. float xx1 = std::max(input_boxes[i].x1, input_boxes[j].x1);
  314. float yy1 = std::max(input_boxes[i].y1, input_boxes[j].y1);
  315. float xx2 = std::min(input_boxes[i].x2, input_boxes[j].x2);
  316. float yy2 = std::min(input_boxes[i].y2, input_boxes[j].y2);
  317. float w = std::max(float(0), xx2 - xx1 + 1);
  318. float   h = std::max(float(0), yy2 - yy1 + 1);
  319. float   inter = w * h;
  320. float ovr = inter / (vArea[i] + vArea[j] - inter);
  321. if (ovr >= nms_thresh)
  322. {
  323. input_boxes.erase(input_boxes.begin() + j);
  324. vArea.erase(vArea.begin() + j);
  325. }
  326. else
  327. {
  328. j++;
  329. }
  330. }
  331. }
  332. }
  333. template <typename Dtype>
  334. void RPNLayer<Dtype>::Forward_cpu(
  335. const vector<Blob<Dtype>*>& bottom,
  336. const vector<Blob<Dtype>*>& top) {
  337. map_width_ = bottom[1]->width();
  338. map_height_ = bottom[1]->height();
  339. //int channels = bottom[1]->channels();
  340. //get boxs_delta,向右。
  341. m_box_->CopyFrom(*(bottom[1]), false, true);
  342. /*cv::Mat boxs_delta(height*width*anchors_nums_, 4, CV_32FC1);
  343. for (int i = 0; i < height; ++i)
  344. {
  345. for (int j = 0; j < width; ++j)
  346. {
  347. for (int k = 0; k < anchors_nums_; ++k)
  348. {
  349. for (int c = 0; c < 4; ++c)
  350. {
  351. boxs_delta.at<float>((i*width + j)*anchors_nums_ + k, c) = bottom[1]->data_at(0, k*4 + c, i, j);
  352. }
  353. }
  354. }
  355. }*/
  356. //get sores 向右,前面anchors_nums_个位bg的得分,后面anchors_nums_为fg得分,我们需要的是后面的。
  357. m_score_->CopyFrom(*(bottom[0]),false,true);
  358. /*cv::Mat scores(height*width*anchors_nums_, 1, CV_32FC1);
  359. for (int i = 0; i < height; ++i)
  360. {
  361. for (int j = 0; j < width; ++j)
  362. {
  363. for (int k = 0; k < anchors_nums_; ++k)
  364. {
  365. scores.at<float>((i*width + j)*anchors_nums_+k, 0) = bottom[0]->data_at(0, k + anchors_nums_, i, j);
  366. }
  367. }
  368. }*/
  369. //get im_info
  370. src_height_ = bottom[2]->data_at(0, 0,0,0);
  371. src_width_ = bottom[2]->data_at(0, 1,0,0);
  372. src_scale_ = bottom[2]->data_at(0, 2, 0, 0);
  373. //gen local anchors 向右
  374. proposal_local_anchor();
  375. //cv::Mat local_anchors = proposal_local_anchor(width, height);
  376. //Convert anchors into proposals via bbox transformations
  377. bbox_tranform_inv();
  378. /*for (int i = 0; i < pre_box.rows; ++i)
  379. {
  380. if (pre_box.at<float>(i, 0) < 0)   pre_box.at<float>(i, 0) = 0;
  381. if (pre_box.at<float>(i, 0) > (src_width_ - 1))    pre_box.at<float>(i, 0) = src_width_ - 1;
  382. if (pre_box.at<float>(i, 2) < 0)   pre_box.at<float>(i, 2) = 0;
  383. if (pre_box.at<float>(i, 2) > (src_width_ - 1))    pre_box.at<float>(i, 2) = src_width_ - 1;
  384. if (pre_box.at<float>(i, 1) < 0)   pre_box.at<float>(i, 1) = 0;
  385. if (pre_box.at<float>(i, 1) > (src_height_ - 1))   pre_box.at<float>(i, 1) = src_height_ - 1;
  386. if (pre_box.at<float>(i, 3) < 0)   pre_box.at<float>(i, 3) = 0;
  387. if (pre_box.at<float>(i, 3) > (src_height_ - 1))   pre_box.at<float>(i, 3) = src_height_ - 1;
  388. }*/
  389. vector<abox>aboxes;
  390. filter_boxs(aboxes);
  391. //clock_t start, end;
  392. //start = clock();
  393. std::sort(aboxes.rbegin(), aboxes.rend()); //降序
  394. if (pre_nms_topN_ > 0)
  395. {
  396. int tmp = mymin(pre_nms_topN_, aboxes.size());
  397. aboxes.erase(aboxes.begin() + tmp, aboxes.end());
  398. }
  399. nms(aboxes,nms_thresh_);
  400. //end = clock();
  401. //std::cout << "sort nms:" << (double)(end - start) / CLOCKS_PER_SEC << std::endl;
  402. if (post_nms_topN_ > 0)
  403. {
  404. int tmp = mymin(post_nms_topN_, aboxes.size());
  405. aboxes.erase(aboxes.begin() + tmp, aboxes.end());
  406. }
  407. top[0]->Reshape(aboxes.size(),5,1,1);
  408. Dtype *top0 = top[0]->mutable_cpu_data();
  409. for (int i = 0; i < aboxes.size(); ++i)
  410. {
  411. //caffe_copy(aboxes.size() * 5, (Dtype*)aboxes.data(), top0);
  412. top0[0] = aboxes[i].batch_ind;
  413. top0[1] = aboxes[i].x1;
  414. top0[2] = aboxes[i].y1;
  415. top0[3] = aboxes[i].x2;
  416. top0[4] = aboxes[i].y2;
  417. top0 += top[0]->offset(1);
  418. }
  419. if (top.size()>1)
  420. {
  421. top[1]->Reshape(aboxes.size(), 1,1,1);
  422. Dtype *top1 = top[1]->mutable_cpu_data();
  423. for (int i = 0; i < aboxes.size(); ++i)
  424. {
  425. top1[0] = aboxes[i].score;
  426. top1 += top[1]->offset(1);
  427. }
  428. }
  429. }
  430. #ifdef CPU_ONLY
  431. STUB_GPU(RPNLayer);
  432. #endif
  433. INSTANTIATE_CLASS(RPNLayer);
  434. REGISTER_LAYER_CLASS(RPN);
  435. }  // namespace caffe<strong>
  436. </strong>

(2) 添加自定义层 roi_pooling_layer.hpp  把它放在 caffe/include/caffe/layers/  目录下

[cpp] view plaincopy
  1. <span style="font-size:10px;">#ifndef CAFFE_ROI_POOLING_LAYER_HPP_
  2. #define CAFFE_ROI_POOLING_LAYER_HPP_
  3. #include <vector>
  4. #include "caffe/blob.hpp"
  5. #include "caffe/common.hpp"
  6. #include "caffe/layer.hpp"
  7. #include "caffe/proto/caffe.pb.h"
  8. namespace caffe {
  9. /**
  10. * @brief Perform max pooling on regions of interest specified by input, takes
  11. *        as input N feature maps and a list of R regions of interest.
  12. *
  13. *   ROIPoolingLayer takes 2 inputs and produces 1 output. bottom[0] is
  14. *   [N x C x H x W] feature maps on which pooling is performed. bottom[1] is
  15. *   [R x 5] containing a list R ROI tuples with batch index and coordinates of
  16. *   regions of interest. Each row in bottom[1] is a ROI tuple in format
  17. *   [batch_index x1 y1 x2 y2], where batch_index corresponds to the index of
  18. *   instance in the first input and x1 y1 x2 y2 are 0-indexed coordinates
  19. *   of ROI rectangle (including its boundaries).
  20. *
  21. *   For each of the R ROIs, max-pooling is performed over pooled_h x pooled_w
  22. *   output bins (specified in roi_pooling_param). The pooling bin sizes are
  23. *   adaptively set such that they tile ROI rectangle in the indexed feature
  24. *   map. The pooling region of vertical bin ph in [0, pooled_h) is computed as
  25. *
  26. *    start_ph (included) = y1 + floor(ph * (y2 - y1 + 1) / pooled_h)
  27. *    end_ph (excluded)   = y1 + ceil((ph + 1) * (y2 - y1 + 1) / pooled_h)
  28. *
  29. *   and similar horizontal bins.
  30. *
  31. * @param param provides ROIPoolingParameter roi_pooling_param,
  32. *        with ROIPoolingLayer options:
  33. *  - pooled_h. The pooled output height.
  34. *  - pooled_w. The pooled output width
  35. *  - spatial_scale. Multiplicative spatial scale factor to translate ROI
  36. *  coordinates from their input scale to the scale used when pooling.
  37. *
  38. * Fast R-CNN
  39. * Written by Ross Girshick
  40. */
  41. template <typename Dtype>
  42. class ROIPoolingLayer : public Layer<Dtype> {
  43. public:
  44. explicit ROIPoolingLayer(const LayerParameter& param)
  45. : Layer<Dtype>(param) {}
  46. virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
  47. const vector<Blob<Dtype>*>& top);
  48. virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
  49. const vector<Blob<Dtype>*>& top);
  50. virtual inline const char* type() const { return "ROIPooling"; }
  51. virtual inline int MinBottomBlobs() const { return 2; }
  52. virtual inline int MaxBottomBlobs() const { return 2; }
  53. virtual inline int MinTopBlobs() const { return 1; }
  54. virtual inline int MaxTopBlobs() const { return 1; }
  55. protected:
  56. virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
  57. const vector<Blob<Dtype>*>& top);
  58. virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
  59. const vector<Blob<Dtype>*>& top);
  60. virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
  61. const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  62. virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
  63. const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  64. int channels_;
  65. int height_;
  66. int width_;
  67. int pooled_height_;
  68. int pooled_width_;
  69. Dtype spatial_scale_;
  70. Blob<int> max_idx_;
  71. };
  72. }  // namespace caffe
  73. #endif  // CAFFE_ROI_POOLING_LAYER_HPP_</span><span style="font-size:18px;"><strong>
  74. </strong></span>

然后是源文件 roi_pooling_layer.cpp 以及cuda版的roi_pooling_layer.cu  放在 caffe/src/caffe/layers/  目录下

[cpp] view plaincopy
  1. #include <algorithm>
  2. #include <cfloat>
  3. #include <vector>
  4. #include "caffe/layers/roi_pooling_layer.hpp"
  5. using std::max;
  6. using std::min;
  7. using std::floor;
  8. using std::ceil;
  9. namespace caffe {
  10. template <typename Dtype>
  11. void ROIPoolingLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
  12. const vector<Blob<Dtype>*>& top) {
  13. ROIPoolingParameter roi_pool_param = this->layer_param_.roi_pooling_param();
  14. CHECK_GT(roi_pool_param.pooled_h(), 0)
  15. << "pooled_h must be > 0";
  16. CHECK_GT(roi_pool_param.pooled_w(), 0)
  17. << "pooled_w must be > 0";
  18. pooled_height_ = roi_pool_param.pooled_h();
  19. pooled_width_ = roi_pool_param.pooled_w();
  20. spatial_scale_ = roi_pool_param.spatial_scale();
  21. LOG(INFO) << "Spatial scale: " << spatial_scale_;
  22. }
  23. template <typename Dtype>
  24. void ROIPoolingLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
  25. const vector<Blob<Dtype>*>& top) {
  26. channels_ = bottom[0]->channels();
  27. height_ = bottom[0]->height();
  28. width_ = bottom[0]->width();
  29. top[0]->Reshape(bottom[1]->num(), channels_, pooled_height_,
  30. pooled_width_);
  31. max_idx_.Reshape(bottom[1]->num(), channels_, pooled_height_,
  32. pooled_width_);
  33. }
  34. template <typename Dtype>
  35. void ROIPoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
  36. const vector<Blob<Dtype>*>& top) {
  37. const Dtype* bottom_data = bottom[0]->cpu_data();
  38. const Dtype* bottom_rois = bottom[1]->cpu_data();
  39. // Number of ROIs
  40. int num_rois = bottom[1]->num();
  41. int batch_size = bottom[0]->num();
  42. int top_count = top[0]->count();
  43. Dtype* top_data = top[0]->mutable_cpu_data();
  44. caffe_set(top_count, Dtype(-FLT_MAX), top_data);
  45. int* argmax_data = max_idx_.mutable_cpu_data();
  46. caffe_set(top_count, -1, argmax_data);
  47. // For each ROI R = [batch_index x1 y1 x2 y2]: max pool over R
  48. for (int n = 0; n < num_rois; ++n) {
  49. int roi_batch_ind = bottom_rois[0];
  50. int roi_start_w = round(bottom_rois[1] * spatial_scale_);
  51. int roi_start_h = round(bottom_rois[2] * spatial_scale_);
  52. int roi_end_w = round(bottom_rois[3] * spatial_scale_);
  53. int roi_end_h = round(bottom_rois[4] * spatial_scale_);
  54. CHECK_GE(roi_batch_ind, 0);
  55. CHECK_LT(roi_batch_ind, batch_size);
  56. int roi_height = max(roi_end_h - roi_start_h + 1, 1);
  57. int roi_width = max(roi_end_w - roi_start_w + 1, 1);
  58. const Dtype bin_size_h = static_cast<Dtype>(roi_height)
  59. / static_cast<Dtype>(pooled_height_);
  60. const Dtype bin_size_w = static_cast<Dtype>(roi_width)
  61. / static_cast<Dtype>(pooled_width_);
  62. const Dtype* batch_data = bottom_data + bottom[0]->offset(roi_batch_ind);
  63. for (int c = 0; c < channels_; ++c) {
  64. for (int ph = 0; ph < pooled_height_; ++ph) {
  65. for (int pw = 0; pw < pooled_width_; ++pw) {
  66. // Compute pooling region for this output unit:
  67. //  start (included) = floor(ph * roi_height / pooled_height_)
  68. //  end (excluded) = ceil((ph + 1) * roi_height / pooled_height_)
  69. int hstart = static_cast<int>(floor(static_cast<Dtype>(ph)
  70. * bin_size_h));
  71. int wstart = static_cast<int>(floor(static_cast<Dtype>(pw)
  72. * bin_size_w));
  73. int hend = static_cast<int>(ceil(static_cast<Dtype>(ph + 1)
  74. * bin_size_h));
  75. int wend = static_cast<int>(ceil(static_cast<Dtype>(pw + 1)
  76. * bin_size_w));
  77. hstart = min(max(hstart + roi_start_h, 0), height_);
  78. hend = min(max(hend + roi_start_h, 0), height_);
  79. wstart = min(max(wstart + roi_start_w, 0), width_);
  80. wend = min(max(wend + roi_start_w, 0), width_);
  81. bool is_empty = (hend <= hstart) || (wend <= wstart);
  82. const int pool_index = ph * pooled_width_ + pw;
  83. if (is_empty) {
  84. top_data[pool_index] = 0;
  85. argmax_data[pool_index] = -1;
  86. }
  87. for (int h = hstart; h < hend; ++h) {
  88. for (int w = wstart; w < wend; ++w) {
  89. const int index = h * width_ + w;
  90. if (batch_data[index] > top_data[pool_index]) {
  91. top_data[pool_index] = batch_data[index];
  92. argmax_data[pool_index] = index;
  93. }
  94. }
  95. }
  96. }
  97. }
  98. // Increment all data pointers by one channel
  99. batch_data += bottom[0]->offset(0, 1);
  100. top_data += top[0]->offset(0, 1);
  101. argmax_data += max_idx_.offset(0, 1);
  102. }
  103. // Increment ROI data pointer
  104. bottom_rois += bottom[1]->offset(1);
  105. }
  106. }
  107. template <typename Dtype>
  108. void ROIPoolingLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
  109. const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  110. if (propagate_down[1]) {
  111. LOG(FATAL) << this->type()
  112. << " Layer cannot backpropagate to roi inputs.";
  113. }
  114. if (!propagate_down[0]) {
  115. return;
  116. }
  117. const Dtype* bottom_rois = bottom[1]->cpu_data();
  118. const Dtype* top_diff = top[0]->cpu_diff();
  119. Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
  120. caffe_set(bottom[0]->count(), Dtype(0.), bottom_diff);
  121. const int* argmax_data = max_idx_.cpu_data();
  122. const int num_rois = top[0]->num();
  123. // Accumulate gradient over all ROIs
  124. for (int roi_n = 0; roi_n < num_rois; ++roi_n) {
  125. int roi_batch_ind = bottom_rois[roi_n * 5];
  126. // Accumulate gradients over each bin in this ROI
  127. for (int c = 0; c < channels_; ++c) {
  128. for (int ph = 0; ph < pooled_height_; ++ph) {
  129. for (int pw = 0; pw < pooled_width_; ++pw) {
  130. int offset_top = ((roi_n * channels_ + c) * pooled_height_ + ph)
  131. * pooled_width_ + pw;
  132. int argmax_index = argmax_data[offset_top];
  133. if (argmax_index >= 0) {
  134. int offset_bottom = (roi_batch_ind * channels_ + c) * height_
  135. * width_ + argmax_index;
  136. bottom_diff[offset_bottom] += top_diff[offset_top];
  137. }
  138. }
  139. }
  140. }
  141. }
  142. }
  143. #ifdef CPU_ONLY
  144. STUB_GPU(ROIPoolingLayer);
  145. #endif
  146. INSTANTIATE_CLASS(ROIPoolingLayer);
  147. REGISTER_LAYER_CLASS(ROIPooling);
  148. }  // namespace caffe<strong>
  149. </strong>
[cpp] view plaincopy
  1. #include <algorithm>
  2. #include <cfloat>
  3. #include <vector>
  4. #include "caffe/layers/roi_pooling_layer.hpp"
  5. using std::max;
  6. using std::min;
  7. namespace caffe {
  8. template <typename Dtype>
  9. __global__ void ROIPoolForward(const int nthreads, const Dtype* bottom_data,
  10. const Dtype spatial_scale, const int channels, const int height,
  11. const int width, const int pooled_height, const int pooled_width,
  12. const Dtype* bottom_rois, Dtype* top_data, int* argmax_data) {
  13. CUDA_KERNEL_LOOP(index, nthreads) {
  14. // (n, c, ph, pw) is an element in the pooled output
  15. int pw = index % pooled_width;
  16. int ph = (index / pooled_width) % pooled_height;
  17. int c = (index / pooled_width / pooled_height) % channels;
  18. int n = index / pooled_width / pooled_height / channels;
  19. bottom_rois += n * 5;
  20. int roi_batch_ind = bottom_rois[0];
  21. int roi_start_w = round(bottom_rois[1] * spatial_scale);
  22. int roi_start_h = round(bottom_rois[2] * spatial_scale);
  23. int roi_end_w = round(bottom_rois[3] * spatial_scale);
  24. int roi_end_h = round(bottom_rois[4] * spatial_scale);
  25. // Force malformed ROIs to be 1x1
  26. int roi_width = max(roi_end_w - roi_start_w + 1, 1);
  27. int roi_height = max(roi_end_h - roi_start_h + 1, 1);
  28. Dtype bin_size_h = static_cast<Dtype>(roi_height)
  29. / static_cast<Dtype>(pooled_height);
  30. Dtype bin_size_w = static_cast<Dtype>(roi_width)
  31. / static_cast<Dtype>(pooled_width);
  32. int hstart = static_cast<int>(floor(static_cast<Dtype>(ph)
  33. * bin_size_h));
  34. int wstart = static_cast<int>(floor(static_cast<Dtype>(pw)
  35. * bin_size_w));
  36. int hend = static_cast<int>(ceil(static_cast<Dtype>(ph + 1)
  37. * bin_size_h));
  38. int wend = static_cast<int>(ceil(static_cast<Dtype>(pw + 1)
  39. * bin_size_w));
  40. // Add roi offsets and clip to input boundaries
  41. hstart = min(max(hstart + roi_start_h, 0), height);
  42. hend = min(max(hend + roi_start_h, 0), height);
  43. wstart = min(max(wstart + roi_start_w, 0), width);
  44. wend = min(max(wend + roi_start_w, 0), width);
  45. bool is_empty = (hend <= hstart) || (wend <= wstart);
  46. // Define an empty pooling region to be zero
  47. Dtype maxval = is_empty ? 0 : -FLT_MAX;
  48. // If nothing is pooled, argmax = -1 causes nothing to be backprop'd
  49. int maxidx = -1;
  50. bottom_data += (roi_batch_ind * channels + c) * height * width;
  51. for (int h = hstart; h < hend; ++h) {
  52. for (int w = wstart; w < wend; ++w) {
  53. int bottom_index = h * width + w;
  54. if (bottom_data[bottom_index] > maxval) {
  55. maxval = bottom_data[bottom_index];
  56. maxidx = bottom_index;
  57. }
  58. }
  59. }
  60. top_data[index] = maxval;
  61. argmax_data[index] = maxidx;
  62. }
  63. }
  64. template <typename Dtype>
  65. void ROIPoolingLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
  66. const vector<Blob<Dtype>*>& top) {
  67. const Dtype* bottom_data = bottom[0]->gpu_data();
  68. const Dtype* bottom_rois = bottom[1]->gpu_data();
  69. Dtype* top_data = top[0]->mutable_gpu_data();
  70. int* argmax_data = max_idx_.mutable_gpu_data();
  71. int count = top[0]->count();
  72. // NOLINT_NEXT_LINE(whitespace/operators)
  73. ROIPoolForward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
  74. count, bottom_data, spatial_scale_, channels_, height_, width_,
  75. pooled_height_, pooled_width_, bottom_rois, top_data, argmax_data);
  76. CUDA_POST_KERNEL_CHECK;
  77. }
  78. template <typename Dtype>
  79. __global__ void ROIPoolBackward(const int nthreads, const Dtype* top_diff,
  80. const int* argmax_data, const int num_rois, const Dtype spatial_scale,
  81. const int channels, const int height, const int width,
  82. const int pooled_height, const int pooled_width, Dtype* bottom_diff,
  83. const Dtype* bottom_rois) {
  84. CUDA_KERNEL_LOOP(index, nthreads) {
  85. // (n, c, h, w) coords in bottom data
  86. int w = index % width;
  87. int h = (index / width) % height;
  88. int c = (index / width / height) % channels;
  89. int n = index / width / height / channels;
  90. Dtype gradient = 0;
  91. // Accumulate gradient over all ROIs that pooled this element
  92. for (int roi_n = 0; roi_n < num_rois; ++roi_n) {
  93. const Dtype* offset_bottom_rois = bottom_rois + roi_n * 5;
  94. int roi_batch_ind = offset_bottom_rois[0];
  95. // Skip if ROI's batch index doesn't match n
  96. if (n != roi_batch_ind) {
  97. continue;
  98. }
  99. int roi_start_w = round(offset_bottom_rois[1] * spatial_scale);
  100. int roi_start_h = round(offset_bottom_rois[2] * spatial_scale);
  101. int roi_end_w = round(offset_bottom_rois[3] * spatial_scale);
  102. int roi_end_h = round(offset_bottom_rois[4] * spatial_scale);
  103. // Skip if ROI doesn't include (h, w)
  104. const bool in_roi = (w >= roi_start_w && w <= roi_end_w &&
  105. h >= roi_start_h && h <= roi_end_h);
  106. if (!in_roi) {
  107. continue;
  108. }
  109. int offset = (roi_n * channels + c) * pooled_height * pooled_width;
  110. const Dtype* offset_top_diff = top_diff + offset;
  111. const int* offset_argmax_data = argmax_data + offset;
  112. // Compute feasible set of pooled units that could have pooled
  113. // this bottom unit
  114. // Force malformed ROIs to be 1x1
  115. int roi_width = max(roi_end_w - roi_start_w + 1, 1);
  116. int roi_height = max(roi_end_h - roi_start_h + 1, 1);
  117. Dtype bin_size_h = static_cast<Dtype>(roi_height)
  118. / static_cast<Dtype>(pooled_height);
  119. Dtype bin_size_w = static_cast<Dtype>(roi_width)
  120. / static_cast<Dtype>(pooled_width);
  121. int phstart = floor(static_cast<Dtype>(h - roi_start_h) / bin_size_h);
  122. int phend = ceil(static_cast<Dtype>(h - roi_start_h + 1) / bin_size_h);
  123. int pwstart = floor(static_cast<Dtype>(w - roi_start_w) / bin_size_w);
  124. int pwend = ceil(static_cast<Dtype>(w - roi_start_w + 1) / bin_size_w);
  125. phstart = min(max(phstart, 0), pooled_height);
  126. phend = min(max(phend, 0), pooled_height);
  127. pwstart = min(max(pwstart, 0), pooled_width);
  128. pwend = min(max(pwend, 0), pooled_width);
  129. for (int ph = phstart; ph < phend; ++ph) {
  130. for (int pw = pwstart; pw < pwend; ++pw) {
  131. if (offset_argmax_data[ph * pooled_width + pw] == (h * width + w)) {
  132. gradient += offset_top_diff[ph * pooled_width + pw];
  133. }
  134. }
  135. }
  136. }
  137. bottom_diff[index] = gradient;
  138. }
  139. }
  140. template <typename Dtype>
  141. void ROIPoolingLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
  142. const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  143. if (!propagate_down[0]) {
  144. return;
  145. }
  146. const Dtype* bottom_rois = bottom[1]->gpu_data();
  147. const Dtype* top_diff = top[0]->gpu_diff();
  148. Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();
  149. const int count = bottom[0]->count();
  150. caffe_gpu_set(count, Dtype(0.), bottom_diff);
  151. const int* argmax_data = max_idx_.gpu_data();
  152. // NOLINT_NEXT_LINE(whitespace/operators)
  153. ROIPoolBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
  154. count, top_diff, argmax_data, top[0]->num(), spatial_scale_, channels_,
  155. height_, width_, pooled_height_, pooled_width_, bottom_diff, bottom_rois);
  156. CUDA_POST_KERNEL_CHECK;
  157. }
  158. INSTANTIATE_LAYER_GPU_FUNCS(ROIPoolingLayer);
  159. }  // namespace caffe

(3) 好了,代码添加完毕,现在在caffe/src/caffe/proto/caffe.proto 中声明这两个类

根据你自己的可用ID 在message Layer中添加这两个类,我的已经添加了,大概是这样的,千万记住大小写!

[cpp] view plaincopy
  1. // NOTE
  2. // Update the next available ID when you add a new LayerParameter field.
  3. // LayerParameter next available layer-specific ID: 152 (last added: rpn_param roi_pooling_param)
[cpp] view plaincopy
  1. optional RPNParameter rpn_param = 150;                  //
  2. optional ROIPoolingParameter roi_pooling_param = 151;       // roi pooling  Faster-Rcnn

这里写好后,因为这两个层都有内置的参数,还得在这个文件的最末尾,定义具体的参数

[cpp] view plaincopy
  1. message ROIPoolingParameter {
  2. optional uint32 pooled_h = 1 [default = 0];
  3. optional uint32 pooled_w = 2 [default = 0];
  4. optional float spatial_scale = 3 [default = 1];
  5. }
  6. message RPNParameter {
  7. optional uint32 feat_stride = 1;
  8. optional uint32 basesize = 2;
  9. repeated uint32 scale = 3;
  10. repeated float ratio = 4;
  11. optional uint32 boxminsize =5;
  12. optional uint32 per_nms_topn = 9;
  13. optional uint32 post_nms_topn = 11;
  14. optional float nms_thresh = 8;
  15. }

(4) 因为自定义层使用了RPN层,为了以后程序中各处都能使用该层,所以得在common.hpp和common.cpp文件的最末尾,添加对应的代码,注意这里的namespace RPN是和namespace caffe同一级的

头文件common.hpp里添加

[cpp] view plaincopy
  1. namespace RPN{
  2. struct abox
  3. {
  4. float x1;
  5. float y1;
  6. float x2;
  7. float y2;
  8. float score;
  9. bool operator <(const abox&tmp) const{
  10. return score < tmp.score;
  11. }
  12. };
  13. void nms(std::vector<abox>& input_boxes,float nms_thresh);
  14. cv::Mat bbox_tranform_inv(cv::Mat, cv::Mat);
  15. } // namespace RPN

源文件common.cpp里,为了防止说找不到cv::Mat类型的错误,添加opencv头文件

#include<opencv2/opencv.hpp>

using namespace cv;

[cpp] view plaincopy
  1. namespace RPN{
  2. cv::Mat bbox_tranform_inv(cv::Mat local_anchors, cv::Mat boxs_delta){
  3. cv::Mat pre_box(local_anchors.rows, local_anchors.cols, CV_32FC1);
  4. for (int i = 0; i < local_anchors.rows; i++)
  5. {
  6. double pred_ctr_x, pred_ctr_y, src_ctr_x, src_ctr_y;
  7. double dst_ctr_x, dst_ctr_y, dst_scl_x, dst_scl_y;
  8. double src_w, src_h, pred_w, pred_h;
  9. src_w = local_anchors.at<float>(i, 2) - local_anchors.at<float>(i, 0) + 1;
  10. src_h = local_anchors.at<float>(i, 3) - local_anchors.at<float>(i, 1) + 1;
  11. src_ctr_x = local_anchors.at<float>(i, 0) + 0.5 * src_w;
  12. src_ctr_y = local_anchors.at<float>(i, 1) + 0.5 * src_h;
  13. dst_ctr_x = boxs_delta.at<float>(i, 0);
  14. dst_ctr_y = boxs_delta.at<float>(i, 1);
  15. dst_scl_x = boxs_delta.at<float>(i, 2);
  16. dst_scl_y = boxs_delta.at<float>(i, 3);
  17. pred_ctr_x = dst_ctr_x*src_w + src_ctr_x;
  18. pred_ctr_y = dst_ctr_y*src_h + src_ctr_y;
  19. pred_w = exp(dst_scl_x) * src_w;
  20. pred_h = exp(dst_scl_y) * src_h;
  21. pre_box.at<float>(i, 0) = pred_ctr_x - 0.5*pred_w;
  22. pre_box.at<float>(i, 1) = pred_ctr_y - 0.5*pred_h;
  23. pre_box.at<float>(i, 2) = pred_ctr_x + 0.5*pred_w;
  24. pre_box.at<float>(i, 3) = pred_ctr_y + 0.5*pred_h;
  25. }
  26. return pre_box;
  27. }
  28. void nms(std::vector<abox> &input_boxes, float nms_thresh){
  29. std::vector<float>vArea(input_boxes.size());
  30. for (int i = 0; i < input_boxes.size(); ++i)
  31. {
  32. vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1)
  33. * (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
  34. }
  35. for (int i = 0; i < input_boxes.size(); ++i)
  36. {
  37. for (int j = i + 1; j < input_boxes.size();)
  38. {
  39. float xx1 = std::max(input_boxes[i].x1, input_boxes[j].x1);
  40. float yy1 = std::max(input_boxes[i].y1, input_boxes[j].y1);
  41. float xx2 = std::min(input_boxes[i].x2, input_boxes[j].x2);
  42. float yy2 = std::min(input_boxes[i].y2, input_boxes[j].y2);
  43. float w = std::max(float(0), xx2 - xx1 + 1);
  44. float   h = std::max(float(0), yy2 - yy1 + 1);
  45. float   inter = w * h;
  46. float ovr = inter / (vArea[i] + vArea[j] - inter);
  47. if (ovr >= nms_thresh)
  48. {
  49. input_boxes.erase(input_boxes.begin() + j);
  50. vArea.erase(vArea.begin() + j);
  51. }
  52. else
  53. {
  54. j++;
  55. }
  56. }
  57. }
  58. }
  59. }

(5)好了,配置弄完了,回到caffe根目录下,

make clean

make all -j

开始编译吧!

可能会出现什么找不到pb.h文件什么的,那就继续执行 make -j5  可能是因为编译的线程太多导致先后顺序什么的。 我也是猜的,反正我是这么解决的。

(6)环境已经配置好了,现在我们再加个类,用来对图片进行检测吧!编写头文件ObjectDetector.hpp

[cpp] view plaincopy
  1. <span style="font-size:10px;">#ifndef OBJECTDETECTOR_H
  2. #define OBJECTDETECTOR_H
  3. #define INPUT_SIZE_NARROW  600
  4. #define INPUT_SIZE_LONG  1000
  5. #include <string>
  6. #include <caffe/net.hpp>
  7. #include <caffe/common.hpp>
  8. #include <opencv2/core/core.hpp>
  9. #include <iostream>
  10. #include <memory>
  11. #include <map>
  12. using namespace std;
  13. class ObjectDetector
  14. {
  15. public:
  16. ObjectDetector(const std::string &model_file, const std::string &weights_file);  //构造函数
  17. //对一张图片,进行检测,将结果保存进map数据结构里,分别表示每个类别对应的目标框,如果需要分数信息,则计算分数
  18. map<int,vector<cv::Rect> > detect(const cv::Mat& image, map<int,vector<float> >* score=NULL);
  19. private:
  20. boost::shared_ptr< caffe::Net<float> > net_;
  21. int class_num_;     //类别数+1   ,官方给的demo 是20+1类
  22. };
  23. #endif<strong>
  24. </strong></span>

源文件ObjectDetector.cpp

[cpp] view plaincopy
  1. #include "ObjectDetector.hpp"
  2. #include <opencv2/highgui/highgui.hpp>
  3. #include <opencv2/imgproc/imgproc.hpp>
  4. #include <vector>
  5. #include <fstream>
  6. using std::string;
  7. using std::vector;
  8. using namespace caffe;
  9. using  std::max;
  10. using std::min;
  11. ObjectDetector::ObjectDetector(const std::string &model_file,const std::string &weights_file){
  12. #ifdef CPU_ONLY
  13. Caffe::set_mode(Caffe::CPU);
  14. #else
  15. Caffe::set_mode(Caffe::GPU);
  16. #endif
  17. net_.reset(new Net<float>(model_file, TEST));
  18. net_->CopyTrainedLayersFrom(weights_file);
  19. this->class_num_ = net_->blob_by_name("cls_prob")->channels();  //求得类别数+1
  20. }
  21. //对一张图片,进行检测,将结果保存进map数据结构里,分别表示每个类别对应的目标框,如果需要分数信息,则计算分数
  22. map<int,vector<cv::Rect> > ObjectDetector::detect(const cv::Mat& image,map<int,vector<float> >* objectScore){
  23. if(objectScore!=NULL)   //如果需要保存置信度
  24. objectScore->clear();
  25. float CONF_THRESH = 0.8;  //置信度阈值
  26. float NMS_THRESH = 0.3;   //非极大值抑制阈值
  27. int max_side = max(image.rows, image.cols);   //分别求出图片宽和高的较大者
  28. int min_side = min(image.rows, image.cols);
  29. float max_side_scale = float(max_side) / float(INPUT_SIZE_LONG);    //分别求出缩放因子
  30. float min_side_scale = float(min_side) / float(INPUT_SIZE_NARROW);
  31. float max_scale = max(max_side_scale, min_side_scale);
  32. float img_scale = float(1) / max_scale;
  33. int height = int(image.rows * img_scale);
  34. int width = int(image.cols * img_scale);
  35. int num_out;
  36. cv::Mat cv_resized;
  37. image.convertTo(cv_resized, CV_32FC3);
  38. cv::resize(cv_resized, cv_resized, cv::Size(width, height));
  39. cv::Mat mean(height, width, cv_resized.type(), cv::Scalar(102.9801, 115.9465, 122.7717));
  40. cv::Mat normalized;
  41. subtract(cv_resized, mean, normalized);
  42. float im_info[3];
  43. im_info[0] = height;
  44. im_info[1] = width;
  45. im_info[2] = img_scale;
  46. shared_ptr<Blob<float> > input_layer = net_->blob_by_name("data");
  47. input_layer->Reshape(1, normalized.channels(), height, width);
  48. net_->Reshape();
  49. float* input_data = input_layer->mutable_cpu_data();
  50. vector<cv::Mat> input_channels;
  51. for (int i = 0; i < input_layer->channels(); ++i) {
  52. cv::Mat channel(height, width, CV_32FC1, input_data);
  53. input_channels.push_back(channel);
  54. input_data += height * width;
  55. }
  56. cv::split(normalized, input_channels);
  57. net_->blob_by_name("im_info")->set_cpu_data(im_info);
  58. net_->Forward();                                       //进行网络前向传播
  59. int num = net_->blob_by_name("rois")->num();    //产生的 ROI 个数,比如为 13949个ROI
  60. const float *rois_data = net_->blob_by_name("rois")->cpu_data();    //维度比如为:13949*5*1*1
  61. int num1 = net_->blob_by_name("bbox_pred")->num();   //预测的矩形框 维度为 13949*84
  62. cv::Mat rois_box(num, 4, CV_32FC1);
  63. for (int i = 0; i < num; ++i)
  64. {
  65. rois_box.at<float>(i, 0) = rois_data[i * 5 + 1] / img_scale;
  66. rois_box.at<float>(i, 1) = rois_data[i * 5 + 2] / img_scale;
  67. rois_box.at<float>(i, 2) = rois_data[i * 5 + 3] / img_scale;
  68. rois_box.at<float>(i, 3) = rois_data[i * 5 + 4] / img_scale;
  69. }
  70. shared_ptr<Blob<float> > bbox_delt_data = net_->blob_by_name("bbox_pred");   // 13949*84
  71. shared_ptr<Blob<float> > score = net_->blob_by_name("cls_prob");             // 3949*21
  72. map<int,vector<cv::Rect> > label_objs;    //每个类别,对应的检测目标框
  73. for (int i = 1; i < class_num_; ++i){     //对每个类,进行遍历
  74. cv::Mat bbox_delt(num, 4, CV_32FC1);
  75. for (int j = 0; j < num; ++j){
  76. bbox_delt.at<float>(j, 0) = bbox_delt_data->data_at(j, i * 4 + 0, 0, 0);
  77. bbox_delt.at<float>(j, 1) = bbox_delt_data->data_at(j, i * 4 + 1, 0, 0);
  78. bbox_delt.at<float>(j, 2) = bbox_delt_data->data_at(j, i * 4 + 2, 0, 0);
  79. bbox_delt.at<float>(j, 3) = bbox_delt_data->data_at(j, i * 4 + 3, 0, 0);
  80. }
  81. cv::Mat box_class = RPN::bbox_tranform_inv(rois_box, bbox_delt);
  82. vector<RPN::abox> aboxes;   //对于 类别i,检测出的矩形框保存在这
  83. for (int j = 0; j < box_class.rows; ++j){
  84. if (box_class.at<float>(j, 0) < 0)  box_class.at<float>(j, 0) = 0;
  85. if (box_class.at<float>(j, 0) > (image.cols - 1))   box_class.at<float>(j, 0) = image.cols - 1;
  86. if (box_class.at<float>(j, 2) < 0)  box_class.at<float>(j, 2) = 0;
  87. if (box_class.at<float>(j, 2) > (image.cols - 1))   box_class.at<float>(j, 2) = image.cols - 1;
  88. if (box_class.at<float>(j, 1) < 0)  box_class.at<float>(j, 1) = 0;
  89. if (box_class.at<float>(j, 1) > (image.rows - 1))   box_class.at<float>(j, 1) = image.rows - 1;
  90. if (box_class.at<float>(j, 3) < 0)  box_class.at<float>(j, 3) = 0;
  91. if (box_class.at<float>(j, 3) > (image.rows - 1))   box_class.at<float>(j, 3) = image.rows - 1;
  92. RPN::abox tmp;
  93. tmp.x1 = box_class.at<float>(j, 0);
  94. tmp.y1 = box_class.at<float>(j, 1);
  95. tmp.x2 = box_class.at<float>(j, 2);
  96. tmp.y2 = box_class.at<float>(j, 3);
  97. tmp.score = score->data_at(j, i, 0, 0);
  98. aboxes.push_back(tmp);
  99. }
  100. std::sort(aboxes.rbegin(), aboxes.rend());
  101. RPN::nms(aboxes, NMS_THRESH);  //与非极大值抑制消除对于的矩形框
  102. for (int k = 0; k < aboxes.size();){
  103. if (aboxes[k].score < CONF_THRESH)
  104. aboxes.erase(aboxes.begin() + k);
  105. else
  106. k++;
  107. }
  108. //################ 将类别i的所有检测框,保存
  109. vector<cv::Rect> rect(aboxes.size());    //对于类别i,检测出的矩形框
  110. for(int ii=0;ii<aboxes.size();++ii)
  111. rect[ii]=cv::Rect(cv::Point(aboxes[ii].x1,aboxes[ii].y1),cv::Point(aboxes[ii].x2,aboxes[ii].y2));
  112. label_objs[i]=rect;
  113. //################ 将类别i的所有检测框的打分,保存
  114. if(objectScore!=NULL){           //################ 将类别i的所有检测框的打分,保存
  115. vector<float> tmp(aboxes.size());       //对于 类别i,检测出的矩形框的得分
  116. for(int ii=0;ii<aboxes.size();++ii)
  117. tmp[ii]=aboxes[ii].score;
  118. objectScore->insert(pair<int,vector<float> >(i,tmp));
  119. }
  120. }
  121. return label_objs;
  122. }

这里的代码,是在参考博客中的代码,我改了下,加了自己的需求。这里的函数返回的是一个map对象,每一个键(类别label),对应一个矩形框向量。比如,一个20类检测任务,而一张图片里有3个人(标签是1),和2辆车(标签是5),那函数会返回一个map,其中有两个键值对,键1对应的值是一个3维的矩形框向量,分别代表着3个人的矩形框;键5对应的值是一个2维的矩形框向量,分别代表的是2辆车的矩形框。同时,函数还接受一个可选参数,可以返回每个矩形框各自对应的置信度。

Ok,现在我们写个主函数,测试下效果吧,我们建个文件夹,首先把网络描述文件test.prototxt拷贝过来,这里我用的是VGG16的,end2end的网络,路径是py-faster-rcnn/models/pascal_voc/VGG16/faster_rcnn_end2end/test.prototxt,Ok,拷贝过来,因为我们不需要python层了,那我们打开这个文件,定位到 Python层,

[cpp] view plaincopy
  1. layer {
  2. name: 'proposal'
  3. type: 'Python'
  4. bottom: 'rpn_cls_prob_reshape'
  5. bottom: 'rpn_bbox_pred'
  6. bottom: 'im_info'
  7. top: 'rois'
  8. python_param {
  9. module: 'rpn.proposal_layer'
  10. layer: 'ProposalLayer'
  11. param_str: "'feat_stride': 16"
  12. }
  13. }

把它修改为

[cpp] view plaincopy
  1. layer {
  2. name: "proposal"
  3. type: "RPN"
  4. bottom: "rpn_cls_prob_reshape"
  5. bottom: "rpn_bbox_pred"
  6. bottom: "im_info"
  7. top: "rois"
  8. rpn_param {
  9. feat_stride : 16
  10. basesize : 16
  11. scale : 8
  12. scale : 16
  13. scale : 32
  14. ratio : 0.5
  15. ratio : 1
  16. ratio : 2
  17. boxminsize :16
  18. per_nms_topn : 0;
  19. post_nms_topn : 0;
  20. nms_thresh : 0.3
  21. }
  22. }

是的,这里的一系列参数,可以自己设置的,大家可以尝试下

然后,我们需要一个已经训练好的检测caffemodel,这里我直接拿示例的20类demo的caffemodel,也把它拷贝到我们的文件夹下,万事俱备,只欠东风了! 赶紧编写个主函数进行测试吧,我的示例如下:

[cpp] view plaincopy
  1. #include "ObjectDetector.hpp"
  2. #include<opencv2/opencv.hpp>
  3. #include<iostream>
  4. #include<sstream>
  5. using namespace cv;
  6. using namespace std;
  7. string num2str(float i){
  8. stringstream ss;
  9. ss<<i;
  10. return ss.str();
  11. }
  12. int main(int argc,char **argv){
  13. ::google::InitGoogleLogging(argv[0]);
  14. #ifdef CPU_ONLY
  15. cout<<"Use CPU\n";
  16. #else
  17. cout<<"Use GPU\n";
  18. #endif
  19. ObjectDetector detect("test.prototxt","1.caffemodel");
  20. Mat img=imread("1.jpg");
  21. map<int,vector<float> > score;
  22. map<int,vector<Rect> > label_objs=detect.detect(img,&score);  //目标检测,同时保存每个框的置信度
  23. for(map<int,vector<Rect> >::iterator it=label_objs.begin();it!=label_objs.end();it++){
  24. int label=it->first;  //标签
  25. vector<Rect> rects=it->second;  //检测框
  26. for(int j=0;j<rects.size();j++){
  27. rectangle(img,rects[j],Scalar(0,0,255),2);   //画出矩形框
  28. string txt=num2str(label)+" : "+num2str(score[label][j]);
  29. putText(img,txt,Point(rects[j].x,rects[j].y),CV_FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0)); //标记 类别:置信度
  30. }
  31. }
  32. imshow("", img);
  33. waitKey();
  34. return 0;
  35. }

好了,这里网络描述文件是 test.prototxt,调用的是caffemodel是官方示例的model,我这为了简单,改名1.caffemodel了, 对图片1.jpg进行测试, 现在编译main.cpp 文件,命令如下:

[cpp] view plaincopy
  1. app.bin: main.cpp ObjectDetector.cpp
  2. g++ -o app.bin main.cpp ObjectDetector.cpp -I /home/*****/caffe/include/ -I /home/*****/caffe/.build_release/src/ -I /usr/local/cuda-8.0/include/ `pkg-config --libs --cflags opencv` -L /home/****/caffe/build/lib/ -lcaffe -lglog -lboost_system -lprotobuf

具体路径参照自己的就好,生成app.bin可执行文件,运行,我们对一张图片进行测试,原图如下

检测后,如下:

这里为了方便,我直接输出的标签号以及对应的置信度了。可以看出 ,飞机的的label为1,船的label是4,我们从python版的demo.py中可以证实这点:

[cpp] view plaincopy
  1. CLASSES = ('__background__',
  2. 'aeroplane', 'bicycle', 'bird', 'boat',
  3. 'bottle', 'bus', 'car', 'cat', 'chair',
  4. 'cow', 'diningtable', 'dog', 'horse',
  5. 'motorbike', 'person', 'pottedplant',
  6. 'sheep', 'sofa', 'train', 'tvmonitor')

OK,大功告成啦~~~

纯C++版的Faster-Rcnn(通过caffe自定义RPN层实现)相关推荐

  1. 目标检测经典论文——Faster R-CNN论文翻译(纯中文版):Faster R-CNN:通过Region Proposal网络实现实时目标检测

    目标检测经典论文翻译汇总:[翻译汇总] 翻译pdf文件下载:[下载地址] 此版为纯中文版,中英文对照版请稳步:[Faster R-CNN中英文对照版] Faster R-CNN:通过Region Pr ...

  2. 记录配置faster rcnn(caffe)CPU版本遇到的问题

    运行Faster-Rcnn代码 与Faster-Rcnn一样Faster-Rcnn官方也是采用caffe作为框架. 首先将项目克隆到本地(需要挂代理) Make sure to clone with ...

  3. faster rcnn windows 下c++版本

    faster rcnn windows 下c++版本 参考了http://www.cnblogs.com/louyihang-loves-baiyan/p/5485955.html,和http://b ...

  4. (目标检测)Faster R-CNN 论文解读+复现

    Faster R-CNN xyang 声明:本篇文章借用了他人理解,如有侵权,请联系,另如需转载,请注明出处 关于最新最全的目标检测论文,可以查看awesome-object-detection &l ...

  5. Faster R-CNN改进篇(一): ION ● HyperNet ● MS CNN

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/linolzhang/article/details/74159463 一. 源起于Faster 深度 ...

  6. Faster R-CNN论文及源码解读

    R-CNN是目标检测领域中十分经典的方法,相比于传统的手工特征,R-CNN将卷积神经网络引入,用于提取深度特征,后接一个分类器判决搜索区域是否包含目标及其置信度,取得了较为准确的检测结果.Fast R ...

  7. 最详细的Faster RCNN论文笔记

    个人博客:http://www.chenjianqu.com/ 原文链接:http://www.chenjianqu.com/show-76.html 论文:Shaoqing Ren, Kaiming ...

  8. 目标检测 -- R-CNN,Fast R-CNN,Faster R-CNN

    R-CNN,Fast R-CNN,Faster R-CNN这些是深度学习目标检测的鼻祖.看各种博客分析,东看看西看看,不系统.这里准备系统的记录一下深度学习目标检测的发展史.这里大部分摘录其他博客.参 ...

  9. 目标检测——Faster R-CNN论文阅读

    论文阅读--Faster R-CNN:Towards Real-Time Object Detection with Region Proposal Networks 文章目录 论文阅读--Faste ...

最新文章

  1. 32位postman_谷歌浏览器下载安装postman教程(详细)
  2. 学习编程做笔记的软件_可以在图片上做笔记的软件
  3. python之WEB开发:图片管理
  4. pragma comment的使用 pragma预处理指令详解
  5. 次世代手游美术资源优化干货分享
  6. tf.layers.dropout
  7. samba安装_Centos安装Samba
  8. 00后确实卷,公司新来的卷王,我们这帮老油条真干不过.....
  9. Gesture APIs-Furthering Windows Mobile 6.5 Touch Gesture Framework
  10. typora 语法教程
  11. 程序员的奋斗史(三十三)——人在囧途之应聘篇(三)
  12. 【官方文档】Fluent Bit 数据管道之输入插件(Tail)
  13. 焦点弦的垂直平分线和轴的交点到焦点的距离和焦点弦的距离之比为二分之e
  14. vue3.0 结合element ui 开发后台ui框架
  15. python中def fun()是什么意思_python学习函数
  16. php 跳转邮箱,实例详解JS简单实现点击跳转登陆邮箱功能的方法
  17. 四超多强 一文看懂中国CV独角兽格局
  18. 20145212 罗天晨 《网络对抗》Exp3 Advanced 恶意代码伪装技术实践
  19. 计算机上海交大考研有学硕吗,2021上海交大考研:学硕专硕的区别
  20. android 广告库sdk,GitHub - adxdata/sdk-android-demo: 美数广告SDK(Android)示例

热门文章

  1. boost::push_front相关的测试程序
  2. boost::math模块具有输出和输入方面的非有限环回的简要基本测试
  3. boost::intrusive::slist用法的测试程序
  4. boost::intrusive::unordered_set用法的测试程序
  5. boost::incremental_components用法的测试程序
  6. Boost::context模块fiber的stack测试程序
  7. Boost:嵌入PTX汇编指令 直接将其添加到boost.compute函数中
  8. VTK:Medical之MedicalDemo4
  9. OpenGL transformation变换的实例
  10. OpenGL 位图字体渲染的实例