YOLOv3在OpenCV4.0.0/OpenCV3.4.2上的C++ demo实现

YOLOv3在OpenCV4.0.0/OpenCV3.4.2上的C++ demo实现

2018年11月20日 15:53:05 Andyoyo007 阅读数:1650

参考:

[1] https://pjreddie.com/darknet/yolo/

[2] https://www.learnopencv.com/deep-learning-based-object-detection-using-yolov3-with-opencv-python-c/

 

1. 运行环境:

Ubuntu16.04+OpenCV3.4.2/OpenCV4.0.0

Intel® Core™ i7-8700K CPU @ 3.70GHz × 12

GeForce GTX 1060 5GB/PCIe/SSE2

注:未使用GPU,在CPU上运行,大约160ms/帧图像,貌似OpenCV对其做了优化,我尝试过直接编译darknet的源码用CPU运行,每张图片需要30s+。

2. yolo_opencv.cpp

对参考网址上的代码进行了整合和修改,能够读取图片、视频文件、摄像头。

 
  1. //YOLOv3 on OpenCV

  2. //reference:https://www.learnopencv.com/deep-learning-based-object-detection-using-yolov3-with-opencv-python-c/

  3. //by:Andyoyo@swust

  4. //data:2018.11.20

  5.  
  6. #include <opencv2/opencv.hpp>

  7. #include <opencv2/dnn.hpp>

  8. #include <opencv2/dnn/shape_utils.hpp>

  9. #include <opencv2/imgproc.hpp>

  10. #include <opencv2/highgui.hpp>

  11. #include <iostream>

  12. #include <fstream>

  13.  
  14.  
  15. // Remove the bounding boxes with low confidence using non-maxima suppression

  16. void postprocess(cv::Mat& frame, std::vector<cv::Mat>& outs);

  17.  
  18. // Get the names of the output layers

  19. std::vector<cv::String> getOutputsNames(const cv::dnn::Net& net);

  20.  
  21. // Draw the predicted bounding box

  22. void drawPred(int classId, float conf, int left, int top, int right, int bottom, cv::Mat& frame);

  23.  
  24. // Initialize the parameters

  25. float confThreshold = 0.5; // Confidence threshold

  26. float nmsThreshold = 0.4; // Non-maximum suppression threshold

  27. int inpWidth = 416; // Width of network's input image

  28. int inpHeight = 416; // Height of network's input image

  29.  
  30. static const char* about =

  31. "This sample uses You only look once (YOLO)-Detector (https://arxiv.org/abs/1612.08242) to detect objects on camera/video/image.\n"

  32. "Models can be downloaded here: https://pjreddie.com/darknet/yolo/\n"

  33. "Default network is 416x416.\n"

  34. "Class names can be downloaded here: https://github.com/pjreddie/darknet/tree/master/data\n";

  35.  
  36. static const char* params =

  37. "{ help | false | ./yolo_opencv -source=../data/3.avi }"

  38. "{ source | ../data/dog.jpg | image or video for detection }"

  39. "{ device | 0 | video for detection }"

  40. "{ save | false | save result }";

  41.  
  42.  
  43. std::vector<std::string> classes;

  44.  
  45. int main(int argc, char** argv)

  46. {

  47. cv::CommandLineParser parser(argc, argv, params);

  48. // Load names of classes

  49. std::string classesFile = "../coco.names";

  50. std::ifstream classNamesFile(classesFile.c_str());

  51. if (classNamesFile.is_open())

  52. {

  53. std::string className = "";

  54. while (std::getline(classNamesFile, className))

  55. classes.push_back(className);

  56. }

  57. else{

  58. std::cout<<"can not open classNamesFile"<<std::endl;

  59. }

  60.  
  61. // Give the configuration and weight files for the model

  62. cv::String modelConfiguration = "../yolov3.cfg";

  63. cv::String modelWeights = "../yolov3.weights";

  64.  
  65. // Load the network

  66. cv::dnn::Net net = cv::dnn::readNetFromDarknet(modelConfiguration, modelWeights);

  67. std::cout<<"Read Darknet..."<<std::endl;

  68. net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);

  69. net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);

  70.  
  71. cv::String outputFile = "../data/yolo_out_cpp.avi";

  72. std::string str;

  73. cv::VideoCapture cap;

  74. double frame_count;

  75. if (parser.get<bool>("help"))

  76. {

  77. std::cout << about << std::endl;

  78. parser.printMessage();

  79. return 0;

  80. }

  81. if (parser.get<cv::String>("source").empty())

  82. {

  83. int cameraDevice = parser.get<int>("device");

  84. cap = cv::VideoCapture(cameraDevice);

  85. if (!cap.isOpened())

  86. {

  87. std::cout << "Couldn't find camera: " << cameraDevice << std::endl;

  88. return -1;

  89. }

  90. }

  91. else

  92. {

  93. str=parser.get<cv::String>("source");

  94. cap.open(str);

  95. if (!cap.isOpened())

  96. {

  97. std::cout << "Couldn't open image or video: " << parser.get<cv::String>("video") << std::endl;

  98. return -1;

  99. }

  100. frame_count=cap.get(cv::CAP_PROP_FRAME_COUNT);

  101. std::cout<<"frame_count:"<<frame_count<<std::endl;

  102. }

  103.  
  104. // Get the video writer initialized to save the output video

  105. cv::VideoWriter video;

  106. if (parser.get<bool>("save"))

  107. {

  108. if(frame_count>1)

  109. {

  110. video.open(outputFile, cv::VideoWriter::fourcc('M','J','P','G'), 28, cv::Size(cap.get(cv::CAP_PROP_FRAME_WIDTH),cap.get(cv::CAP_PROP_FRAME_HEIGHT)));

  111. }

  112. else

  113. {

  114. str.replace(str.end()-4, str.end(), "_yolo_out.jpg");

  115. outputFile = str;

  116. }

  117.  
  118. }

  119.  
  120. // Process frames.

  121. std::cout <<"Processing..."<<std::endl;

  122. cv::Mat frame;

  123. while (1)

  124. {

  125. // get frame from the video

  126. cap >> frame;

  127.  
  128. // Stop the program if reached end of video

  129. if (frame.empty()) {

  130. std::cout << "Done processing !!!" << std::endl;

  131. if(parser.get<bool>("save"))

  132. std::cout << "Output file is stored as " << outputFile << std::endl;

  133. std::cout << "Please enter Esc to quit!" << std::endl;

  134. if(cv::waitKey(0)==27)

  135. break;

  136. }

  137.  
  138. //show frame

  139. cv::imshow("frame",frame);

  140.  
  141. // Create a 4D blob from a frame.

  142. cv::Mat blob;

  143. cv::dnn::blobFromImage(frame, blob, 1/255.0, cv::Size(inpWidth, inpHeight), cv::Scalar(0,0,0), true, false);

  144.  
  145. //Sets the input to the network

  146. net.setInput(blob);

  147.  
  148. // Runs the forward pass to get output of the output layers

  149. std::vector<cv::Mat> outs;

  150. net.forward(outs, getOutputsNames(net));

  151.  
  152. // Remove the bounding boxes with low confidence

  153. postprocess(frame, outs);

  154.  
  155. // Put efficiency information. The function getPerfProfile returns the

  156. // overall time for inference(t) and the timings for each of the layers(in layersTimes)

  157. std::vector<double> layersTimes;

  158. double freq = cv::getTickFrequency() / 1000;

  159. double t = net.getPerfProfile(layersTimes) / freq;

  160. std::string label = cv::format("Inference time for a frame : %.2f ms", t);

  161. cv::putText(frame, label, cv::Point(0, 15), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255));

  162.  
  163. // Write the frame with the detection boxes

  164. cv::Mat detectedFrame;

  165. frame.convertTo(detectedFrame, CV_8U);

  166. //show detectedFrame

  167. cv::imshow("detectedFrame",detectedFrame);

  168. //save result

  169. if(parser.get<bool>("save"))

  170. {

  171. if(frame_count>1)

  172. {

  173. video.write(detectedFrame);

  174.  
  175. }

  176. else

  177. {

  178. cv::imwrite(outputFile, detectedFrame);

  179. }

  180. }

  181.  
  182. if(cv::waitKey(10)==27)

  183. {

  184. break;

  185. }

  186.  
  187. }

  188. std::cout<<"Esc..."<<std::endl;

  189. return 0;

  190. }

  191.  
  192.  
  193. // Get the names of the output layers

  194. std::vector<cv::String> getOutputsNames(const cv::dnn::Net& net)

  195. {

  196. static std::vector<cv::String> names;

  197. if (names.empty())

  198. {

  199. //Get the indices of the output layers, i.e. the layers with unconnected outputs

  200. std::vector<int> outLayers = net.getUnconnectedOutLayers();

  201.  
  202. //get the names of all the layers in the network

  203. std::vector<cv::String> layersNames = net.getLayerNames();

  204.  
  205. // Get the names of the output layers in names

  206. names.resize(outLayers.size());

  207. for (size_t i = 0; i < outLayers.size(); ++i)

  208. names[i] = layersNames[outLayers[i] - 1];

  209. }

  210. return names;

  211. }

  212.  
  213.  
  214. // Remove the bounding boxes with low confidence using non-maxima suppression

  215. void postprocess(cv::Mat& frame, std::vector<cv::Mat>& outs)

  216. {

  217. std::vector<int> classIds;

  218. std::vector<float> confidences;

  219. std::vector<cv::Rect> boxes;

  220.  
  221. for (size_t i = 0; i < outs.size(); ++i)

  222. {

  223. // Scan through all the bounding boxes output from the network and keep only the

  224. // ones with high confidence scores. Assign the box's class label as the class

  225. // with the highest score for the box.

  226. float* data = (float*)outs[i].data;

  227. for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)

  228. {

  229. cv::Mat scores = outs[i].row(j).colRange(5, outs[i].cols);

  230. cv::Point classIdPoint;

  231. double confidence;

  232. // Get the value and location of the maximum score

  233. cv::minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);

  234.  
  235. if (confidence > confThreshold)

  236. {

  237. int centerX = (int)(data[0] * frame.cols);

  238. int centerY = (int)(data[1] * frame.rows);

  239. int width = (int)(data[2] * frame.cols);

  240. int height = (int)(data[3] * frame.rows);

  241. int left = centerX - width / 2;

  242. int top = centerY - height / 2;

  243.  
  244. classIds.push_back(classIdPoint.x);

  245. confidences.push_back((float)confidence);

  246. boxes.push_back(cv::Rect(left, top, width, height));

  247. }

  248. }

  249. }

  250.  
  251.  
  252. // Perform non maximum suppression to eliminate redundant overlapping boxes with

  253. // lower confidences

  254. std::vector<int> indices;

  255. cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);

  256. for (size_t i = 0; i < indices.size(); ++i)

  257. {

  258. int idx = indices[i];

  259. cv::Rect box = boxes[idx];

  260. drawPred(classIds[idx], confidences[idx], box.x, box.y,

  261. box.x + box.width, box.y + box.height, frame);

  262. }

  263. }

  264.  
  265. // Draw the predicted bounding box

  266. void drawPred(int classId, float conf, int left, int top, int right, int bottom, cv::Mat& frame)

  267. {

  268. //Draw a rectangle displaying the bounding box

  269. cv::rectangle(frame, cv::Point(left, top), cv::Point(right, bottom), cv::Scalar(0, 0, 255));

  270.  
  271. //Get the label for the class name and its confidence

  272. std::string label = cv::format("%.2f", conf);

  273. if (!classes.empty())

  274. {

  275. CV_Assert(classId < (int)classes.size());

  276. label = classes[classId] + ":" + label;

  277. }

  278. else

  279. {

  280. std::cout<<"classes is empty..."<<std::endl;

  281. }

  282.  
  283. //Display the label at the top of the bounding box

  284. int baseLine;

  285. cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);

  286. top = std::max(top, labelSize.height);

  287. cv::putText(frame, label, cv::Point(left, top), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255,255,255));

  288. }

