qrcode.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "opencv2/objdetect.hpp"
  2. #include "opencv2/imgproc.hpp"
  3. #include "opencv2/highgui.hpp"
  4. #include "opencv2/videoio.hpp"
  5. #include "opencv2/imgcodecs.hpp"
  6. #include <string>
  7. #include <iostream>
  8. using namespace std;
  9. using namespace cv;
  10. static int liveQRCodeDetect();
  11. static int imageQRCodeDetect(const string& in_file);
  12. static bool g_useArucoBased = false;
  13. static bool g_modeMultiQR = false;
  14. static bool g_detectOnly = false;
  15. static string g_out_file_name, g_out_file_ext;
  16. static int g_save_idx = 0;
  17. static bool g_saveDetections = false;
  18. static bool g_saveAll = false;
  19. static string getQRModeString()
  20. {
  21. std::ostringstream out;
  22. out << "QR"
  23. << (g_modeMultiQR ? " multi" : "")
  24. << (g_detectOnly ? " detector" : " decoder");
  25. return out.str();
  26. }
  27. int main(int argc, char *argv[])
  28. {
  29. const string keys =
  30. "{h help ? | | print help messages }"
  31. "{i in | | input image path (also switches to image detection mode) }"
  32. "{aruco_based | false | use Aruco-based QR code detector instead of contour-based }"
  33. "{detect | false | detect QR code only (skip decoding) }"
  34. "{m multi | | use detect for multiple qr-codes }"
  35. "{o out | qr_code.png | path to result file }"
  36. "{save_detections | false | save all QR detections (video mode only) }"
  37. "{save_all | false | save all processed frames (video mode only) }"
  38. ;
  39. CommandLineParser cmd_parser(argc, argv, keys);
  40. cmd_parser.about("This program detects the QR-codes from camera or images using the OpenCV library.");
  41. if (cmd_parser.has("help"))
  42. {
  43. cmd_parser.printMessage();
  44. return 0;
  45. }
  46. string in_file_name = cmd_parser.get<string>("in"); // path to input image
  47. if (cmd_parser.has("out"))
  48. {
  49. std::string fpath = cmd_parser.get<string>("out"); // path to output image
  50. std::string::size_type idx = fpath.rfind('.');
  51. if (idx != std::string::npos)
  52. {
  53. g_out_file_name = fpath.substr(0, idx);
  54. g_out_file_ext = fpath.substr(idx);
  55. }
  56. else
  57. {
  58. g_out_file_name = fpath;
  59. g_out_file_ext = ".png";
  60. }
  61. }
  62. if (!cmd_parser.check())
  63. {
  64. cmd_parser.printErrors();
  65. return -1;
  66. }
  67. g_modeMultiQR = cmd_parser.has("multi") && cmd_parser.get<bool>("multi");
  68. g_detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect");
  69. g_useArucoBased = cmd_parser.has("aruco_based") && cmd_parser.get<bool>("aruco_based");
  70. g_saveDetections = cmd_parser.has("save_detections") && cmd_parser.get<bool>("save_detections");
  71. g_saveAll = cmd_parser.has("save_all") && cmd_parser.get<bool>("save_all");
  72. int return_code = 0;
  73. if (in_file_name.empty())
  74. {
  75. return_code = liveQRCodeDetect();
  76. }
  77. else
  78. {
  79. return_code = imageQRCodeDetect(samples::findFile(in_file_name));
  80. }
  81. return return_code;
  82. }
  83. static
  84. void drawQRCodeContour(Mat &color_image, const vector<Point>& corners)
  85. {
  86. if (!corners.empty())
  87. {
  88. double show_radius = (color_image.rows > color_image.cols)
  89. ? (2.813 * color_image.rows) / color_image.cols
  90. : (2.813 * color_image.cols) / color_image.rows;
  91. double contour_radius = show_radius * 0.4;
  92. vector< vector<Point> > contours;
  93. contours.push_back(corners);
  94. drawContours(color_image, contours, 0, Scalar(211, 0, 148), cvRound(contour_radius));
  95. RNG rng(1000);
  96. for (size_t i = 0; i < 4; i++)
  97. {
  98. Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
  99. circle(color_image, corners[i], cvRound(show_radius), color, -1);
  100. }
  101. }
  102. }
  103. static
  104. void drawFPS(Mat &color_image, double fps)
  105. {
  106. ostringstream convert;
  107. convert << cv::format("%.2f", fps) << " FPS (" << getQRModeString() << ")";
  108. putText(color_image, convert.str(), Point(25, 25), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255), 2);
  109. }
  110. static
  111. void drawQRCodeResults(Mat& frame, const vector<Point>& corners, const vector<cv::String>& decode_info, double fps)
  112. {
  113. if (!corners.empty())
  114. {
  115. for (size_t i = 0; i < corners.size(); i += 4)
  116. {
  117. size_t qr_idx = i / 4;
  118. vector<Point> qrcode_contour(corners.begin() + i, corners.begin() + i + 4);
  119. drawQRCodeContour(frame, qrcode_contour);
  120. cout << "QR[" << qr_idx << "] @ " << Mat(qrcode_contour).reshape(2, 1) << ": ";
  121. if (decode_info.size() > qr_idx)
  122. {
  123. if (!decode_info[qr_idx].empty())
  124. cout << "'" << decode_info[qr_idx] << "'" << endl;
  125. else
  126. cout << "can't decode QR code" << endl;
  127. }
  128. else
  129. {
  130. cout << "decode information is not available (disabled)" << endl;
  131. }
  132. }
  133. }
  134. else
  135. {
  136. cout << "QR code is not detected" << endl;
  137. }
  138. drawFPS(frame, fps);
  139. }
  140. static
  141. void runQR(
  142. const GraphicalCodeDetector& qrcode, const Mat& input,
  143. vector<Point>& corners, vector<cv::String>& decode_info
  144. // +global: bool g_modeMultiQR, bool g_detectOnly
  145. )
  146. {
  147. if (!g_modeMultiQR)
  148. {
  149. if (!g_detectOnly)
  150. {
  151. String decode_info1 = qrcode.detectAndDecode(input, corners);
  152. decode_info.push_back(decode_info1);
  153. }
  154. else
  155. {
  156. bool detection_result = qrcode.detect(input, corners);
  157. CV_UNUSED(detection_result);
  158. }
  159. }
  160. else
  161. {
  162. if (!g_detectOnly)
  163. {
  164. bool result_detection = qrcode.detectAndDecodeMulti(input, decode_info, corners);
  165. CV_UNUSED(result_detection);
  166. }
  167. else
  168. {
  169. bool result_detection = qrcode.detectMulti(input, corners);
  170. CV_UNUSED(result_detection);
  171. }
  172. }
  173. }
  174. static
  175. double processQRCodeDetection(const GraphicalCodeDetector& qrcode, const Mat& input, Mat& result, vector<Point>& corners)
  176. {
  177. if (input.channels() == 1)
  178. cvtColor(input, result, COLOR_GRAY2BGR);
  179. else
  180. input.copyTo(result);
  181. cout << "Run " << getQRModeString()
  182. << " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
  183. << endl;
  184. TickMeter timer;
  185. vector<cv::String> decode_info;
  186. timer.start();
  187. runQR(qrcode, input, corners, decode_info);
  188. timer.stop();
  189. double fps = 1 / timer.getTimeSec();
  190. drawQRCodeResults(result, corners, decode_info, fps);
  191. return fps;
  192. }
  193. int liveQRCodeDetect()
  194. {
  195. VideoCapture cap(0);
  196. if (!cap.isOpened())
  197. {
  198. cout << "Cannot open a camera" << endl;
  199. return 2;
  200. }
  201. cout << "Press 'm' to switch between detectAndDecode and detectAndDecodeMulti" << endl;
  202. cout << "Press 'd' to switch between decoder and detector" << endl;
  203. cout << "Press ' ' (space) to save result into images" << endl;
  204. cout << "Press 'ESC' to exit" << endl;
  205. GraphicalCodeDetector qrcode = QRCodeDetector();
  206. if (g_useArucoBased)
  207. qrcode = QRCodeDetectorAruco();
  208. for (;;)
  209. {
  210. Mat frame;
  211. cap >> frame;
  212. if (frame.empty())
  213. {
  214. cout << "End of video stream" << endl;
  215. break;
  216. }
  217. bool forceSave = g_saveAll;
  218. Mat result;
  219. try
  220. {
  221. vector<Point> corners;
  222. double fps = processQRCodeDetection(qrcode, frame, result, corners);
  223. cout << "FPS: " << fps << endl;
  224. forceSave |= (g_saveDetections && !corners.empty());
  225. //forceSave |= fps < 1.0;
  226. }
  227. catch (const cv::Exception& e)
  228. {
  229. cerr << "ERROR exception: " << e.what() << endl;
  230. forceSave = true;
  231. }
  232. if (!result.empty())
  233. imshow("QR code", result);
  234. int code = waitKey(1);
  235. if (code < 0 && !forceSave)
  236. continue; // timeout
  237. char c = (char)code;
  238. if (c == ' ' || forceSave)
  239. {
  240. string fsuffix = cv::format("-%05d", g_save_idx++);
  241. string fname_input = g_out_file_name + fsuffix + "_input.png";
  242. cout << "Saving QR code detection input: '" << fname_input << "' ..." << endl;
  243. imwrite(fname_input, frame);
  244. string fname = g_out_file_name + fsuffix + g_out_file_ext;
  245. cout << "Saving QR code detection result: '" << fname << "' ..." << endl;
  246. imwrite(fname, result);
  247. cout << "Saved" << endl;
  248. }
  249. if (c == 'm')
  250. {
  251. g_modeMultiQR = !g_modeMultiQR;
  252. cout << "Switching QR code mode ==> " << (g_modeMultiQR ? "detectAndDecodeMulti" : "detectAndDecode") << endl;
  253. }
  254. if (c == 'd')
  255. {
  256. g_detectOnly = !g_detectOnly;
  257. cout << "Switching QR decoder mode ==> " << (g_detectOnly ? "detect" : "decode") << endl;
  258. }
  259. if (c == 27)
  260. {
  261. cout << "'ESC' is pressed. Exiting..." << endl;
  262. break;
  263. }
  264. }
  265. cout << "Exit." << endl;
  266. return 0;
  267. }
  268. int imageQRCodeDetect(const string& in_file)
  269. {
  270. const int count_experiments = 10;
  271. Mat input = imread(in_file, IMREAD_COLOR);
  272. cout << "Run " << getQRModeString()
  273. << " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
  274. << endl;
  275. GraphicalCodeDetector qrcode = QRCodeDetector();
  276. if (g_useArucoBased)
  277. qrcode = QRCodeDetectorAruco();
  278. vector<Point> corners;
  279. vector<cv::String> decode_info;
  280. TickMeter timer;
  281. for (size_t i = 0; i < count_experiments; i++)
  282. {
  283. corners.clear();
  284. decode_info.clear();
  285. timer.start();
  286. runQR(qrcode, input, corners, decode_info);
  287. timer.stop();
  288. }
  289. double fps = count_experiments / timer.getTimeSec();
  290. cout << "FPS: " << fps << endl;
  291. Mat result; input.copyTo(result);
  292. drawQRCodeResults(result, corners, decode_info, fps);
  293. imshow("QR", result); waitKey(1);
  294. if (!g_out_file_name.empty())
  295. {
  296. string out_file = g_out_file_name + g_out_file_ext;
  297. cout << "Saving result: " << out_file << endl;
  298. imwrite(out_file, result);
  299. }
  300. cout << "Press any key to exit ..." << endl;
  301. waitKey(0);
  302. cout << "Exit." << endl;
  303. return 0;
  304. }