cpp
#include <iostream> // 引入标准输入输出流库
#include "opencv2/objdetect.hpp" // 引入OpenCV物体检测库
#include "opencv2/imgproc.hpp" // 引入OpenCV图像处理库
#include "opencv2/highgui.hpp" // 引入OpenCV高层GUI库
using namespace cv; // 使用OpenCV命名空间
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)); // 生成随机颜色
}
//==============================================================================
// TheApp结构体,其中封装了应用程序的主要逻辑
struct TheApp
{
Ptr<barcode::BarcodeDetector> bardet; // 指向条码检测器的智能指针
//! [output]
vector<Point> corners; // 存储检测到的条码的角点
vector<string> decode_info; // 存储条码的解码信息
vector<string> decode_type; // 存储条码的类型
//! [output]
bool detectOnly; // 标记是否只进行检测而不解码
// 清理函数,用于清除存储结果的vector
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; // 计算当前idx
// 判断当前条码是否被成功解码
const bool isDecodable = idx < decode_info.size()
&& idx < decode_type.size()
&& !decode_type[idx].empty();
const Scalar lineColor = isDecodable ? greenColor : redColor; // 根据是否解码成功选择颜色
// 绘制条码轮廓的矩形
vector<Point> contour(corners.begin() + i, corners.begin() + i + 4);
const vector< vector<Point> > contours {contour};
drawContours(frame, contours, 0, lineColor, 1); // 画轮廓
// 绘制轮廓的四个角点
for (size_t j = 0; j < 4; j++)
circle(frame, contour[j], 2, randColor(), -1);
// 如果解码成功,写出解码文本
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]
}
// 绘制FPS信息的函数
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 "; // 组织FPS显示的文本内容
putText(frame, buf.str(), Point(25, 25), FONT_HERSHEY_COMPLEX, 0.8, redColor, 2); // 将FPS信息绘制到帧上
}
// 调用解码函数的中间处理函数
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; // 返回错误码2
}
Mat frame; // 创建Mat对象用于存储捕获的帧
Mat result; // 创建另一个Mat对象用于存放处理结果
cap >> frame; // 从摄像头读取一帧到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); // 如果帧是灰度图像,则转换为BGR
TickMeter timer; // 创建计时器
timer.start(); // 开始计时
call_decode(frame); // 调用解码函数处理当前帧
timer.stop(); // 停止计时
drawResults(frame); // 绘制检测结果
drawFPS(frame, timer.getFPS()); // 显示帧率(FPS)
imshow("barcode", frame); // 显示带有条码信息的帧
const char c = (char)waitKey(1); // 等待1ms,并读取用户按键
if (c == 'd') // 如果按下'd'键
{
detectOnly = !detectOnly; // 切换检测模式
cout << "Mode switched to " << modeString() << endl; // 输出当前模式
}
else if (c == 27) // 如果按下'ESC'键(ASCII码为27)
{
cout << "'ESC' is pressed. Exiting..." << endl; // 输出退出信息
break; // 跳出循环终止程序
}
}
return 0; // 正常退出返回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; // 输出平均帧率(FPS)
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; // 正常退出返回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; // 有误则返回-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; // 初始化失败则返回-1
}
//! [initialize]
// 根据命令行参数执行相应的检测函数
if (in_file.empty())
return app.liveBarCodeDetect(); // 如果未提供输入文件则启动实时摄像头检测
else
return app.imageBarCodeDetect(in_file, out_file); // 否则进行单帧图像检测
}
bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);