backport: refined QRCodeDetector API for OpenCV 4.0

pull/13097/head
Vadim Pisarevsky 6 years ago committed by Alexander Alekhin
parent 8675a8c743
commit 82e8657a6d
  1. 42
      modules/objdetect/include/opencv2/objdetect.hpp
  2. 19
      modules/objdetect/perf/perf_qrcode_pipeline.cpp
  3. 72
      modules/objdetect/src/qrcode.cpp
  4. 13
      modules/objdetect/test/test_qrcode.cpp
  5. 26
      samples/cpp/live_detect_qrcode.cpp

@ -670,16 +670,46 @@ public:
void groupRectangles(std::vector<cv::Rect>& rectList, std::vector<double>& weights, int groupThreshold, double eps) const;
};
class CV_EXPORTS QRCodeDetector
class CV_EXPORTS_W QRCodeDetector
{
public:
QRCodeDetector();
CV_WRAP QRCodeDetector();
~QRCodeDetector();
void setEpsX(double epsX);
void setEpsY(double epsY);
/** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection.
@param epsX Epsilon neighborhood, which allows you to determine the horizontal pattern
of the scheme 1:1:3:1:1 according to QR code standard.
*/
CV_WRAP void setEpsX(double epsX);
/** @brief sets the epsilon used during the vertical scan of QR code stop marker detection.
@param epsY Epsilon neighborhood, which allows you to determine the vertical pattern
of the scheme 1:1:3:1:1 according to QR code standard.
*/
CV_WRAP void setEpsY(double epsY);
/** @brief Detects QR code in image and returns the quadrangle containing the code.
@param img grayscale or color (BGR) image containing (or not) QR code.
@param points Output vector of vertices of the minimum-area quadrangle containing the code.
*/
CV_WRAP bool detect(InputArray img, OutputArray points) const;
/** @brief Decodes QR code in image once it's found by the detect() method.
Returns UTF8-encoded output string or empty string if the code cannot be decoded.
bool detect(InputArray in, OutputArray points) const;
@param img grayscale or color (BGR) image containing QR code.
@param points Quadrangle vertices found by detect() method (or some other algorithm).
@param straight_qrcode The optional output image containing rectified and binarized QR code
*/
CV_WRAP cv::String decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray());
/** @brief Both detects and decodes QR code
@param img grayscale or color (BGR) image containing QR code.
@param points opiotnal output array of vertices of the found QR code quadrangle. Will be empty if not found.
@param straight_qrcode The optional output image containing rectified and binarized QR code
*/
CV_WRAP cv::String detectAndDecode(InputArray img, OutputArray points=noArray(),
OutputArray straight_qrcode = noArray());
protected:
struct Impl;
Ptr<Impl> p;
@ -700,8 +730,8 @@ CV_EXPORTS bool detectQRCode(InputArray in, std::vector<Point> &points, double e
@param straight_qrcode Matrix of the type CV_8UC1 containing an binary straight QR code.
*/
CV_EXPORTS bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode = noArray());
//! @} objdetect
//! @} objdetect
}
#include "opencv2/objdetect/detection_based_tracker.hpp"

@ -21,7 +21,8 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, detect)
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
std::vector< Point > corners;
TEST_CYCLE() ASSERT_TRUE(detectQRCode(src, corners));
QRCodeDetector qrcode;
TEST_CYCLE() ASSERT_TRUE(qrcode.detect(src, corners));
SANITY_CHECK(corners);
}
@ -37,8 +38,13 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, decode)
std::vector< Point > corners;
std::string decoded_info;
ASSERT_TRUE(detectQRCode(src, corners));
TEST_CYCLE() ASSERT_TRUE(decodeQRCode(src, corners, decoded_info, straight_barcode));
QRCodeDetector qrcode;
ASSERT_TRUE(qrcode.detect(src, corners));
TEST_CYCLE()
{
decoded_info = qrcode.decode(src, corners, straight_barcode);
ASSERT_FALSE(decoded_info.empty());
}
std::vector<uint8_t> decoded_info_uint8_t(decoded_info.begin(), decoded_info.end());
SANITY_CHECK(decoded_info_uint8_t);
@ -69,7 +75,8 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, detect)
rng.fill(not_qr_code, RNG::UNIFORM, Scalar(0), Scalar(1));
}
TEST_CYCLE() ASSERT_FALSE(detectQRCode(not_qr_code, corners));
QRCodeDetector qrcode;
TEST_CYCLE() ASSERT_FALSE(qrcode.detect(not_qr_code, corners));
SANITY_CHECK_NOTHING();
}
@ -77,7 +84,6 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, detect)
PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode)
{
Mat straight_barcode;
std::string decoded_info;
std::vector< Point > corners;
corners.push_back(Point( 0, 0)); corners.push_back(Point( 0, 5));
corners.push_back(Point(10, 0)); corners.push_back(Point(15, 15));
@ -91,7 +97,8 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode)
rng.fill(not_qr_code, RNG::UNIFORM, Scalar(0), Scalar(1));
}
TEST_CYCLE() ASSERT_FALSE(decodeQRCode(not_qr_code, corners, decoded_info, straight_barcode));
QRCodeDetector qrcode;
TEST_CYCLE() ASSERT_TRUE(qrcode.decode(not_qr_code, corners, straight_barcode).empty());
SANITY_CHECK_NOTHING();
}
#endif

