Merge pull request #15338 from rayonnant14:my_detect_and_decode_3.4
QR-Code detector : multiple detection * change in qr-codes detection * change in qr-codes detection * change in test * change in test * add multiple detection * multiple detection * multiple detect * add parallel implementation * add functional for performance tests * change in test * add perftest * returned implementation for 1 qr-code, added support for vector<Mat> and vector<vector<Point2f>> in MultipleDetectAndDecode * deleted all lambda expressions * changing in triangle sort * fixed warnings * fixed errors * add java and python tests * change in java tests * change in java and python tests * change in perf test * change in qrcode.cpp * add spaces * change in qrcode.cpp * change in qrcode.cpp * change in qrcode.cpp * change in java tests * change in java tests * solved problems * solved problems * change in java and python tests * change in python tests * change in python tests * change in python tests * change in methods name * deleted sample qrcode_multi, change in qrcode.cpp * change in perf tests * change in objdetect.hpp * deleted code duplication in sample qrcode.cpp * returned spaces * added spaces * deleted draw function * change in qrcode.cpp * change in qrcode.cpp * deleted all draw functions * objdetect(QR): extractVerticalLines * objdetect(QR): whitespaces * objdetect(QR): simplify operations, avoid duplicated code * change in interface, additional checks in java and python tests, added new key in sample for saving original image from camera * fix warnings and errors in python test * fix * write in file with space key * solved error with empty mat check in python test * correct path to test image * deleted spaces * solved error with check empty mat in python tests * added check of empty vector of points * samples: rework qrcode.cpp * objdetect(QR): fix API, input parameters must be first * objdetect(QR): test/fix points layout
This commit is contained in:
committed by
Alexander Alekhin
parent
282a15c9f9
commit
acc089ca64
+246
-86
@@ -2,23 +2,45 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void drawQRCodeContour(Mat &color_image, vector<Point> transform);
|
||||
static void drawFPS(Mat &color_image, double fps);
|
||||
static int liveQRCodeDetect(const string& out_file);
|
||||
static int imageQRCodeDetect(const string& in_file, const string& out_file);
|
||||
static int liveQRCodeDetect();
|
||||
static int imageQRCodeDetect(const string& in_file);
|
||||
|
||||
static bool g_modeMultiQR = false;
|
||||
static bool g_detectOnly = false;
|
||||
|
||||
static string g_out_file_name, g_out_file_ext;
|
||||
static int g_save_idx = 0;
|
||||
|
||||
static bool g_saveDetections = false;
|
||||
static bool g_saveAll = false;
|
||||
|
||||
static string getQRModeString()
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "QR"
|
||||
<< (g_modeMultiQR ? " multi" : "")
|
||||
<< (g_detectOnly ? " detector" : " decoder");
|
||||
return out.str();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const string keys =
|
||||
"{h help ? | | print help messages }"
|
||||
"{i in | | input path to file for detect (with parameter - show image, otherwise - camera)}"
|
||||
"{o out | | output path to file (save image, work with -i parameter) }";
|
||||
"{i in | | input image path (also switches to image detection mode) }"
|
||||
"{detect | false | detect QR code only (skip decoding) }"
|
||||
"{m multi | | use detect for multiple qr-codes }"
|
||||
"{o out | qr_code.png | path to result file }"
|
||||
"{save_detections | false | save all QR detections (video mode only) }"
|
||||
"{save_all | false | save all processed frames (video mode only) }"
|
||||
;
|
||||
CommandLineParser cmd_parser(argc, argv, keys);
|
||||
|
||||
cmd_parser.about("This program detects the QR-codes from camera or images using the OpenCV library.");
|
||||
@@ -28,32 +50,51 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
string in_file_name = cmd_parser.get<string>("in"); // input path to image
|
||||
string out_file_name;
|
||||
if (cmd_parser.has("out"))
|
||||
out_file_name = cmd_parser.get<string>("out"); // output path to image
|
||||
string in_file_name = cmd_parser.get<string>("in"); // path to input image
|
||||
|
||||
if (cmd_parser.has("out"))
|
||||
{
|
||||
std::string fpath = cmd_parser.get<string>("out"); // path to output image
|
||||
std::string::size_type idx = fpath.rfind('.');
|
||||
if (idx != std::string::npos)
|
||||
{
|
||||
g_out_file_name = fpath.substr(0, idx);
|
||||
g_out_file_ext = fpath.substr(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_out_file_name = fpath;
|
||||
g_out_file_ext = ".png";
|
||||
}
|
||||
}
|
||||
if (!cmd_parser.check())
|
||||
{
|
||||
cmd_parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_modeMultiQR = cmd_parser.has("multi") && cmd_parser.get<bool>("multi");
|
||||
g_detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect");
|
||||
|
||||
g_saveDetections = cmd_parser.has("save_detections") && cmd_parser.get<bool>("save_detections");
|
||||
g_saveAll = cmd_parser.has("save_all") && cmd_parser.get<bool>("save_all");
|
||||
|
||||
int return_code = 0;
|
||||
if (in_file_name.empty())
|
||||
{
|
||||
return_code = liveQRCodeDetect(out_file_name);
|
||||
return_code = liveQRCodeDetect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return_code = imageQRCodeDetect(samples::findFile(in_file_name), out_file_name);
|
||||
return_code = imageQRCodeDetect(samples::findFile(in_file_name));
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
void drawQRCodeContour(Mat &color_image, vector<Point> transform)
|
||||
static
|
||||
void drawQRCodeContour(Mat &color_image, const vector<Point>& corners)
|
||||
{
|
||||
if (!transform.empty())
|
||||
if (!corners.empty())
|
||||
{
|
||||
double show_radius = (color_image.rows > color_image.cols)
|
||||
? (2.813 * color_image.rows) / color_image.cols
|
||||
@@ -61,127 +102,246 @@ void drawQRCodeContour(Mat &color_image, vector<Point> transform)
|
||||
double contour_radius = show_radius * 0.4;
|
||||
|
||||
vector< vector<Point> > contours;
|
||||
contours.push_back(transform);
|
||||
contours.push_back(corners);
|
||||
drawContours(color_image, contours, 0, Scalar(211, 0, 148), cvRound(contour_radius));
|
||||
|
||||
RNG rng(1000);
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
|
||||
circle(color_image, transform[i], cvRound(show_radius), color, -1);
|
||||
circle(color_image, corners[i], cvRound(show_radius), color, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void drawFPS(Mat &color_image, double fps)
|
||||
{
|
||||
ostringstream convert;
|
||||
convert << cvRound(fps) << " FPS (QR detection)";
|
||||
convert << cv::format("%.2f", fps) << " FPS (" << getQRModeString() << ")";
|
||||
putText(color_image, convert.str(), Point(25, 25), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255), 2);
|
||||
}
|
||||
|
||||
int liveQRCodeDetect(const string& out_file)
|
||||
static
|
||||
void drawQRCodeResults(Mat& frame, const vector<Point>& corners, const vector<cv::String>& decode_info, double fps)
|
||||
{
|
||||
VideoCapture cap(0);
|
||||
if(!cap.isOpened())
|
||||
if (!corners.empty())
|
||||
{
|
||||
cout << "Cannot open a camera" << endl;
|
||||
return -4;
|
||||
for (size_t i = 0; i < corners.size(); i += 4)
|
||||
{
|
||||
size_t qr_idx = i / 4;
|
||||
vector<Point> qrcode_contour(corners.begin() + i, corners.begin() + i + 4);
|
||||
drawQRCodeContour(frame, qrcode_contour);
|
||||
|
||||
cout << "QR[" << qr_idx << "] @ " << Mat(qrcode_contour).reshape(2, 1) << ": ";
|
||||
if (decode_info.size() > qr_idx)
|
||||
{
|
||||
if (!decode_info[qr_idx].empty())
|
||||
cout << "'" << decode_info[qr_idx] << "'" << endl;
|
||||
else
|
||||
cout << "can't decode QR code" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "decode information is not available (disabled)" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "QR code is not detected" << endl;
|
||||
}
|
||||
|
||||
QRCodeDetector qrcode;
|
||||
TickMeter total;
|
||||
for(;;)
|
||||
drawFPS(frame, fps);
|
||||
}
|
||||
|
||||
static
|
||||
void runQR(
|
||||
QRCodeDetector& qrcode, const Mat& input,
|
||||
vector<Point>& corners, vector<cv::String>& decode_info
|
||||
// +global: bool g_modeMultiQR, bool g_detectOnly
|
||||
)
|
||||
{
|
||||
if (!g_modeMultiQR)
|
||||
{
|
||||
Mat frame, src, straight_barcode;
|
||||
string decode_info;
|
||||
vector<Point> transform;
|
||||
if (!g_detectOnly)
|
||||
{
|
||||
String decode_info1 = qrcode.detectAndDecode(input, corners);
|
||||
decode_info.push_back(decode_info1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool detection_result = qrcode.detect(input, corners);
|
||||
CV_UNUSED(detection_result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!g_detectOnly)
|
||||
{
|
||||
bool result_detection = qrcode.detectAndDecodeMulti(input, decode_info, corners);
|
||||
CV_UNUSED(result_detection);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result_detection = qrcode.detectMulti(input, corners);
|
||||
CV_UNUSED(result_detection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
double processQRCodeDetection(QRCodeDetector& qrcode, const Mat& input, Mat& result, vector<Point>& corners)
|
||||
{
|
||||
if (input.channels() == 1)
|
||||
cvtColor(input, result, COLOR_GRAY2BGR);
|
||||
else
|
||||
input.copyTo(result);
|
||||
|
||||
cout << "Run " << getQRModeString()
|
||||
<< " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
|
||||
<< endl;
|
||||
|
||||
TickMeter timer;
|
||||
|
||||
vector<cv::String> decode_info;
|
||||
timer.start();
|
||||
runQR(qrcode, input, corners, decode_info);
|
||||
timer.stop();
|
||||
|
||||
double fps = 1 / timer.getTimeSec();
|
||||
drawQRCodeResults(result, corners, decode_info, fps);
|
||||
|
||||
return fps;
|
||||
}
|
||||
|
||||
int liveQRCodeDetect()
|
||||
{
|
||||
VideoCapture cap(0);
|
||||
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "Cannot open a camera" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
cout << "Press 'm' to switch between detectAndDecode and detectAndDecodeMulti" << endl;
|
||||
cout << "Press 'd' to switch between decoder and detector" << endl;
|
||||
cout << "Press ' ' (space) to save result into images" << endl;
|
||||
cout << "Press 'ESC' to exit" << endl;
|
||||
QRCodeDetector qrcode;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Mat frame;
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
cout << "End of video stream" << endl;
|
||||
break;
|
||||
}
|
||||
cvtColor(frame, src, COLOR_BGR2GRAY);
|
||||
|
||||
total.start();
|
||||
bool result_detection = qrcode.detect(src, transform);
|
||||
if (result_detection)
|
||||
bool forceSave = g_saveAll;
|
||||
|
||||
Mat result;
|
||||
|
||||
try
|
||||
{
|
||||
decode_info = qrcode.decode(src, transform, straight_barcode);
|
||||
if (!decode_info.empty()) { cout << decode_info << endl; }
|
||||
vector<Point> corners;
|
||||
double fps = processQRCodeDetection(qrcode, frame, result, corners);
|
||||
cout << "FPS: " << fps << endl;
|
||||
forceSave |= (g_saveDetections && !corners.empty());
|
||||
//forceSave |= fps < 1.0;
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
cerr << "ERROR exception: " << e.what() << endl;
|
||||
forceSave = true;
|
||||
}
|
||||
total.stop();
|
||||
double fps = 1 / total.getTimeSec();
|
||||
total.reset();
|
||||
|
||||
if (result_detection) { drawQRCodeContour(frame, transform); }
|
||||
drawFPS(frame, fps);
|
||||
if (!result.empty())
|
||||
imshow("QR code", result);
|
||||
|
||||
imshow("Live QR code detector", frame);
|
||||
char c = (char)waitKey(30);
|
||||
int code = waitKey(1);
|
||||
if (code < 0 && !forceSave)
|
||||
continue; // timeout
|
||||
char c = (char)code;
|
||||
if (c == ' ' || forceSave)
|
||||
{
|
||||
string fsuffix = cv::format("-%05d", g_save_idx++);
|
||||
|
||||
string fname_input = g_out_file_name + fsuffix + "_input.png";
|
||||
cout << "Saving QR code detection input: '" << fname_input << "' ..." << endl;
|
||||
imwrite(fname_input, frame);
|
||||
|
||||
string fname = g_out_file_name + fsuffix + g_out_file_ext;
|
||||
cout << "Saving QR code detection result: '" << fname << "' ..." << endl;
|
||||
imwrite(fname, result);
|
||||
|
||||
cout << "Saved" << endl;
|
||||
}
|
||||
if (c == 'm')
|
||||
{
|
||||
g_modeMultiQR = !g_modeMultiQR;
|
||||
cout << "Switching QR code mode ==> " << (g_modeMultiQR ? "detectAndDecodeMulti" : "detectAndDecode") << endl;
|
||||
}
|
||||
if (c == 'd')
|
||||
{
|
||||
g_detectOnly = !g_detectOnly;
|
||||
cout << "Switching QR decoder mode ==> " << (g_detectOnly ? "detect" : "decode") << endl;
|
||||
}
|
||||
if (c == 27)
|
||||
{
|
||||
cout << "'ESC' is pressed. Exiting..." << endl;
|
||||
break;
|
||||
if (c == ' ' && !out_file.empty())
|
||||
imwrite(out_file, frame); // TODO write original frame too
|
||||
}
|
||||
}
|
||||
cout << "Exit." << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imageQRCodeDetect(const string& in_file, const string& out_file)
|
||||
int imageQRCodeDetect(const string& in_file)
|
||||
{
|
||||
Mat color_src = imread(in_file, IMREAD_COLOR), src;
|
||||
cvtColor(color_src, src, COLOR_BGR2GRAY);
|
||||
Mat straight_barcode;
|
||||
string decoded_info;
|
||||
vector<Point> transform;
|
||||
const int count_experiments = 10;
|
||||
double transform_time = 0.0;
|
||||
bool result_detection = false;
|
||||
TickMeter total;
|
||||
|
||||
Mat input = imread(in_file, IMREAD_COLOR);
|
||||
cout << "Run " << getQRModeString()
|
||||
<< " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
|
||||
<< endl;
|
||||
|
||||
QRCodeDetector qrcode;
|
||||
vector<Point> corners;
|
||||
vector<cv::String> decode_info;
|
||||
|
||||
TickMeter timer;
|
||||
for (size_t i = 0; i < count_experiments; i++)
|
||||
{
|
||||
total.start();
|
||||
transform.clear();
|
||||
result_detection = qrcode.detect(src, transform);
|
||||
total.stop();
|
||||
transform_time += total.getTimeSec();
|
||||
total.reset();
|
||||
if (!result_detection)
|
||||
continue;
|
||||
corners.clear();
|
||||
decode_info.clear();
|
||||
|
||||
total.start();
|
||||
decoded_info = qrcode.decode(src, transform, straight_barcode);
|
||||
total.stop();
|
||||
transform_time += total.getTimeSec();
|
||||
total.reset();
|
||||
timer.start();
|
||||
runQR(qrcode, input, corners, decode_info);
|
||||
timer.stop();
|
||||
}
|
||||
double fps = count_experiments / transform_time;
|
||||
if (!result_detection)
|
||||
cout << "QR code not found" << endl;
|
||||
if (decoded_info.empty())
|
||||
cout << "QR code cannot be decoded" << endl;
|
||||
|
||||
drawQRCodeContour(color_src, transform);
|
||||
drawFPS(color_src, fps);
|
||||
|
||||
cout << "Input image file path: " << in_file << endl;
|
||||
cout << "Output image file path: " << out_file << endl;
|
||||
cout << "Size: " << color_src.size() << endl;
|
||||
double fps = count_experiments / timer.getTimeSec();
|
||||
cout << "FPS: " << fps << endl;
|
||||
cout << "Decoded info: " << decoded_info << endl;
|
||||
|
||||
if (!out_file.empty())
|
||||
Mat result; input.copyTo(result);
|
||||
drawQRCodeResults(result, corners, decode_info, fps);
|
||||
|
||||
imshow("QR", result); waitKey(1);
|
||||
|
||||
if (!g_out_file_name.empty())
|
||||
{
|
||||
imwrite(out_file, color_src);
|
||||
string out_file = g_out_file_name + g_out_file_ext;
|
||||
cout << "Saving result: " << out_file << endl;
|
||||
imwrite(out_file, result);
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
imshow("Detect QR code on image", color_src);
|
||||
if (waitKey(0) == 27)
|
||||
break;
|
||||
}
|
||||
cout << "Press any key to exit ..." << endl;
|
||||
waitKey(0);
|
||||
cout << "Exit." << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user