Skip to content

Commit a988f33

Browse files
authored
feat: add cpp demo for wechatqrcode (#279)
* feat: add cpp demo for wechatqrcode * refactor: change to class based * fix: make cpp11 compatible * fix: add opencv_contrib in readme
1 parent 807f45b commit a988f33

File tree

3 files changed

+222
-0
lines changed

3 files changed

+222
-0
lines changed
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cmake_minimum_required(VERSION 3.24.0)
2+
project(opencv_zoo_qrcode_wechatqrcode)
3+
4+
set(OPENCV_VERSION "4.10.0")
5+
set(OPENCV_INSTALLATION_PATH "" CACHE PATH "Where to look for OpenCV installation")
6+
7+
# Find OpenCV
8+
find_package(OpenCV ${OPENCV_VERSION} REQUIRED HINTS ${OPENCV_INSTALLATION_PATH})
9+
10+
add_executable(demo demo.cpp)
11+
target_link_libraries(demo ${OpenCV_LIBS})

models/qrcode_wechatqrcode/README.md

+19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Notes:
99

1010
## Demo
1111

12+
### Python
13+
1214
Run the following command to try the demo:
1315

1416
```shell
@@ -21,6 +23,23 @@ python demo.py --input /path/to/image -v
2123
python demo.py --help
2224
```
2325

26+
### C++
27+
28+
Install latest OpenCV (with opencv_contrib) and CMake >= 3.24.0 to get started with:
29+
30+
```shell
31+
# A typical and default installation path of OpenCV is /usr/local
32+
cmake -B build -D OPENCV_INSTALLATION_PATH=/path/to/opencv/installation .
33+
cmake --build build
34+
35+
# detect on camera input
36+
./build/demo
37+
# detect on an image
38+
./build/demo -i=/path/to/image -v
39+
# get help messages
40+
./build/demo -h
41+
```
42+
2443
### Example outputs
2544

2645
![webcam demo](./example_outputs/wechat_qrcode_demo.gif)

models/qrcode_wechatqrcode/demo.cpp

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#include <iostream>
2+
#include <opencv2/opencv.hpp>
3+
#include <opencv2/wechat_qrcode.hpp>
4+
#include <string>
5+
#include <vector>
6+
7+
class WeChatQRCode {
8+
public:
9+
WeChatQRCode(const std::string& detect_prototxt,
10+
const std::string& detect_model,
11+
const std::string& sr_prototxt, const std::string& sr_model,
12+
int backend_target_index)
13+
: backend_target_index_(backend_target_index) {
14+
15+
const std::vector<std::pair<int, int>> backend_target_pairs = {
16+
{cv::dnn::DNN_BACKEND_OPENCV, cv::dnn::DNN_TARGET_CPU},
17+
{cv::dnn::DNN_BACKEND_CUDA, cv::dnn::DNN_TARGET_CUDA},
18+
{cv::dnn::DNN_BACKEND_CUDA, cv::dnn::DNN_TARGET_CUDA_FP16},
19+
{cv::dnn::DNN_BACKEND_TIMVX, cv::dnn::DNN_TARGET_NPU},
20+
{cv::dnn::DNN_BACKEND_CANN, cv::dnn::DNN_TARGET_NPU}};
21+
22+
if (backend_target_index_ < 0 ||
23+
backend_target_index_ >= backend_target_pairs.size()) {
24+
throw std::invalid_argument("Invalid backend-target index");
25+
}
26+
27+
// initialize detector
28+
detector_ = cv::makePtr<cv::wechat_qrcode::WeChatQRCode>(
29+
detect_prototxt, detect_model, sr_prototxt, sr_model);
30+
}
31+
32+
std::pair<std::vector<std::string>, std::vector<cv::Mat>> detect(
33+
const cv::Mat& image) {
34+
std::vector<std::string> results;
35+
std::vector<cv::Mat> points;
36+
results = detector_->detectAndDecode(image, points);
37+
return {results, points};
38+
}
39+
40+
cv::Mat visualize(const cv::Mat& image,
41+
const std::vector<std::string>& results,
42+
const std::vector<cv::Mat>& points,
43+
cv::Scalar points_color = cv::Scalar(0, 255, 0),
44+
cv::Scalar text_color = cv::Scalar(0, 255, 0),
45+
double fps = -1) const {
46+
cv::Mat output = image.clone();
47+
48+
if (fps >= 0) {
49+
cv::putText(output, "FPS: " + std::to_string(fps), cv::Point(0, 15),
50+
cv::FONT_HERSHEY_SIMPLEX, 0.5, text_color);
51+
}
52+
53+
double fontScale = 0.5;
54+
int fontSize = 1;
55+
56+
for (size_t i = 0; i < results.size(); ++i) {
57+
const auto& p = points[i];
58+
59+
for (int r = 0; r < p.rows; ++r) {
60+
cv::Point point(p.at<float>(r, 0), p.at<float>(r, 1));
61+
cv::circle(output, point, 10, points_color, -1);
62+
}
63+
64+
int qrcode_center_x = (p.at<float>(0, 0) + p.at<float>(2, 0)) / 2;
65+
int qrcode_center_y = (p.at<float>(0, 1) + p.at<float>(2, 1)) / 2;
66+
67+
int baseline = 0;
68+
cv::Size text_size =
69+
cv::getTextSize(results[i], cv::FONT_HERSHEY_DUPLEX, fontScale,
70+
fontSize, &baseline);
71+
72+
cv::Point text_pos(qrcode_center_x - text_size.width / 2,
73+
qrcode_center_y + text_size.height / 2);
74+
75+
cv::putText(output, results[i], text_pos, cv::FONT_HERSHEY_DUPLEX,
76+
fontScale, text_color, fontSize);
77+
}
78+
79+
return output;
80+
}
81+
82+
private:
83+
int backend_target_index_;
84+
cv::Ptr<cv::wechat_qrcode::WeChatQRCode> detector_;
85+
};
86+
87+
int main(int argc, char** argv) {
88+
89+
cv::CommandLineParser parser(
90+
argc, argv,
91+
"{help h | | Show this help message.}"
92+
"{input i | | Set path to the input image. Omit for using default camera.}"
93+
"{detect_prototxt_path | detect_2021nov.prototxt | Set path to detect.prototxt.}"
94+
"{detect_model_path | detect_2021nov.caffemodel | Set path to detect.caffemodel.}"
95+
"{sr_prototxt_path | sr_2021nov.prototxt | Set path to sr.prototxt.}"
96+
"{sr_model_path | sr_2021nov.caffemodel | Set path to sr.caffemodel.}"
97+
"{backend_target bt | 0 | Choose one of the backend-target pairs to run this demo.}"
98+
"{save s | false | Specify to save file with results.}"
99+
"{vis v | false | Specify to open a new window to show results.}");
100+
101+
if (parser.has("help")) {
102+
parser.printMessage();
103+
return 0;
104+
}
105+
106+
// get paths
107+
std::string detect_prototxt = parser.get<std::string>("detect_prototxt_path");
108+
std::string detect_model = parser.get<std::string>("detect_model_path");
109+
std::string sr_prototxt = parser.get<std::string>("sr_prototxt_path");
110+
std::string sr_model = parser.get<std::string>("sr_model_path");
111+
int backend_target_index = parser.get<int>("backend_target");
112+
113+
// input check
114+
std::string input_path = parser.get<std::string>("input");
115+
bool save_result = parser.get<bool>("save");
116+
bool visualize_result = parser.get<bool>("vis");
117+
118+
try {
119+
WeChatQRCode qrDetector(detect_prototxt, detect_model, sr_prototxt,
120+
sr_model, backend_target_index);
121+
122+
if (!input_path.empty()) {
123+
// process image
124+
cv::Mat image = cv::imread(input_path);
125+
if (image.empty()) {
126+
std::cerr << "Could not read the image" << std::endl;
127+
return -1;
128+
}
129+
130+
std::pair<std::vector<std::string>, std::vector<cv::Mat>> detectionResult = qrDetector.detect(image);
131+
auto& results = detectionResult.first;
132+
auto& points = detectionResult.second;
133+
134+
for (const auto& result : results) {
135+
std::cout << result << std::endl;
136+
}
137+
138+
cv::Mat result_image = qrDetector.visualize(image, results, points);
139+
140+
if (save_result) {
141+
cv::imwrite("result.jpg", result_image);
142+
std::cout << "Results saved to result.jpg" << std::endl;
143+
}
144+
145+
if (visualize_result) {
146+
cv::imshow(input_path, result_image);
147+
cv::waitKey(0);
148+
}
149+
} else {
150+
// process camera
151+
cv::VideoCapture cap(0);
152+
if (!cap.isOpened()) {
153+
std::cerr << "Error opening camera" << std::endl;
154+
return -1;
155+
}
156+
157+
cv::Mat frame;
158+
cv::TickMeter tm;
159+
160+
while (true) {
161+
cap >> frame;
162+
if (frame.empty()) {
163+
std::cout << "No frames grabbed" << std::endl;
164+
break;
165+
}
166+
167+
std::pair<std::vector<std::string>, std::vector<cv::Mat>> detectionResult = qrDetector.detect(frame);
168+
auto& results = detectionResult.first;
169+
auto& points = detectionResult.second;
170+
171+
tm.start();
172+
double fps = tm.getFPS();
173+
tm.stop();
174+
175+
cv::Mat result_frame = qrDetector.visualize(
176+
frame, results, points, cv::Scalar(0, 255, 0),
177+
cv::Scalar(0, 255, 0), fps);
178+
cv::imshow("WeChatQRCode Demo", result_frame);
179+
180+
tm.reset();
181+
182+
if (cv::waitKey(1) >= 0) break;
183+
}
184+
}
185+
186+
} catch (const std::exception& ex) {
187+
std::cerr << "Error: " << ex.what() << std::endl;
188+
return -1;
189+
}
190+
191+
return 0;
192+
}

0 commit comments

Comments
 (0)