@ -778,7 +778,15 @@ bool QRCodeDetector::detect(InputArray in, OutputArray points) const
{
Mat inarr = in.getMat();
CV_Assert(!inarr.empty());
CV_Assert(inarr.type() == CV_8UC1);
CV_Assert(inarr.depth() == CV_8U);
int incn = inarr.channels();
if( incn == 3 || incn == 4 )
{
Mat gray;
cvtColor(inarr, gray, COLOR_BGR2GRAY);
inarr = gray;
}
QRDetect qrdet;
qrdet.init(inarr, p->epsX, p->epsY);
if (!qrdet.localization()) { return false; }
@ -788,7 +796,7 @@ bool QRCodeDetector::detect(InputArray in, OutputArray points) const
return true;
}
CV_EXPORTS bool detectQRCode(InputArray in, vector<Point> &points, double eps_x, double eps_y)
bool detectQRCode(InputArray in, vector<Point> &points, double eps_x, double eps_y)
{
QRCodeDetector qrdetector;
qrdetector.setEpsX(eps_x);
@ -1060,11 +1068,27 @@ bool QRDecode::fullDecodingProcess()
#endif
}
CV_EXPORTS bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode)
bool decodeQRCode(InputArray in, InputArray points, std::string &decoded_info, OutputArray straight_qrcode)
{
QRCodeDetector qrcode;
decoded_info = qrcode.decode(in, points, straight_qrcode);
return !decoded_info.empty();
}
cv::String QRCodeDetector::decode(InputArray in, InputArray points,
OutputArray straight_qrcode)
{
Mat inarr = in.getMat();
CV_Assert(!inarr.empty());
inarr.convertTo(inarr, CV_8UC1);
CV_Assert(inarr.depth() == CV_8U);
int incn = inarr.channels();
if( incn == 3 || incn == 4 )
{
Mat gray;
cvtColor(inarr, gray, COLOR_BGR2GRAY);
inarr = gray;
}
CV_Assert(points.isVector());
vector<Point2f> src_points;
@ -1074,18 +1098,50 @@ CV_EXPORTS bool decodeQRCode(InputArray in, InputArray points, std::string &deco
QRDecode qrdec;
qrdec.init(inarr, src_points);
bool exit_flag = qrdec.fullDecodingProcess();
bool ok = qrdec.fullDecodingProcess();
decoded_info = qrdec.getDecodeInformation();
std::string decoded_info = qrdec.getDecodeInformation();
if (exit_flag && straight_qrcode.needed())
if (ok && straight_qrcode.needed())
{
qrdec.getStraightBarcode().convertTo(straight_qrcode,
straight_qrcode.fixedType() ?
straight_qrcode.type() : CV_32FC2);
}
return exit_flag;
return ok ? decoded_info : std::string();
}
cv::String QRCodeDetector::detectAndDecode(InputArray in,
OutputArray points_,
OutputArray straight_qrcode)
{
Mat inarr = in.getMat();
CV_Assert(!inarr.empty());
CV_Assert(inarr.depth() == CV_8U);
int incn = inarr.channels();
if( incn == 3 || incn == 4 )
{
Mat gray;
cvtColor(inarr, gray, COLOR_BGR2GRAY);
inarr = gray;
}
vector<Point2f> points;
bool ok = detect(inarr, points);
if( points_.needed() )
{
if( ok )
Mat(points).copyTo(points_);
else
points_.release();
}
std::string decoded_info;
if( ok )
decoded_info = decode(inarr, points, straight_qrcode);
return decoded_info;
}
}

