因爲須要把FasterRCNN作的工程化,所以這裏須要對Caffe進行封裝。其實封裝聽起來感受很高深的樣子,其實就是將本身在caffe上再調用的接口作成一個動態庫,同時將Caffe的庫連着Caffe的那些庫依賴一塊兒作成本身工程的庫依賴就能夠了。若是你只是直接使用Caffe的話,那麼到時候直接連接到caffe下面build目錄中的libcaffe.so或者libcaffe.a就能夠了。若是有對Caffe有C++代碼的改動,操做也是同樣的,可是若是用了Python 模塊好比使用了Python Layer,那麼在使用中,還須要爲Caffe指定其Python模塊的位置。python
好了,首先在這裏我參照http://blog.csdn.net/xyy19920105/article/details/50440957,感謝他的分享,讓咱們很受用。首先寫成一個C++版本。一開始的版本是這樣的,當時只有一個cpp文件。c++
#include <stdio.h> // for snprintf #include <string> #include <vector> #include <math.h> #include <fstream> #include <boost/python.hpp> #include "caffe/caffe.hpp" #include "gpu_nms.hpp" #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace caffe; using namespace std; #define max(a, b) (((a)>(b)) ? (a) :(b)) #define min(a, b) (((a)<(b)) ? (a) :(b)) const int class_num=2; /* * === Class ====================================================================== * Name: Detector * Description: FasterRCNN CXX Detector * ===================================================================================== */ class Detector { public: Detector(const string& model_file, const string& weights_file); void Detection(const string& im_name); void bbox_transform_inv(const int num, const float* box_deltas, const float* pred_cls, float* boxes, float* pred, int img_height, int img_width); void vis_detections(cv::Mat image, int* keep, int num_out, float* sorted_pred_cls, float CONF_THRESH); void boxes_sort(int num, const float* pred, float* sorted_pred); private: shared_ptr<Net<float> > net_; Detector(){} }; /* * === FUNCTION ====================================================================== * Name: Detector * Description: Load the model file and weights file * ===================================================================================== */ //load modelfile and weights Detector::Detector(const string& model_file, const string& weights_file) { net_ = shared_ptr<Net<float> >(new Net<float>(model_file, caffe::TEST)); net_->CopyTrainedLayersFrom(weights_file); } //Using for box sort struct Info { float score; const float* head; }; bool compare(const Info& Info1, const Info& Info2) { return Info1.score > Info2.score; } /* * === FUNCTION ====================================================================== * Name: Detect * Description: Perform detection operation * Warning the max input size should less than 1000*600 * ===================================================================================== */ //perform detection operation //input image max size 1000*600 void Detector::Detection(const string& im_name) { float CONF_THRESH = 0.8; float NMS_THRESH = 0.3; const int max_input_side=1000; const int min_input_side=600; cv::Mat cv_img = cv::imread(im_name); cv::Mat cv_new(cv_img.rows, cv_img.cols, CV_32FC3, cv::Scalar(0,0,0)); if(cv_img.empty()) { std::cout<<"Can not get the image file !"<<endl; return ; } int max_side = max(cv_img.rows, cv_img.cols); int min_side = min(cv_img.rows, cv_img.cols); float max_side_scale = float(max_side) / float(max_input_side); float min_side_scale = float(min_side) /float( min_input_side); float max_scale=max(max_side_scale, min_side_scale); float img_scale = 1; if(max_scale > 1) { img_scale = float(1) / max_scale; } int height = int(cv_img.rows * img_scale); int width = int(cv_img.cols * img_scale); int num_out; cv::Mat cv_resized; std::cout<<"imagename "<<im_name<<endl; float im_info[3]; float data_buf[height*width*3]; float *boxes = NULL; float *pred = NULL; float *pred_per_class = NULL; float *sorted_pred_cls = NULL; int *keep = NULL; const float* bbox_delt; const float* rois; const float* pred_cls; int num; for (int h = 0; h < cv_img.rows; ++h ) { for (int w = 0; w < cv_img.cols; ++w) { cv_new.at<cv::Vec3f>(cv::Point(w, h))[0] = float(cv_img.at<cv::Vec3b>(cv::Point(w, h))[0])-float(102.9801); cv_new.at<cv::Vec3f>(cv::Point(w, h))[1] = float(cv_img.at<cv::Vec3b>(cv::Point(w, h))[1])-float(115.9465); cv_new.at<cv::Vec3f>(cv::Point(w, h))[2] = float(cv_img.at<cv::Vec3b>(cv::Point(w, h))[2])-float(122.7717); } } cv::resize(cv_new, cv_resized, cv::Size(width, height)); im_info[0] = cv_resized.rows; im_info[1] = cv_resized.cols; im_info[2] = img_scale; for (int h = 0; h < height; ++h ) { for (int w = 0; w < width; ++w) { data_buf[(0*height+h)*width+w] = float(cv_resized.at<cv::Vec3f>(cv::Point(w, h))[0]); data_buf[(1*height+h)*width+w] = float(cv_resized.at<cv::Vec3f>(cv::Point(w, h))[1]); data_buf[(2*height+h)*width+w] = float(cv_resized.at<cv::Vec3f>(cv::Point(w, h))[2]); } } net_->blob_by_name("data")->Reshape(1, 3, height, width); net_->blob_by_name("data")->set_cpu_data(data_buf); net_->blob_by_name("im_info")->set_cpu_data(im_info); net_->ForwardFrom(0); bbox_delt = net_->blob_by_name("bbox_pred")->cpu_data(); num = net_->blob_by_name("rois")->num(); rois = net_->blob_by_name("rois")->cpu_data(); pred_cls = net_->blob_by_name("cls_prob")->cpu_data(); boxes = new float[num*4]; pred = new float[num*5*class_num]; pred_per_class = new float[num*5]; sorted_pred_cls = new float[num*5]; keep = new int[num]; for (int n = 0; n < num; n++) { for (int c = 0; c < 4; c++) { boxes[n*4+c] = rois[n*5+c+1] / img_scale; } } bbox_transform_inv(num, bbox_delt, pred_cls, boxes, pred, cv_img.rows, cv_img.cols); for (int i = 1; i < class_num; i ++) { for (int j = 0; j< num; j++) { for (int k=0; k<5; k++) pred_per_class[j*5+k] = pred[(i*num+j)*5+k]; } boxes_sort(num, pred_per_class, sorted_pred_cls); _nms(keep, &num_out, sorted_pred_cls, num, 5, NMS_THRESH, 0); vis_detections(cv_img, keep, num_out, sorted_pred_cls, CONF_THRESH); } cv::imwrite("vis.jpg",cv_img); delete []boxes; delete []pred; delete []pred_per_class; delete []keep; delete []sorted_pred_cls; } /* * === FUNCTION ====================================================================== * Name: vis_detections * Description: Visuallize the detection result * ===================================================================================== */ void Detector::vis_detections(cv::Mat image, int* keep, int num_out, float* sorted_pred_cls, float CONF_THRESH) { int i=0; while(sorted_pred_cls[keep[i]*5+4]>CONF_THRESH && i < num_out) { if(i>=num_out) return; cv::rectangle(image,cv::Point(sorted_pred_cls[keep[i]*5+0], sorted_pred_cls[keep[i]*5+1]),cv::Point(sorted_pred_cls[keep[i]*5+2], sorted_pred_cls[keep[i]*5+3]),cv::Scalar(255,0,0)); i++; } } /* * === FUNCTION ====================================================================== * Name: boxes_sort * Description: Sort the bounding box according score * ===================================================================================== */ void Detector::boxes_sort(const int num, const float* pred, float* sorted_pred) { vector<Info> my; Info tmp; for (int i = 0; i< num; i++) { tmp.score = pred[i*5 + 4]; tmp.head = pred + i*5; my.push_back(tmp); } std::sort(my.begin(), my.end(), compare); for (int i=0; i<num; i++) { for (int j=0; j<5; j++) sorted_pred[i*5+j] = my[i].head[j]; } } /* * === FUNCTION ====================================================================== * Name: bbox_transform_inv * Description: Compute bounding box regression value * ===================================================================================== */ void Detector::bbox_transform_inv(int num, const float* box_deltas, const float* pred_cls, float* boxes, float* pred, int img_height, int img_width) { float width, height, ctr_x, ctr_y, dx, dy, dw, dh, pred_ctr_x, pred_ctr_y, pred_w, pred_h; for(int i=0; i< num; i++) { width = boxes[i*4+2] - boxes[i*4+0] + 1.0; height = boxes[i*4+3] - boxes[i*4+1] + 1.0; ctr_x = boxes[i*4+0] + 0.5 * width; ctr_y = boxes[i*4+1] + 0.5 * height; for (int j=0; j< class_num; j++) { dx = box_deltas[(i*class_num+j)*4+0]; dy = box_deltas[(i*class_num+j)*4+1]; dw = box_deltas[(i*class_num+j)*4+2]; dh = box_deltas[(i*class_num+j)*4+3]; pred_ctr_x = ctr_x + width*dx; pred_ctr_y = ctr_y + height*dy; pred_w = width * exp(dw); pred_h = height * exp(dh); pred[(j*num+i)*5+0] = max(min(pred_ctr_x - 0.5* pred_w, img_width -1), 0); pred[(j*num+i)*5+1] = max(min(pred_ctr_y - 0.5* pred_h, img_height -1), 0); pred[(j*num+i)*5+2] = max(min(pred_ctr_x + 0.5* pred_w, img_width -1), 0); pred[(j*num+i)*5+3] = max(min(pred_ctr_y + 0.5* pred_h, img_height -1), 0); pred[(j*num+i)*5+4] = pred_cls[i*class_num+j]; } } } int main() { string model_file = "/home/lyh1/workspace/py-faster-rcnn/models/pascal_voc/VGG_CNN_M_1024/faster_rcnn_alt_opt/faster_rcnn_test.pt"; string weights_file = "/home/lyh1/workspace/py-faster-rcnn/output/default/yuanzhang_car/vgg_cnn_m_1024_fast_rcnn_stage2_iter_40000.caffemodel"; int GPUID=0; Caffe::SetDevice(GPUID); Caffe::set_mode(Caffe::GPU); Detector det = Detector(model_file, weights_file); det.Detection("/home/lyh1/workspace/py-faster-rcnn/data/demo/car.jpg"); return 0; }
這個文件對應的CMakeLists.txt,其中對編譯部分加了相應的註釋。此處須要添加$PYTHONPATH
,緣由是由於咱們在使用中是C++接口調用的Caffe,可是在Caffe運行中調用了Python,所以須要告訴Caffe咱們本身的Python模塊的路徑,好比這裏去模型的定義文件中看,裏面用了py-faster-rcnn根目錄下的lib中的rpn文件夾下的proposal模塊,所以須要在$PYTHONPATH
中加入這個模塊路徑...\py-faster-rcnn\lib
以及Caffe的Python接口路徑...\py-faster-rcnn\caffe-fast-rcnn\python
,下面是我在~.bashrc
中添加$PYTHONPATH
,可供參考。添加以後執行source ~/.bashrc
,對bash當即生效,提醒一下.bashrc
是在你啓動一個bash的時候會被當即執行的。要麼你直接在當前bash中直接source,不然須要再打開一個新的bash
。還有一點,順帶提一下,加入到$PYTHONPATH
以後,若是你係統中有多個caffe
,那麼你得想一下怎麼防止衝突,當時我就給本身埋了個坑,別的caffe
調用PYTHON
接口時,就直接調到這個加入到$PYTHONPATH
中的這個caffe了,因此各類報錯,若是能夠話能夠考慮用Docker來處理這種東西。git
如圖添加github
仍是接下來說一下這個CMakeList.txt。我也是第一次用,以前用Autotools
,真是各類麻煩,對Linux下的手動編譯都有一些陰影。用Cmake真的是比較傻瓜式的,能把東西梳理的比較清晰。bash
#This part is used for compile faster_rcnn_demo.cpp #這裏是版本要求,根據本身項目而定,我用了默認的 cmake_minimum_required (VERSION 2.8) #咱們的工程的名字 project (faster_rcnn_demo) #添加咱們要生成的可執行文件的名字,以及相應的源碼文件 add_executable(faster_rcnn_demo faster_rcnn_demo.cpp) #這裏添加這個faster_rcnn_demo.cpp所依賴的頭文件路徑 #首先是Caffe目錄的include #其次是用了gpu_nms.cu,因此也要添加相應的頭文件gpu_nms.hpp在py-faster-rcnn根目錄下的lib/nms中 #下面就是幾個Caffe的依賴項,包括Python #值得注意的是boost/python。hpp的頭文件路徑也要加入 #還有opencv的路徑,cuda路徑,線性代數庫路徑相應的都要添加 include_directories ( "${PROJECT_SOURCE_DIR}/../caffe-fast-rcnn/include" "${PROJECT_SOURCE_DIR}/../lib/nms" /share/apps/local/include /usr/local/include /opt/python/include/python2.7 /share/apps/opt/intel/mkl/include /usr/local/cuda/include ) #在這裏值得說一下,target_link_libraries 語句中 生成的目標是可執行文件 後面緊跟的得是動態庫的完整路徑,不然會出錯 #我一開始用的是Link_directoreis而後在後面直接加入了動態庫的路徑,結果他一直報錯,提示找不到庫,ORZ真是跪了, #因此要麼在這裏直接加入完整路徑或者同經過另外一條語句find_library(),這種方式也比較好,直接去指定路徑查找,返回相應的絕對路徑也可避免直接添加地址的問題 #gpu_nms.so 在py-faster-rcnn根目錄下的lib\nms中,直接make就會生成這個so文件 target_link_libraries(faster_rcnn_demo /home/lyh1/workspace/py-faster-rcnn/caffe-fast-rcnn/build/lib/libcaffe.so /home/lyh1/workspace/py-faster-rcnn/lib/nms/gpu_nms.so /share/apps/local/lib/libopencv_highgui.so /share/apps/local/lib/libopencv_core.so /share/apps/local/lib/libopencv_imgproc.so /share/apps/local/lib/libopencv_imgcodecs.so /share/apps/local/lib/libglog.so /share/apps/local/lib/libboost_system.so /share/apps/local/lib/libboost_python.so /share/apps/local/lib/libglog.so /opt/rh/python27/root/usr/lib64/libpython2.7.so )
編譯的時候比較坑爹,還會到這個問題app
可是我用find命令查找了一下整個caffe的工程目錄下竟然沒有這個caffe.pb.h 後來Google一下以後才知道須要手動生成,解決辦法以下用protoc命令手動生成,並放到include文件夾下less
protoc src/caffe/proto/caffe.proto --cpp_out=. mkdir include/caffe/proto mv src/caffe/proto/caffe.pb.h include/caffe/proto
最後cmake .
而後make
編譯成功,運行正常。
接下來就是這麼打包成動態庫了,具體操做在下一個博文中python2.7