eyemCodeDetector.cpp
8.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#include "eyemCodeDetector.h"
#include "yoloWrapper.h"
#include "barcodeDetector.h"
class CodeDetector::Impl {
public:
Impl() {}
~Impl() {}
//目标检测
std::vector<cv::Rect> detect(const cv::Mat& img);
//二维码解码
std::vector<std::string> decode(const cv::Mat& img, std::vector<cv::Rect>& bboxes, std::vector<cv::Rect>& points);
//获取缩放列表
std::vector<float> getScaleList(const int width, const int height);
//缩放图像
cv::Mat processImageScale(const cv::Mat &src, float scale);
//目标检测器
std::shared_ptr<YoloWrapper> detector_;
//一维码检测器
std::shared_ptr<BarcodeDetector> bardetector_;
};
CodeDetector::CodeDetector(const std::string & detector_config_path,
const std::string & detector_model_path,
const std::string & super_resolution_prototxt_path,
const std::string & super_resolution_caffe_model_path) {
p = cv::makePtr<CodeDetector::Impl>();
if (!detector_config_path.empty() && !detector_model_path.empty()) {
p->detector_ = std::make_shared<YoloWrapper>();
p->detector_->init(detector_config_path, detector_model_path, cv::Size(960, 960));
}
else {
p->detector_ = NULL;
}
p->bardetector_ = std::make_shared<BarcodeDetector>();
//TODO:初始化超分辨率图像模型
}
std::vector<std::string> CodeDetector::detectAndDecode(cv::InputArray img, std::vector<cv::Rect> &points, std::vector<cv::Rect> &__points) {
CV_Assert(!img.empty());
if (img.cols() <= 20 || img.rows() <= 20) {
return std::vector<std::string>();
}
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:开始图像格式转换");
cv::Mat input;
int incn = img.channels();
if (incn == 4) {
cv::cvtColor(img, input, cv::COLOR_BGRA2BGR);
}
else if (incn == 1) {
cv::cvtColor(img, input, cv::COLOR_GRAY2BGR);//根据配置支持三通道图像
}
else {
input = img.getMat();
}
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:图像格式转换完成");
_mtx.lock();
int x = 0;
//识别
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:开始识别二维码");
std::vector<cv::Rect> bboxes = p->detect(input);
for (auto&bbox : bboxes) {
__points.push_back(bbox);
}
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:识别二维码结束");
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:开始解码");
//解码
std::vector<std::string> results = p->decode(input, bboxes, points);
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__:解码完成");
_mtx.unlock();
return results;
}
std::vector<cv::Rect> CodeDetector::Impl::detect(const cv::Mat& img) {
return detector_->forward(img);
}
std::vector<std::string> CodeDetector::Impl::decode(const cv::Mat& img, std::vector<cv::Rect>& bboxes, std::vector<cv::Rect>& points) {
if (bboxes.size() == 0) {
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:未识别到二维码");
return std::vector<std::string>();
}
std::vector<std::string> decode_results;
//限定框,防止越界
for (auto& bbox : bboxes) {
if (bbox.height > 1000 || bbox.width > 1000) {
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:识别尺寸太大,判断不是二维码");
continue;
}
float padding_w = 0.1f, padding_h = 0.1f;
auto min_padding = 15;
int padx = (int)cv::max(padding_w * bbox.width, static_cast<float>(min_padding));
int pady = (int)cv::max(padding_h * bbox.height, static_cast<float>(min_padding));
int crop_x_ = cv::max(bbox.tl().x - padx, 0);
int crop_y_ = cv::max(bbox.tl().y - pady, 0);
int end_x = cv::min(bbox.br().x + padx, img.cols - 1);
int end_y = cv::min(bbox.br().y + pady, img.rows - 1);
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:裁剪图像");
cv::Rect crop_roi(crop_x_, crop_y_, end_x - crop_x_ + 1, end_y - crop_y_ + 1);
cv::Mat cropped_img = img(crop_roi);
//转单通道
cv::cvtColor(cropped_img, cropped_img, cv::COLOR_BGR2GRAY);
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:获取多尺度信息");
auto scale_list = getScaleList(cropped_img.cols, cropped_img.rows);
for (auto cur_scale : scale_list) {
//缩放图像
cv::Mat scaled_img =
processImageScale(cropped_img, cur_scale);
std::string result;
//解码
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:开始调用外部dll解码");
std::vector<cv::Point> _points;
auto ret = decodeImage(scaled_img.clone(), result, false, 0, _points);
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:调用外部dll解码完成");
if (ret != -1) {
//恢复真实尺度
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:恢复真实尺度");
for (auto& _point : _points) {
_point.x = cvRound((float)_point.x / cur_scale);
_point.x += crop_roi.tl().x;
_point.y = cvRound((float)_point.y / cur_scale);
_point.y += crop_roi.tl().y;
}
logger.t("__eyemDetectAndDecodeUseNN__detectAndDecode__decode__:解码成功");
cv::Rect br = cv::boundingRect(_points);
points.push_back(br);
decode_results.push_back(result);
break;
}
}
}
return decode_results;
}
std::vector<std::string> CodeDetector::detectAndDecodeBarcode(cv::Mat& img, cv::Mat &showMat, std::vector<cv::Point> &points) {
CV_Assert(!img.empty());
if (img.cols <= 40 || img.rows <= 40) {
return std::vector<std::string>();
}
cv::Mat input;
int incn = img.channels();
if (incn == 4) {
cv::cvtColor(img, input, cv::COLOR_BGRA2BGR);
}
else if (incn == 1) {
cv::cvtColor(img, input, cv::COLOR_GRAY2BGR);//根据配置支持三通道图像
}
else {
input = img;
}
//检测
std::vector<cv::Point> _points;
p->bardetector_->detect(input, _points);
//转单通道
cv::Mat input_gray;
cv::cvtColor(input, input_gray, cv::COLOR_BGR2GRAY);
//解码
std::vector<std::string> results;
if (!_points.empty()) {
for (int n = 0; n < _points.size(); n += 4)
{
std::vector<cv::Point> __points(_points.begin() + n, _points.begin() + n + 4);
//防止越界
cv::Rect box = cv::boundingRect(__points)&cv::Rect(0, 0, input.cols, input.rows);
//
cv::Mat _input = input_gray(box);
cv::Point _t = (__points[2] + __points[3]) / 2 - (__points[0] + __points[1]) / 2;
double t = atan2(_t.y, _t.x)*180.0 / CV_PI;
const int tckH = _input.rows, tckW = _input.cols;
int tckdW = cvRound((double)tckH * abs(sin(t * CV_PI / 180.)) + (double)tckW * abs(cos(t * CV_PI / 180.)));
int tckdH = cvRound(ceil((double)tckW * abs(sin(t * CV_PI / 180.)) + (double)tckH * abs(cos(t * CV_PI / 180.))));
//创建矩阵
cv::Mat matx23f(2, 3, CV_64F);
matx23f = cv::getRotationMatrix2D(cv::Point2f((float)tckW / 2.0f - 0.5f, (float)tckH / 2.0f - 0.5f), t, 1.0);
//由于旋转产生的偏移
matx23f.ptr<double>(0)[2] += (float)(tckdW - tckW) / 2.0;
matx23f.ptr<double>(1)[2] += (float)(tckdH - tckH) / 2.0;
//输出矩阵
float matx[6];
matx[0] = (float)matx23f.ptr<double>(0)[0]; matx[1] = (float)matx23f.ptr<double>(0)[1]; matx[2] = (float)matx23f.ptr<double>(0)[2];
matx[3] = (float)matx23f.ptr<double>(1)[0]; matx[4] = (float)matx23f.ptr<double>(1)[1]; matx[5] = (float)matx23f.ptr<double>(1)[2];
//仿射变换
cv::Mat __input;
cv::warpAffine(_input, __input, matx23f, cv::Size(tckdW, tckdH), cv::INTER_CUBIC, cv::BORDER_REPLICATE);
//计算变换后的点
for (auto&__point : __points) {
cv::Point pt(__point.x - box.x, __point.y - box.y);
__point.x = cvRound((float)pt.x*matx[0] + (float)pt.y*matx[1] + matx[2]);
__point.y = cvRound((float)pt.x*matx[3] + (float)pt.y*matx[4] + matx[5]);
}
auto scale_list = { 1.0f,2.0f };
for (auto cur_scale : scale_list) {
cv::Mat scaled_img =
p->processImageScale(__input(cv::boundingRect(__points)&cv::Rect(0, 0, __input.cols, input.rows)), cur_scale);
std::string result;
int iRet = decodeImage(scaled_img(cv::Range(0, cv::min(21, scaled_img.rows)), cv::Range::all()), result, true);
if (iRet != -1) {
//画图
cv::Point pt = cv::Point(box.tl() + box.br()) / 2;
cv::putText(input, result, pt, cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1);
//输出
points.push_back(pt);
results.push_back(result);
break;
}
}
}
}
p->bardetector_->drawBarcode(showMat, _points);
return results;
}
std::vector<float> CodeDetector::Impl::getScaleList(const int width, const int height) {
if (width < 320 || height < 320) return{ 1.0, 2.0, 0.5 };
if (width < 640 && height < 640) return{ 1.0, 0.5 };
return{ 0.5, 1.0 };
}
cv::Mat CodeDetector::Impl::processImageScale(const cv::Mat &src, float scale) {
cv::Mat sr = src;
if (scale == 1.0) {
return sr;
}
cv::resize(src, sr, cv::Size(), scale, scale, cv::INTER_CUBIC);
return sr;
}