opencv/samples/cpp/barcode.cpp
Maksim Shabunin 463cd09811
Merge pull request #23666 from mshabunin:barcode-move
Moved barcode from opencv_contrib #23666

Merge with https://github.com/opencv/opencv_contrib/pull/3497

##### TODO
- [x] Documentation (bib)
- [x] Tutorial (references)
- [x] Sample app (refactored)
- [x] Java (test passes)
- [x] Python (test passes)
- [x] Build without DNN
2023-06-14 22:21:38 +03:00

224 lines
7.0 KiB
C++

#include <iostream>
#include "opencv2/objdetect.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
using namespace std;
static const Scalar greenColor(0, 255, 0);
static const Scalar redColor(0, 0, 255);
static const Scalar yellowColor(0, 255, 255);
static Scalar randColor()
{
RNG &rng = theRNG();
return Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
}
//==============================================================================
struct TheApp
{
Ptr<barcode::BarcodeDetector> bardet;
//! [output]
vector<Point> corners;
vector<string> decode_info;
vector<string> decode_type;
//! [output]
bool detectOnly;
void cleanup()
{
corners.clear();
decode_info.clear();
decode_type.clear();
}
inline string modeString() const
{
return detectOnly ? "<detect>" : "<detectAndDecode>";
}
void drawResults(Mat &frame) const
{
//! [visualize]
for (size_t i = 0; i < corners.size(); i += 4)
{
const size_t idx = i / 4;
const bool isDecodable = idx < decode_info.size()
&& idx < decode_type.size()
&& !decode_type[idx].empty();
const Scalar lineColor = isDecodable ? greenColor : redColor;
// draw barcode rectangle
vector<Point> contour(corners.begin() + i, corners.begin() + i + 4);
const vector< vector<Point> > contours {contour};
drawContours(frame, contours, 0, lineColor, 1);
// draw vertices
for (size_t j = 0; j < 4; j++)
circle(frame, contour[j], 2, randColor(), -1);
// write decoded text
if (isDecodable)
{
ostringstream buf;
buf << "[" << decode_type[idx] << "] " << decode_info[idx];
putText(frame, buf.str(), contour[1], FONT_HERSHEY_COMPLEX, 0.8, yellowColor, 1);
}
}
//! [visualize]
}
void drawFPS(Mat &frame, double fps) const
{
ostringstream buf;
buf << modeString()
<< " (" << corners.size() / 4 << "/" << decode_type.size() << "/" << decode_info.size() << ") "
<< cv::format("%.2f", fps) << " FPS ";
putText(frame, buf.str(), Point(25, 25), FONT_HERSHEY_COMPLEX, 0.8, redColor, 2);
}
inline void call_decode(Mat &frame)
{
cleanup();
if (detectOnly)
{
//! [detect]
bardet->detectMulti(frame, corners);
//! [detect]
}
else
{
//! [detectAndDecode]
bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);
//! [detectAndDecode]
}
}
int liveBarCodeDetect()
{
VideoCapture cap(0);
if (!cap.isOpened())
{
cout << "Cannot open a camera" << endl;
return 2;
}
Mat frame;
Mat result;
cap >> frame;
cout << "Image size: " << frame.size() << endl;
cout << "Press 'd' to switch between <detect> and <detectAndDecode> modes" << endl;
cout << "Press 'ESC' to exit" << endl;
for (;;)
{
cap >> frame;
if (frame.empty())
{
cout << "End of video stream" << endl;
break;
}
if (frame.channels() == 1)
cvtColor(frame, frame, COLOR_GRAY2BGR);
TickMeter timer;
timer.start();
call_decode(frame);
timer.stop();
drawResults(frame);
drawFPS(frame, timer.getFPS());
imshow("barcode", frame);
const char c = (char)waitKey(1);
if (c == 'd')
{
detectOnly = !detectOnly;
cout << "Mode switched to " << modeString() << endl;
}
else if (c == 27)
{
cout << "'ESC' is pressed. Exiting..." << endl;
break;
}
}
return 0;
}
int imageBarCodeDetect(const string &in_file, const string &out_file)
{
Mat frame = imread(in_file, IMREAD_COLOR);
cout << "Image size: " << frame.size() << endl;
cout << "Mode is " << modeString() << endl;
const int count_experiments = 100;
TickMeter timer;
for (size_t i = 0; i < count_experiments; i++)
{
timer.start();
call_decode(frame);
timer.stop();
}
cout << "FPS: " << timer.getFPS() << endl;
drawResults(frame);
if (!out_file.empty())
{
cout << "Saving result: " << out_file << endl;
imwrite(out_file, frame);
}
imshow("barcode", frame);
cout << "Press any key to exit ..." << endl;
waitKey(0);
return 0;
}
};
//==============================================================================
int main(int argc, char **argv)
{
const string keys = "{h help ? | | print help messages }"
"{i in | | input image path (also switches to image detection mode) }"
"{detect | false | detect 1D barcode only (skip decoding) }"
"{o out | | path to result file (only for single image decode) }"
"{sr_prototxt| | super resolution prototxt path }"
"{sr_model | | super resolution model path }";
CommandLineParser cmd_parser(argc, argv, keys);
cmd_parser.about("This program detects the 1D barcodes from camera or images using the OpenCV library.");
if (cmd_parser.has("help"))
{
cmd_parser.printMessage();
return 0;
}
const string in_file = cmd_parser.get<string>("in");
const string out_file = cmd_parser.get<string>("out");
const string sr_prototxt = cmd_parser.get<string>("sr_prototxt");
const string sr_model = cmd_parser.get<string>("sr_model");
if (!cmd_parser.check())
{
cmd_parser.printErrors();
return -1;
}
TheApp app;
app.detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect");
//! [initialize]
try
{
app.bardet = makePtr<barcode::BarcodeDetector>(sr_prototxt, sr_model);
}
catch (const std::exception& e)
{
cout <<
"\n---------------------------------------------------------------\n"
"Failed to initialize super resolution.\n"
"Please, download 'sr.*' from\n"
"https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n"
"and put them into the current directory.\n"
"Or you can leave sr_prototxt and sr_model unspecified.\n"
"---------------------------------------------------------------\n";
cout << e.what() << endl;
return -1;
}
//! [initialize]
if (in_file.empty())
return app.liveBarCodeDetect();
else
return app.imageBarCodeDetect(in_file, out_file);
}