|
|
|
@ -641,6 +641,8 @@ bool CPlateLocate::rotation(Mat &in, Mat &out, const Size rect_size,
|
|
|
|
|
bool CPlateLocate::isdeflection(const Mat &in, const double angle,
|
|
|
|
|
double &slope) { /*imshow("in",in);
|
|
|
|
|
waitKey(0);*/
|
|
|
|
|
|
|
|
|
|
//用于检测输入图像 in 是否有偏转,并计算斜率 slope
|
|
|
|
|
if (0) { //用于调试
|
|
|
|
|
imshow("in", in);
|
|
|
|
|
waitKey(0);
|
|
|
|
@ -654,13 +656,13 @@ bool CPlateLocate::isdeflection(const Mat &in, const double angle,
|
|
|
|
|
//获取图像的行数 nRows 和列数 nCols,并确认图像是单通道(灰度图)。
|
|
|
|
|
int comp_index[3];
|
|
|
|
|
int len[3];
|
|
|
|
|
|
|
|
|
|
// 分别计算1/4、1/2、3/4高度处的行索引
|
|
|
|
|
comp_index[0] = nRows / 4;
|
|
|
|
|
comp_index[1] = nRows / 4 * 2;
|
|
|
|
|
comp_index[2] = nRows / 4 * 3;
|
|
|
|
|
|
|
|
|
|
const uchar* p;
|
|
|
|
|
|
|
|
|
|
// 这个循环会在每个四分位的行上找到第一个非零值的位置
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
int index = comp_index[i];
|
|
|
|
|
p = in.ptr<uchar>(index);
|
|
|
|
@ -686,19 +688,15 @@ bool CPlateLocate::isdeflection(const Mat &in, const double angle,
|
|
|
|
|
double PI = 3.14159265;
|
|
|
|
|
|
|
|
|
|
double g = tan(angle * PI / 180.0);
|
|
|
|
|
|
|
|
|
|
//检查最长和最短长度是否有显著差异
|
|
|
|
|
if (maxlen - len[1] > nCols / 32 || len[1] - minlen > nCols / 32) {
|
|
|
|
|
|
|
|
|
|
double slope_can_1 =
|
|
|
|
|
double(len[2] - len[0]) / double(comp_index[1]);
|
|
|
|
|
double slope_can_2 = double(len[1] - len[0]) / double(comp_index[0]);
|
|
|
|
|
double slope_can_3 = double(len[2] - len[1]) / double(comp_index[0]);
|
|
|
|
|
// cout<<"angle:"<<angle<<endl;
|
|
|
|
|
// cout<<"g:"<<g<<endl;
|
|
|
|
|
// cout << "slope_can_1:" << slope_can_1 << endl;
|
|
|
|
|
// cout << "slope_can_2:" << slope_can_2 << endl;
|
|
|
|
|
// cout << "slope_can_3:" << slope_can_3 << endl;
|
|
|
|
|
// if(g>=0)
|
|
|
|
|
|
|
|
|
|
// 选择和输入角度的正切值差异最小的斜率为最终值
|
|
|
|
|
slope = abs(slope_can_1 - g) <= abs(slope_can_2 - g) ? slope_can_1
|
|
|
|
|
: slope_can_2;
|
|
|
|
|
// cout << "slope:" << slope << endl;
|
|
|
|
@ -708,21 +706,23 @@ bool CPlateLocate::isdeflection(const Mat &in, const double angle,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CPlateLocate::affine(const Mat &in, Mat &out, const double slope) {
|
|
|
|
|
// imshow("in", in);
|
|
|
|
|
// waitKey(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//对输入图像进行仿射变换,用于矫正车牌图像倾斜
|
|
|
|
|
Point2f dstTri[3];
|
|
|
|
|
Point2f plTri[3];
|
|
|
|
|
|
|
|
|
|
//输入图像的高度和宽度
|
|
|
|
|
float height = (float) in.rows;
|
|
|
|
|
float width = (float) in.cols;
|
|
|
|
|
float xiff = (float) abs(slope) * height;
|
|
|
|
|
|
|
|
|
|
if (slope > 0) {
|
|
|
|
|
//如果斜率 slope > 0,变换将图像向右倾斜。
|
|
|
|
|
|
|
|
|
|
// right, new position is xiff/2
|
|
|
|
|
|
|
|
|
@ -734,7 +734,7 @@ void CPlateLocate::affine(const Mat &in, Mat &out, const double slope) {
|
|
|
|
|
dstTri[1] = Point2f(width - 1 - xiff / 2, 0);
|
|
|
|
|
dstTri[2] = Point2f(xiff / 2, height - 1);
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
//如果斜率 slope < 0,变换将图像向左倾斜。
|
|
|
|
|
// left, new position is -xiff/2
|
|
|
|
|
|
|
|
|
|
plTri[0] = Point2f(0 + xiff, 0);
|
|
|
|
@ -747,15 +747,18 @@ void CPlateLocate::affine(const Mat &in, Mat &out, const double slope) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat warp_mat = getAffineTransform(plTri, dstTri);
|
|
|
|
|
|
|
|
|
|
//使用 OpenCV 的 getAffineTransform 函数,根据源点和目标点计算仿射变换矩阵 warp_mat。
|
|
|
|
|
Mat affine_mat;
|
|
|
|
|
affine_mat.create((int) height, (int) width, TYPE);
|
|
|
|
|
|
|
|
|
|
if (in.rows > HEIGHT || in.cols > WIDTH)
|
|
|
|
|
if (in.rows > HEIGHT || in.cols > WIDTH)//根据输入图像的大小,选择不同的插值方法:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//如果图像的大小超过预设的 HEIGHT 或 WIDTH,使用 CV_INTER_AREA 插值,这个通常用于缩小。
|
|
|
|
|
warpAffine(in, affine_mat, warp_mat, affine_mat.size(),
|
|
|
|
|
CV_INTER_AREA);
|
|
|
|
|
else
|
|
|
|
|
//否则使用 CV_INTER_CUBIC 插值,这个插值方法在放大时可以提供平滑的边界。
|
|
|
|
|
warpAffine(in, affine_mat, warp_mat, affine_mat.size(), CV_INTER_CUBIC);
|
|
|
|
|
|
|
|
|
|
out = affine_mat;
|
|
|
|
@ -763,22 +766,24 @@ void CPlateLocate::affine(const Mat &in, Mat &out, const double slope) {
|
|
|
|
|
|
|
|
|
|
int CPlateLocate::plateColorLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
int index) {
|
|
|
|
|
//初始化两个 RotatedRect 类型的向量 rects_color_blue 和 rects_color_yellow,以及两个 CPlate 类型的向量 plates_blue 和 plates_yellow
|
|
|
|
|
vector<RotatedRect> rects_color_blue;
|
|
|
|
|
rects_color_blue.reserve(64);
|
|
|
|
|
vector<RotatedRect> rects_color_yellow;
|
|
|
|
|
rects_color_yellow.reserve(64);
|
|
|
|
|
|
|
|
|
|
//这些向量用于存储找到的蓝色和黄色车牌的位置和信息。
|
|
|
|
|
vector<CPlate> plates_blue;
|
|
|
|
|
plates_blue.reserve(64);
|
|
|
|
|
vector<CPlate> plates_yellow;
|
|
|
|
|
plates_yellow.reserve(64);
|
|
|
|
|
|
|
|
|
|
Mat src_clone = src.clone();
|
|
|
|
|
|
|
|
|
|
//使用 OpenMP 并行处理,分别对蓝色和黄色车牌进行搜索和倾斜矫正。
|
|
|
|
|
//这是通过调用 colorSearch 和 deskew 函数完成的。
|
|
|
|
|
Mat src_b_blue;
|
|
|
|
|
Mat src_b_yellow;
|
|
|
|
|
#pragma omp parallel sections
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
#pragma omp section
|
|
|
|
|
{
|
|
|
|
|
colorSearch(src, BLUE, src_b_blue, rects_color_blue);
|
|
|
|
@ -790,6 +795,7 @@ int CPlateLocate::plateColorLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
deskew(src_clone, src_b_yellow, rects_color_yellow, plates_yellow, true, YELLOW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//将找到的蓝色和黄色车牌信息添加到 candPlates 向量中。
|
|
|
|
|
|
|
|
|
|
candPlates.insert(candPlates.end(), plates_blue.begin(), plates_blue.end());
|
|
|
|
|
candPlates.insert(candPlates.end(), plates_yellow.begin(), plates_yellow.end());
|
|
|
|
@ -885,10 +891,13 @@ int CPlateLocate::plateMserLocate(Mat src, vector<CPlate> &candPlates, int img_i
|
|
|
|
|
|
|
|
|
|
int CPlateLocate::sobelOperT(const Mat &in, Mat &out, int blurSize, int morphW,
|
|
|
|
|
int morphH) {
|
|
|
|
|
//在输入图像(src)中使用 MSER(最大稳定极值区域)方法定位车牌
|
|
|
|
|
Mat mat_blur;
|
|
|
|
|
mat_blur = in.clone();
|
|
|
|
|
GaussianBlur(in, mat_blur, Size(blurSize, blurSize), 0, 0, BORDER_DEFAULT);
|
|
|
|
|
//对输入图像进行高斯模糊,这是为了减少噪声
|
|
|
|
|
|
|
|
|
|
//将模糊后的图像转换为灰度图像。如果原图像已经是灰度图,则直接使用。
|
|
|
|
|
Mat mat_gray;
|
|
|
|
|
if (mat_blur.channels() == 3)
|
|
|
|
|
cvtColor(mat_blur, mat_gray, CV_BGR2GRAY);
|
|
|
|
@ -902,24 +911,25 @@ int CPlateLocate::sobelOperT(const Mat &in, Mat &out, int blurSize, int morphW,
|
|
|
|
|
int scale = SOBEL_SCALE;
|
|
|
|
|
int delta = SOBEL_DELTA;
|
|
|
|
|
int ddepth = SOBEL_DDEPTH;
|
|
|
|
|
|
|
|
|
|
//对灰度图像应用 Sobel 操作,得到 x 和 y 方向的梯度。
|
|
|
|
|
Mat grad_x, grad_y;
|
|
|
|
|
Mat abs_grad_x, abs_grad_y;
|
|
|
|
|
|
|
|
|
|
Sobel(mat_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);
|
|
|
|
|
convertScaleAbs(grad_x, abs_grad_x);
|
|
|
|
|
|
|
|
|
|
//将 x 方向的梯度转换为绝对值,然后与 y 方向的梯度合并(假设 y 方向的梯度为0)。
|
|
|
|
|
Mat grad;
|
|
|
|
|
addWeighted(abs_grad_x, 1, 0, 0, 0, grad);
|
|
|
|
|
|
|
|
|
|
utils::imwrite("resources/image/tmp/graygrad.jpg", grad);
|
|
|
|
|
|
|
|
|
|
//使用 Otsu 的阈值法对得到的梯度图像进行二值化
|
|
|
|
|
Mat mat_threshold;
|
|
|
|
|
double otsu_thresh_val =
|
|
|
|
|
threshold(grad, mat_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
|
|
|
|
|
|
|
|
|
utils::imwrite("resources/image/tmp/grayBINARY.jpg", mat_threshold);
|
|
|
|
|
|
|
|
|
|
//对二值化的图像进行形态学闭操作,这有助于连接相邻的区域。
|
|
|
|
|
Mat element = getStructuringElement(MORPH_RECT, Size(morphW, morphH));
|
|
|
|
|
morphologyEx(mat_threshold, mat_threshold, MORPH_CLOSE, element);
|
|
|
|
|
|
|
|
|
@ -933,6 +943,7 @@ int CPlateLocate::sobelOperT(const Mat &in, Mat &out, int blurSize, int morphW,
|
|
|
|
|
int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
int index) {
|
|
|
|
|
vector<RotatedRect> rects_sobel_all;
|
|
|
|
|
//引用传递的 CPlate 类的矢量,用于存储最后识别为候选车牌的结果。
|
|
|
|
|
rects_sobel_all.reserve(256);
|
|
|
|
|
|
|
|
|
|
vector<CPlate> plates;
|
|
|
|
@ -942,11 +953,13 @@ int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
bound_rects.reserve(256);
|
|
|
|
|
|
|
|
|
|
sobelFrtSearch(src, bound_rects);
|
|
|
|
|
|
|
|
|
|
//使用 Sobel 算子处理输入的图像 src 并返回可能的边界矩形 bound_rects。
|
|
|
|
|
vector<Rect_<float>> bound_rects_part;
|
|
|
|
|
bound_rects_part.reserve(256);
|
|
|
|
|
|
|
|
|
|
// enlarge area
|
|
|
|
|
//通过扩大每个边界矩形的面积进行进一步处理,这通常是为了使候选区域更大,
|
|
|
|
|
//以包含整个车牌。代码通过改变矩形的 x 坐标,宽度,和 y 坐标,高度来实现此目的。
|
|
|
|
|
for (size_t i = 0; i < bound_rects.size(); i++) {
|
|
|
|
|
float fRatio = bound_rects[i].width * 1.0f / bound_rects[i].height;
|
|
|
|
|
if (fRatio < 3.0 && fRatio > 1.0 && bound_rects[i].height < 120) {
|
|
|
|
@ -969,6 +982,8 @@ int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// second processing to split one
|
|
|
|
|
//使用 OpenMP 并行处理进行第二次搜索。pragma omp parallel for 使循环并行执行,
|
|
|
|
|
//以加快处理速度。在每次循环中,对于每个边界矩形:
|
|
|
|
|
#pragma omp parallel for
|
|
|
|
|
for (int i = 0; i < (int)bound_rects_part.size(); i++) {
|
|
|
|
|
Rect_<float> bound_rect = bound_rects_part[i];
|
|
|
|
@ -984,12 +999,13 @@ int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
|
|
|
|
|
Rect_<float> safe_bound_rect(x, y, width, height);
|
|
|
|
|
Mat bound_mat = src(safe_bound_rect);
|
|
|
|
|
|
|
|
|
|
//调用 sobelSecSearchPart 函数,它可能进一步处理提取的子图并返回可能的车牌候选区域 rects_sobel
|
|
|
|
|
vector<RotatedRect> rects_sobel;
|
|
|
|
|
rects_sobel.reserve(128);
|
|
|
|
|
sobelSecSearchPart(bound_mat, refpoint, rects_sobel);
|
|
|
|
|
|
|
|
|
|
#pragma omp critical
|
|
|
|
|
//确保当多个线程尝试将其搜索结果添加到 rects_sobel_all 集合时,不会发生冲突。
|
|
|
|
|
{
|
|
|
|
|
rects_sobel_all.insert(rects_sobel_all.end(), rects_sobel.begin(), rects_sobel.end());
|
|
|
|
|
}
|
|
|
|
@ -1022,13 +1038,15 @@ int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat src_b;
|
|
|
|
|
//调用 sobelOper 函数来执行 Sobel 操作。
|
|
|
|
|
sobelOper(src, src_b, 3, 10, 3);
|
|
|
|
|
//每个可能的矩形区域都发送给 deskew 函数,这个函数可能旨在纠正候选车牌的偏斜。
|
|
|
|
|
|
|
|
|
|
deskew(src, src_b, rects_sobel_all, plates);
|
|
|
|
|
|
|
|
|
|
//for (size_t i = 0; i < plates.size(); i++)
|
|
|
|
|
// candPlates.push_back(plates[i]);
|
|
|
|
|
|
|
|
|
|
//得到的车牌从 plates 转移至 candPlates
|
|
|
|
|
candPlates.insert(candPlates.end(), plates.begin(), plates.end());
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -1036,12 +1054,15 @@ int CPlateLocate::plateSobelLocate(Mat src, vector<CPlate> &candPlates,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int CPlateLocate::plateLocate(Mat src, vector<Mat> &resultVec, int index) {
|
|
|
|
|
//对输入图像src执行车牌定位,并将定位到的车牌图像放入resultVec中
|
|
|
|
|
vector<CPlate> all_result_Plates;
|
|
|
|
|
|
|
|
|
|
//三个函数分别使用颜色定位、Sobel边缘检测和MSER算法来识别车牌
|
|
|
|
|
plateColorLocate(src, all_result_Plates, index);
|
|
|
|
|
plateSobelLocate(src, all_result_Plates, index);
|
|
|
|
|
plateMserLocate(src, all_result_Plates, index);
|
|
|
|
|
|
|
|
|
|
//循环通过all_result_Plates,对于每个CPlate对象,
|
|
|
|
|
//调用getPlateMat()获取车牌对应的图像,并将其添加到resultVec向量中。
|
|
|
|
|
for (size_t i = 0; i < all_result_Plates.size(); i++) {
|
|
|
|
|
CPlate plate = all_result_Plates[i];
|
|
|
|
|
resultVec.push_back(plate.getPlateMat());
|
|
|
|
@ -1051,12 +1072,14 @@ int CPlateLocate::plateLocate(Mat src, vector<Mat> &resultVec, int index) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CPlateLocate::plateLocate(Mat src, vector<CPlate> &resultVec, int index) {
|
|
|
|
|
//对输入图像src执行车牌定位,将定位到的车牌对象(CPlate)放入resultVec中
|
|
|
|
|
vector<CPlate> all_result_Plates;
|
|
|
|
|
|
|
|
|
|
plateColorLocate(src, all_result_Plates, index);
|
|
|
|
|
plateSobelLocate(src, all_result_Plates, index);
|
|
|
|
|
plateMserLocate(src, all_result_Plates, index);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//循环通过all_result_Plates,将每一个CPlate对象直接添加到resultVec向量中。
|
|
|
|
|
for (size_t i = 0; i < all_result_Plates.size(); i++) {
|
|
|
|
|
resultVec.push_back(all_result_Plates[i]);
|
|
|
|
|
}
|
|
|
|
|