3. CMakeLists.txt

本人使用的环境安装了OpenCV3.4.2和4.0.0两个版本,均调试通过,不用修改源码,只需在CMakeLists.txt文件中指定OpenCV版本即可。

 
  1. cmake_minimum_required(VERSION 2.8)

  2. #OpenCV4 must enable c++11

  3. add_definitions(-std=c++11)

  4. #setOpenCV_DIR

  5. #set(OpenCV_DIR "/home/andyoyo/opencv/opencv-4.0.0-beta/build")

  6.  
  7. project(yolo_opencv)

  8.  
  9. find_package(OpenCV 4 REQUIRED)

  10.  
  11. #print OpenCV_VERSION on terminal

  12. message(STATUS "OpenCV_VERSION:" ${OpenCV_VERSION})

  13.  
  14. file(GLOB native_srcs "src/*.cpp")

  15.  
  16. add_executable(${PROJECT_NAME} ${native_srcs})

  17.  
  18. target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} )

  19.  

4. 其他说明

运行前先下载yolov3的配置文件等,包括:coco.names,yolov3.cfg,yolov3.weights三个文件,可通过wget下载。

 
  1. wget https://github.com/pjreddie/darknet/blob/master/data/coco.names?raw=true -O ./coco.names

  2. wget https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg?raw=true -O ./yolov3.cfg

  3. wget https://pjreddie.com/media/files/yolov3.weights

5.运行效果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值