Skip to content

Commit cef9724

Browse files
authored
Merge pull request #3351 from bit1002lst:decode_multi_qrcodes
fix the bug that cannot detect multi qrcode when use_nn_detector is false
2 parents 94d15ee + a3afb87 commit cef9724

File tree

10 files changed

+255
-55
lines changed

10 files changed

+255
-55
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <iostream>
2+
#include <opencv2/core.hpp>
3+
#include <opencv2/highgui.hpp>
4+
#include <opencv2/imgproc.hpp>
5+
6+
using namespace std;
7+
using namespace cv;
8+
9+
#include <opencv2/wechat_qrcode.hpp>
10+
11+
int main(int argc, char* argv[]) {
12+
cout << endl << argv[0] << endl << endl;
13+
cout << "A demo program of WeChat QRCode Detector: " << endl;
14+
15+
Mat img;
16+
int camIdx = -1;
17+
if (argc > 1) {
18+
bool live = strcmp(argv[1], "-camera") == 0;
19+
if (live) {
20+
camIdx = argc > 2 ? atoi(argv[2]) : 0;
21+
} else {
22+
img = imread(argv[1]);
23+
}
24+
} else {
25+
cout << " Usage: " << argv[0] << " <input_image>" << endl;
26+
return 0;
27+
}
28+
// The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings,
29+
// otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode.
30+
Ptr<wechat_qrcode::WeChatQRCode> detector;
31+
32+
try {
33+
detector = makePtr<wechat_qrcode::WeChatQRCode>("", "", "", "");
34+
} catch (const std::exception& e) {
35+
cout <<
36+
"\n---------------------------------------------------------------\n"
37+
"Failed to initialize WeChatQRCode.\n"
38+
"---------------------------------------------------------------\n";
39+
cout << e.what() << endl;
40+
return 0;
41+
}
42+
string prevstr = "";
43+
vector<Mat> points;
44+
45+
if (camIdx < 0) {
46+
auto res = detector->detectAndDecode(img, points);
47+
for (const auto& t : res) cout << t << endl;
48+
} else {
49+
VideoCapture cap(camIdx);
50+
for(;;) {
51+
cap >> img;
52+
if (img.empty())
53+
break;
54+
auto res = detector->detectAndDecode(img, points);
55+
for (const auto& t : res) {
56+
if (t != prevstr)
57+
cout << t << endl;
58+
}
59+
if (!res.empty())
60+
prevstr = res.back();
61+
imshow("image", img);
62+
if (waitKey(30) >= 0)
63+
break;
64+
}
65+
}
66+
return 0;
67+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import cv2
2+
import sys
3+
4+
print(sys.argv[0])
5+
print('A demo program of WeChat QRCode Detector:')
6+
camIdx = -1
7+
if len(sys.argv) > 1:
8+
if sys.argv[1] == "-camera":
9+
camIdx = int(sys.argv[2]) if len(sys.argv)>2 else 0
10+
img = cv2.imread(sys.argv[1])
11+
else:
12+
print(" Usage: " + sys.argv[0] + " <input_image>")
13+
exit(0)
14+
15+
# For python API generator, it follows the template: {module_name}_{class_name},
16+
# so it is a little weird.
17+
# The model is downloaded to ${CMAKE_BINARY_DIR}/downloads/wechat_qrcode if cmake runs without warnings,
18+
# otherwise you can download them from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode.
19+
try:
20+
detector = cv2.wechat_qrcode_WeChatQRCode(
21+
"", "", "", "")
22+
except:
23+
print("---------------------------------------------------------------")
24+
print("Failed to initialize WeChatQRCode.")
25+
print("---------------------------------------------------------------")
26+
exit(0)
27+
28+
prevstr = ""
29+
30+
if camIdx < 0:
31+
res, points = detector.detectAndDecode(img)
32+
print(res,points)
33+
else:
34+
cap = cv2.VideoCapture(camIdx)
35+
while True:
36+
res, img = cap.read()
37+
if img is None:
38+
break
39+
res, points = detector.detectAndDecode(img)
40+
for t in res:
41+
if t != prevstr:
42+
print(t)
43+
if res:
44+
prevstr = res[-1]
45+
cv2.imshow("image", img)
46+
if cv2.waitKey(30) >= 0:
47+
break
48+
# When everything done, release the capture
49+
cap.release()
50+
cv2.destroyAllWindows()

modules/wechat_qrcode/src/decodermgr.cpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using zxing::Result;
1818
using zxing::UnicomBlock;
1919
namespace cv {
2020
namespace wechat_qrcode {
21-
int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector<Point2f>& points) {
21+
int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, vector<string>& results, vector<vector<Point2f>>& zxing_points) {
2222
int width = src.cols;
2323
int height = src.rows;
2424
if (width <= 20 || height <= 20)
@@ -28,7 +28,7 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v
2828
zxing::ArrayRef<uint8_t> scaled_img_zx =
2929
zxing::ArrayRef<uint8_t>(new zxing::Array<uint8_t>(scaled_img_data));
3030

31-
zxing::Ref<zxing::Result> zx_result;
31+
vector<zxing::Ref<zxing::Result>> zx_results;
3232

3333
decode_hints_.setUseNNDetector(use_nn_detector);
3434

@@ -43,16 +43,20 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v
4343
} else {
4444
source->reset(scaled_img_zx.data(), width, height);
4545
}
46-
int ret = TryDecode(source, zx_result);
46+
int ret = TryDecode(source, zx_results);
4747
if (!ret) {
48-
result = zx_result->getText()->getText();
49-
auto result_points = zx_result->getResultPoints();
50-
for(int i = 0; i < result_points->size() / 4; i++) {
51-
const int ind = i * 4;
52-
for (int j = 1; j < 4; j++)
53-
points.emplace_back(result_points[ind+j]->getX(), result_points[ind+j]->getY());
54-
55-
points.emplace_back(result_points[ind]->getX(), result_points[ind]->getY());
48+
for(size_t k = 0; k < zx_results.size(); k++) {
49+
results.emplace_back(zx_results[k]->getText()->getText());
50+
vector<Point2f> tmp_qr_points;
51+
auto tmp_zx_points = zx_results[k]->getResultPoints();
52+
for (int i = 0; i < tmp_zx_points->size() / 4; i++) {
53+
const int ind = i * 4;
54+
for (int j = 1; j < 4; j++){
55+
tmp_qr_points.emplace_back(tmp_zx_points[ind + j]->getX(), tmp_zx_points[ind + j]->getY());
56+
}
57+
tmp_qr_points.emplace_back(tmp_zx_points[ind]->getX(), tmp_zx_points[ind]->getY());
58+
}
59+
zxing_points.push_back(tmp_qr_points);
5660
}
5761
return ret;
5862
}
@@ -62,7 +66,7 @@ int DecoderMgr::decodeImage(cv::Mat src, bool use_nn_detector, string& result, v
6266
return -1;
6367
}
6468

65-
int DecoderMgr::TryDecode(Ref<LuminanceSource> source, Ref<Result>& result) {
69+
int DecoderMgr::TryDecode(Ref<LuminanceSource> source, vector<Ref<Result>>& results) {
6670
int res = -1;
6771
string cell_result;
6872

@@ -71,17 +75,17 @@ int DecoderMgr::TryDecode(Ref<LuminanceSource> source, Ref<Result>& result) {
7175
zxing::Ref<zxing::BinaryBitmap> binary_bitmap(new BinaryBitmap(binarizer));
7276
binary_bitmap->m_poUnicomBlock = qbarUicomBlock_;
7377

74-
result = Decode(binary_bitmap, decode_hints_);
75-
res = (result == NULL) ? 1 : 0;
78+
results = Decode(binary_bitmap, decode_hints_);
79+
res = (results.size() == 0) ? 1 : 0;
7680

7781
if (res == 0) {
78-
result->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer()));
82+
results[0]->setBinaryMethod(int(binarizer_mgr_.GetCurBinarizer()));
7983
}
8084

8185
return res;
8286
}
8387

84-
Ref<Result> DecoderMgr::Decode(Ref<BinaryBitmap> image, DecodeHints hints) {
88+
vector<Ref<Result>> DecoderMgr::Decode(Ref<BinaryBitmap> image, DecodeHints hints) {
8589
return reader_->decode(image, hints);
8690
}
8791
} // namespace wechat_qrcode

modules/wechat_qrcode/src/decodermgr.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class DecoderMgr {
2626
DecoderMgr() { reader_ = new zxing::qrcode::QRCodeReader(); };
2727
~DecoderMgr(){};
2828

29-
int decodeImage(cv::Mat src, bool use_nn_detector, string& result, vector<Point2f>& points);
29+
int decodeImage(cv::Mat src, bool use_nn_detector, vector<string>& result, vector<vector<Point2f>>& zxing_points);
3030

3131
private:
3232
zxing::Ref<zxing::UnicomBlock> qbarUicomBlock_;
@@ -35,10 +35,10 @@ class DecoderMgr {
3535
zxing::Ref<zxing::qrcode::QRCodeReader> reader_;
3636
BinarizerMgr binarizer_mgr_;
3737

38-
zxing::Ref<zxing::Result> Decode(zxing::Ref<zxing::BinaryBitmap> image,
38+
vector<zxing::Ref<zxing::Result>> Decode(zxing::Ref<zxing::BinaryBitmap> image,
3939
zxing::DecodeHints hints);
4040

41-
int TryDecode(zxing::Ref<zxing::LuminanceSource> source, zxing::Ref<zxing::Result>& result);
41+
int TryDecode(zxing::Ref<zxing::LuminanceSource> source, vector<zxing::Ref<zxing::Result>>& result);
4242
};
4343

4444
} // namespace wechat_qrcode

modules/wechat_qrcode/src/wechat_qrcode.cpp

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,21 +144,44 @@ vector<string> WeChatQRCode::Impl::decode(const Mat& img, vector<Mat>& candidate
144144
super_resolution_model_->processImageScale(cropped_img, cur_scale, use_nn_sr_);
145145
string result;
146146
DecoderMgr decodemgr;
147-
vector<Point2f> points_qr;
148-
auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, result, points_qr);
147+
vector<vector<Point2f>> zxing_points, check_points;
148+
auto ret = decodemgr.decodeImage(scaled_img, use_nn_detector_, decode_results, zxing_points);
149149
if (ret == 0) {
150-
for (auto&& pt: points_qr) {
151-
pt /= cur_scale;
150+
for(size_t i = 0; i <zxing_points.size(); i++){
151+
vector<Point2f> points_qr = zxing_points[i];
152+
for (auto&& pt: points_qr) {
153+
pt /= cur_scale;
154+
}
155+
156+
if (use_nn_detector_)
157+
points_qr = aligner.warpBack(points_qr);
158+
for (int j = 0; j < 4; ++j) {
159+
point.at<float>(j, 0) = points_qr[j].x;
160+
point.at<float>(j, 1) = points_qr[j].y;
161+
}
162+
// try to find duplicate qr corners
163+
bool isDuplicate = false;
164+
for (const auto &tmp_points: check_points) {
165+
const float eps = 10.f;
166+
for (size_t j = 0; j < tmp_points.size(); j++) {
167+
if (abs(tmp_points[j].x - points_qr[j].x) < eps &&
168+
abs(tmp_points[j].y - points_qr[j].y) < eps) {
169+
isDuplicate = true;
170+
}
171+
else {
172+
isDuplicate = false;
173+
break;
174+
}
175+
}
176+
}
177+
if (isDuplicate == false) {
178+
points.push_back(point);
179+
check_points.push_back(points_qr);
180+
}
181+
else {
182+
decode_results.erase(decode_results.begin() + i, decode_results.begin() + i + 1);
183+
}
152184
}
153-
154-
if (use_nn_detector_)
155-
points_qr = aligner.warpBack(points_qr);
156-
for (int i = 0; i < 4; ++i) {
157-
point.at<float>(i, 0) = points_qr[i].x;
158-
point.at<float>(i, 1) = points_qr[i].y;
159-
}
160-
decode_results.push_back(result);
161-
points.push_back(point);
162185
break;
163186
}
164187
}

modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.cpp

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,33 @@ QRCodeReader::QRCodeReader() : decoder_() {
3434
smoothMaxMultiple_ = 40;
3535
}
3636

37-
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image) { return decode(image, DecodeHints()); }
37+
vector<Ref<Result>> QRCodeReader::decode(Ref<BinaryBitmap> image) { return decode(image, DecodeHints()); }
3838

39-
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
39+
vector<Ref<Result>> QRCodeReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
4040
// Binarize image using the Histogram Binarized method and be binarized
4141
ErrorHandler err_handler;
42+
vector<Ref<Result>> result_list;
4243
Ref<BitMatrix> imageBitMatrix = image->getBlackMatrix(err_handler);
43-
if (err_handler.ErrCode() || imageBitMatrix == NULL) return Ref<Result>();
44+
if (err_handler.ErrCode() || imageBitMatrix == NULL) return result_list;
4445

45-
Ref<Result> rst = decodeMore(image, imageBitMatrix, hints, err_handler);
46-
if (err_handler.ErrCode() || rst == NULL) {
46+
vector<Ref<Result>> rst = decodeMore(image, imageBitMatrix, hints, err_handler);
47+
if (err_handler.ErrCode() || rst.empty()) {
4748
// black white mirro!!!
4849
Ref<BitMatrix> invertedMatrix = image->getInvertedMatrix(err_handler);
49-
if (err_handler.ErrCode() || invertedMatrix == NULL) return Ref<Result>();
50-
Ref<Result> tmp_rst = decodeMore(image, invertedMatrix, hints, err_handler);
51-
if (err_handler.ErrCode() || tmp_rst == NULL) return Ref<Result>();
52-
return tmp_rst;
50+
if (err_handler.ErrCode() || invertedMatrix == NULL) return result_list;
51+
vector<Ref<Result>> tmp_rst = decodeMore(image, invertedMatrix, hints, err_handler);
52+
if (err_handler.ErrCode() || tmp_rst.empty()) return tmp_rst;
5353
}
5454

5555
return rst;
5656
}
5757

58-
Ref<Result> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> imageBitMatrix,
58+
vector<Ref<Result>> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> imageBitMatrix,
5959
DecodeHints hints, ErrorHandler &err_handler) {
6060
nowHints_ = hints;
6161
std::string ept;
62-
63-
if (imageBitMatrix == NULL) return Ref<Result>();
62+
vector<Ref<Result>> result_list;
63+
if (imageBitMatrix == NULL) return result_list;
6464
image->m_poUnicomBlock->Init();
6565
image->m_poUnicomBlock->Reset(imageBitMatrix);
6666

@@ -88,7 +88,7 @@ Ref<Result> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> ima
8888
Ref<FinderPatternInfo> patternInfo = detector->getFinderPatternInfo(i);
8989
setPatternFix(patternInfo->getPossibleFix());
9090
if (patternInfo->getAnglePossibleFix() < 0.6 && i) continue;
91-
91+
bool patternFoundFlag = false;
9292
int possibleAlignmentCount = 0;
9393
possibleAlignmentCount = detector->getPossibleAlignmentCount(i);
9494
if (possibleAlignmentCount < 0) continue;
@@ -99,6 +99,7 @@ Ref<Result> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> ima
9999

100100
vector<bool> needTryVariousDeimensions(possibleAlignmentCount, false);
101101
for (int j = 0; j < possibleAlignmentCount; j++) {
102+
if (patternFoundFlag){break;}
102103
ArrayRef<Ref<ResultPoint> > points;
103104
err_handler.Reset();
104105
Ref<DetectorResult> detectorResult =
@@ -142,11 +143,15 @@ Ref<Result> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> ima
142143
decoderResult->getCharset(), decoderResult->getQRCodeVersion(),
143144
decoderResult->getEcLevel(), decoderResult->getCharsetMode()));
144145
setSuccFix(points);
145-
146-
return result;
146+
result_list.push_back(result);
147+
patternFoundFlag = true;
148+
if (nowHints_.getUseNNDetector()) {
149+
return result_list;
150+
}
147151
}
148152
// try different dimentions
149153
for (int j = 0; j < possibleAlignmentCount; j++) {
154+
if (patternFoundFlag){break;}
150155
err_handler.Reset();
151156
ArrayRef<Ref<ResultPoint> > points;
152157
if (needTryVariousDeimensions[j]) {
@@ -188,13 +193,17 @@ Ref<Result> QRCodeReader::decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> ima
188193
decoderResult->getEcLevel(), decoderResult->getCharsetMode()));
189194

190195
setSuccFix(points);
191-
return result;
196+
result_list.push_back(result);
197+
patternFoundFlag = true;
198+
if (nowHints_.getUseNNDetector()) {
199+
return result_list;
200+
}
192201
}
193202
}
194203
}
195204
}
196205
}
197-
return Ref<Result>();
206+
return result_list;
198207
}
199208

200209
vector<int> QRCodeReader::getPossibleDimentions(int detectDimension) {

modules/wechat_qrcode/src/zxing/qrcode/qrcode_reader.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ class QRCodeReader : public Reader {
7575
virtual ~QRCodeReader();
7676
string name() override { return "qrcode"; }
7777

78-
Ref<Result> decode(Ref<BinaryBitmap> image) override;
79-
Ref<Result> decode(Ref<BinaryBitmap> image, DecodeHints hints) override;
78+
vector<Ref<Result>> decode(Ref<BinaryBitmap> image) override;
79+
vector<Ref<Result>> decode(Ref<BinaryBitmap> image, DecodeHints hints) override;
8080

81-
Ref<Result> decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> imageBitMatrix,
81+
vector<Ref<Result>> decodeMore(Ref<BinaryBitmap> image, Ref<BitMatrix> imageBitMatrix,
8282
DecodeHints hints, ErrorHandler& err_handler);
8383

8484
private:

0 commit comments

Comments
 (0)