@ -66,9 +66,13 @@ TEST_P(Objdetect_QRCode, regression)
std::vector<Point> corners;
std::string decoded_info;
ASSERT_TRUE(detectQRCode(src, corners));
QRCodeDetector qrcode;
#ifdef HAVE_QUIRC
ASSERT_TRUE(decodeQRCode(src, corners, decoded_info, straight_barcode));
decoded_info = qrcode.detectAndDecode(src, corners, straight_barcode);
ASSERT_FALSE(corners.empty());
ASSERT_FALSE(decoded_info.empty());
#else
ASSERT_TRUE(qrcode.detect(src, corners));
#endif
const std::string dataset_config = findDataFile(root + "dataset_config.json", false);
@ -119,10 +123,11 @@ TEST(Objdetect_QRCode_basic, not_found_qrcode)
Mat straight_barcode;
std::string decoded_info;
Mat zero_image = Mat::zeros(256, 256, CV_8UC1);
EXPECT_FALSE(detectQRCode(zero_image, corners));
QRCodeDetector qrcode;
EXPECT_FALSE(qrcode.detect(zero_image, corners));
#ifdef HAVE_QUIRC
corners = std::vector<Point>(4);
EXPECT_ANY_THROW(decodeQRCode(zero_image, corners, decoded_info, straight_barcode));
EXPECT_ANY_THROW(qrcode.decode(zero_image, corners, straight_barcode));
#endif
}

@ -86,6 +86,7 @@ int liveQRCodeDetect()
return -4;
}
QRCodeDetector qrcode;
TickMeter total;
for(;;)
{
@ -97,11 +98,11 @@ int liveQRCodeDetect()
cvtColor(frame, src, COLOR_BGR2GRAY);
total.start();
bool result_detection = detectQRCode(src, transform);
bool result_detection = qrcode.detect(src, transform);
if (result_detection)
{
bool result_decode = decodeQRCode(src, transform, decode_info, straight_barcode);
if (result_decode) { cout << decode_info << '\n'; }
decode_info = qrcode.decode(src, transform, straight_barcode);
if (!decode_info.empty()) { cout << decode_info << '\n'; }
}
total.stop();
double fps = 1 / total.getTimeSec();
@ -110,7 +111,7 @@ int liveQRCodeDetect()
if (result_detection) { getMatWithQRCodeContour(frame, transform); }
getMatWithFPS(frame, fps);
imshow("Live detect QR code", frame);
imshow("Live QR code detector", frame);
if( waitKey(30) > 0 ) { break; }
}
return 0;
@ -119,33 +120,34 @@ int liveQRCodeDetect()
int showImageQRCodeDetect(string in, string out)
{
Mat src = imread(in, IMREAD_GRAYSCALE), straight_barcode;
string decode_info;
string decoded_info;
vector<Point> transform;
const int count_experiments = 10;
double transform_time = 0.0;
bool result_detection = false, result_decode = false;
bool result_detection = false;
TickMeter total;
QRCodeDetector qrcode;
for (size_t i = 0; i < count_experiments; i++)
{
total.start();
transform.clear();
result_detection = detectQRCode(src, transform);
result_detection = qrcode.detect(src, transform);
total.stop();
transform_time += total.getTimeSec();
total.reset();
if (!result_detection) { break; }
total.start();
result_decode = decodeQRCode(src, transform, decode_info, straight_barcode);
decoded_info = qrcode.decode(src, transform, straight_barcode);
total.stop();
transform_time += total.getTimeSec();
total.reset();
if (!result_decode) { break; }
if (decoded_info.empty()) { break; }
}
double fps = count_experiments / transform_time;
if (!result_detection) { cout << "Not find QR-code." << '\n'; return -2; }
if (!result_decode) { cout << "Not decode QR-code." << '\n'; return -3; }
if (!result_detection) { cout << "QR code not found\n"; return -2; }
if (decoded_info.empty()) { cout << "QR code cannot be decoded\n"; return -3; }
Mat color_src = imread(in);
getMatWithQRCodeContour(color_src, transform);
@ -166,7 +168,7 @@ int showImageQRCodeDetect(string in, string out)
cout << "Output image file path: " << out << '\n';
cout << "Size: " << color_src.size() << '\n';
cout << "FPS: " << fps << '\n';
cout << "Decode info: " << decode_info << '\n';
cout << "Decoded info: " << decoded_info << '\n';
vector<int> compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);

Loading…
Cancel
Save