mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge pull request #13097 from alalek:backport_13086
This commit is contained in:
commit
57a30a1dd8
@ -670,16 +670,46 @@ public:
|
|||||||
void groupRectangles(std::vector<cv::Rect>& rectList, std::vector<double>& weights, int groupThreshold, double eps) const;
|
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:
|
public:
|
||||||
QRCodeDetector();
|
CV_WRAP QRCodeDetector();
|
||||||
~QRCodeDetector();
|
~QRCodeDetector();
|
||||||
|
|
||||||
void setEpsX(double epsX);
|
/** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection.
|
||||||
void setEpsY(double epsY);
|
@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);
|
||||||
|
|
||||||
bool detect(InputArray in, OutputArray points) const;
|
/** @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.
|
||||||
|
|
||||||
|
@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:
|
protected:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
Ptr<Impl> p;
|
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.
|
@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());
|
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"
|
#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;
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
|
|
||||||
std::vector< Point > corners;
|
std::vector< Point > corners;
|
||||||
TEST_CYCLE() ASSERT_TRUE(detectQRCode(src, corners));
|
QRCodeDetector qrcode;
|
||||||
|
TEST_CYCLE() ASSERT_TRUE(qrcode.detect(src, corners));
|
||||||
SANITY_CHECK(corners);
|
SANITY_CHECK(corners);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,8 +38,13 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, decode)
|
|||||||
|
|
||||||
std::vector< Point > corners;
|
std::vector< Point > corners;
|
||||||
std::string decoded_info;
|
std::string decoded_info;
|
||||||
ASSERT_TRUE(detectQRCode(src, corners));
|
QRCodeDetector qrcode;
|
||||||
TEST_CYCLE() ASSERT_TRUE(decodeQRCode(src, corners, decoded_info, straight_barcode));
|
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());
|
std::vector<uint8_t> decoded_info_uint8_t(decoded_info.begin(), decoded_info.end());
|
||||||
SANITY_CHECK(decoded_info_uint8_t);
|
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));
|
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();
|
SANITY_CHECK_NOTHING();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +84,6 @@ PERF_TEST_P_(Perf_Objdetect_Not_QRCode, detect)
|
|||||||
PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode)
|
PERF_TEST_P_(Perf_Objdetect_Not_QRCode, decode)
|
||||||
{
|
{
|
||||||
Mat straight_barcode;
|
Mat straight_barcode;
|
||||||
std::string decoded_info;
|
|
||||||
std::vector< Point > corners;
|
std::vector< Point > corners;
|
||||||
corners.push_back(Point( 0, 0)); corners.push_back(Point( 0, 5));
|
corners.push_back(Point( 0, 0)); corners.push_back(Point( 0, 5));
|
||||||
corners.push_back(Point(10, 0)); corners.push_back(Point(15, 15));
|
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));
|
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();
|
SANITY_CHECK_NOTHING();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -778,7 +778,15 @@ bool QRCodeDetector::detect(InputArray in, OutputArray points) const
|
|||||||
{
|
{
|
||||||
Mat inarr = in.getMat();
|
Mat inarr = in.getMat();
|
||||||
CV_Assert(!inarr.empty());
|
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;
|
QRDetect qrdet;
|
||||||
qrdet.init(inarr, p->epsX, p->epsY);
|
qrdet.init(inarr, p->epsX, p->epsY);
|
||||||
if (!qrdet.localization()) { return false; }
|
if (!qrdet.localization()) { return false; }
|
||||||
@ -788,7 +796,7 @@ bool QRCodeDetector::detect(InputArray in, OutputArray points) const
|
|||||||
return true;
|
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;
|
QRCodeDetector qrdetector;
|
||||||
qrdetector.setEpsX(eps_x);
|
qrdetector.setEpsX(eps_x);
|
||||||
@ -1060,11 +1068,27 @@ bool QRDecode::fullDecodingProcess()
|
|||||||
#endif
|
#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();
|
Mat inarr = in.getMat();
|
||||||
CV_Assert(!inarr.empty());
|
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());
|
CV_Assert(points.isVector());
|
||||||
vector<Point2f> src_points;
|
vector<Point2f> src_points;
|
||||||
@ -1074,18 +1098,50 @@ CV_EXPORTS bool decodeQRCode(InputArray in, InputArray points, std::string &deco
|
|||||||
|
|
||||||
QRDecode qrdec;
|
QRDecode qrdec;
|
||||||
qrdec.init(inarr, src_points);
|
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,
|
qrdec.getStraightBarcode().convertTo(straight_qrcode,
|
||||||
straight_qrcode.fixedType() ?
|
straight_qrcode.fixedType() ?
|
||||||
straight_qrcode.type() : CV_32FC2);
|
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::vector<Point> corners;
|
||||||
std::string decoded_info;
|
std::string decoded_info;
|
||||||
ASSERT_TRUE(detectQRCode(src, corners));
|
QRCodeDetector qrcode;
|
||||||
#ifdef HAVE_QUIRC
|
#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
|
#endif
|
||||||
|
|
||||||
const std::string dataset_config = findDataFile(root + "dataset_config.json", false);
|
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;
|
Mat straight_barcode;
|
||||||
std::string decoded_info;
|
std::string decoded_info;
|
||||||
Mat zero_image = Mat::zeros(256, 256, CV_8UC1);
|
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
|
#ifdef HAVE_QUIRC
|
||||||
corners = std::vector<Point>(4);
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ int liveQRCodeDetect()
|
|||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRCodeDetector qrcode;
|
||||||
TickMeter total;
|
TickMeter total;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
@ -97,11 +98,11 @@ int liveQRCodeDetect()
|
|||||||
cvtColor(frame, src, COLOR_BGR2GRAY);
|
cvtColor(frame, src, COLOR_BGR2GRAY);
|
||||||
|
|
||||||
total.start();
|
total.start();
|
||||||
bool result_detection = detectQRCode(src, transform);
|
bool result_detection = qrcode.detect(src, transform);
|
||||||
if (result_detection)
|
if (result_detection)
|
||||||
{
|
{
|
||||||
bool result_decode = decodeQRCode(src, transform, decode_info, straight_barcode);
|
decode_info = qrcode.decode(src, transform, straight_barcode);
|
||||||
if (result_decode) { cout << decode_info << '\n'; }
|
if (!decode_info.empty()) { cout << decode_info << '\n'; }
|
||||||
}
|
}
|
||||||
total.stop();
|
total.stop();
|
||||||
double fps = 1 / total.getTimeSec();
|
double fps = 1 / total.getTimeSec();
|
||||||
@ -110,7 +111,7 @@ int liveQRCodeDetect()
|
|||||||
if (result_detection) { getMatWithQRCodeContour(frame, transform); }
|
if (result_detection) { getMatWithQRCodeContour(frame, transform); }
|
||||||
getMatWithFPS(frame, fps);
|
getMatWithFPS(frame, fps);
|
||||||
|
|
||||||
imshow("Live detect QR code", frame);
|
imshow("Live QR code detector", frame);
|
||||||
if( waitKey(30) > 0 ) { break; }
|
if( waitKey(30) > 0 ) { break; }
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -119,33 +120,34 @@ int liveQRCodeDetect()
|
|||||||
int showImageQRCodeDetect(string in, string out)
|
int showImageQRCodeDetect(string in, string out)
|
||||||
{
|
{
|
||||||
Mat src = imread(in, IMREAD_GRAYSCALE), straight_barcode;
|
Mat src = imread(in, IMREAD_GRAYSCALE), straight_barcode;
|
||||||
string decode_info;
|
string decoded_info;
|
||||||
vector<Point> transform;
|
vector<Point> transform;
|
||||||
const int count_experiments = 10;
|
const int count_experiments = 10;
|
||||||
double transform_time = 0.0;
|
double transform_time = 0.0;
|
||||||
bool result_detection = false, result_decode = false;
|
bool result_detection = false;
|
||||||
TickMeter total;
|
TickMeter total;
|
||||||
|
QRCodeDetector qrcode;
|
||||||
for (size_t i = 0; i < count_experiments; i++)
|
for (size_t i = 0; i < count_experiments; i++)
|
||||||
{
|
{
|
||||||
total.start();
|
total.start();
|
||||||
transform.clear();
|
transform.clear();
|
||||||
result_detection = detectQRCode(src, transform);
|
result_detection = qrcode.detect(src, transform);
|
||||||
total.stop();
|
total.stop();
|
||||||
transform_time += total.getTimeSec();
|
transform_time += total.getTimeSec();
|
||||||
total.reset();
|
total.reset();
|
||||||
if (!result_detection) { break; }
|
if (!result_detection) { break; }
|
||||||
|
|
||||||
total.start();
|
total.start();
|
||||||
result_decode = decodeQRCode(src, transform, decode_info, straight_barcode);
|
decoded_info = qrcode.decode(src, transform, straight_barcode);
|
||||||
total.stop();
|
total.stop();
|
||||||
transform_time += total.getTimeSec();
|
transform_time += total.getTimeSec();
|
||||||
total.reset();
|
total.reset();
|
||||||
if (!result_decode) { break; }
|
if (decoded_info.empty()) { break; }
|
||||||
|
|
||||||
}
|
}
|
||||||
double fps = count_experiments / transform_time;
|
double fps = count_experiments / transform_time;
|
||||||
if (!result_detection) { cout << "Not find QR-code." << '\n'; return -2; }
|
if (!result_detection) { cout << "QR code not found\n"; return -2; }
|
||||||
if (!result_decode) { cout << "Not decode QR-code." << '\n'; return -3; }
|
if (decoded_info.empty()) { cout << "QR code cannot be decoded\n"; return -3; }
|
||||||
|
|
||||||
Mat color_src = imread(in);
|
Mat color_src = imread(in);
|
||||||
getMatWithQRCodeContour(color_src, transform);
|
getMatWithQRCodeContour(color_src, transform);
|
||||||
@ -166,7 +168,7 @@ int showImageQRCodeDetect(string in, string out)
|
|||||||
cout << "Output image file path: " << out << '\n';
|
cout << "Output image file path: " << out << '\n';
|
||||||
cout << "Size: " << color_src.size() << '\n';
|
cout << "Size: " << color_src.size() << '\n';
|
||||||
cout << "FPS: " << fps << '\n';
|
cout << "FPS: " << fps << '\n';
|
||||||
cout << "Decode info: " << decode_info << '\n';
|
cout << "Decoded info: " << decoded_info << '\n';
|
||||||
|
|
||||||
vector<int> compression_params;
|
vector<int> compression_params;
|
||||||
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
|
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
|
||||||
|
Loading…
Reference in New Issue
Block a user