评注完毕

gewenlin_branch
gewenlin 10 months ago
parent f98d9757f8
commit 831103c2f6

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

@ -19,8 +19,10 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVe
// 1. plate detect // 1. plate detect
std::vector<CPlate> plateVec; std::vector<CPlate> plateVec;
//车牌检测
int resultPD = plateDetect(img, plateVec, img_index); int resultPD = plateDetect(img, plateVec, img_index);
if (resultPD == 0) { if (resultPD == 0) {
//显示车牌图像(如果启用)。
size_t num = plateVec.size(); size_t num = plateVec.size();
for (size_t j = 0; j < num; j++) { for (size_t j = 0; j < num; j++) {
CPlate& item = plateVec.at(j); CPlate& item = plateVec.at(j);
@ -28,12 +30,14 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVe
SHOW_IMAGE(plateMat, 0); SHOW_IMAGE(plateMat, 0);
// scale the rect to src; // scale the rect to src;
//依比例缩放检测到的车牌矩形位置。
item.setPlateScale(scale); item.setPlateScale(scale);
RotatedRect rect = item.getPlatePos(); RotatedRect rect = item.getPlatePos();
item.setPlatePos(scaleBackRRect(rect, 1.f / scale)); item.setPlatePos(scaleBackRRect(rect, 1.f / scale));
// get plate color // get plate color
Color color = item.getPlateColor(); Color color = item.getPlateColor();
//获得车牌颜色,并将其设置为车牌对象的属性。
if (color == UNKNOWN) { if (color == UNKNOWN) {
color = getPlateType(plateMat, true); color = getPlateType(plateMat, true);
item.setPlateColor(color); item.setPlateColor(color);
@ -45,10 +49,14 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVe
// 2. chars recognize // 2. chars recognize
std::string plateIdentify = ""; std::string plateIdentify = "";
//字符识别
//调用 charsRecognise 方法进行字符识别,
//并将结果附加到车牌颜色字符串前以形成最终的车牌字符串。
int resultCR = charsRecognise(item, plateIdentify); int resultCR = charsRecognise(item, plateIdentify);
if (resultCR == 0) { if (resultCR == 0) {
std::string license = plateColor + ":" + plateIdentify; std::string license = plateColor + ":" + plateIdentify;
item.setPlateStr(license); item.setPlateStr(license);
//将含有识别字符串的车牌对象 item 添加到输出向量 plateVecOut 中。
plateVecOut.push_back(item); plateVecOut.push_back(item);
if (0) std::cout << "resultCR:" << resultCR << std::endl; if (0) std::cout << "resultCR:" << resultCR << std::endl;
} }
@ -70,23 +78,24 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVe
} }
return resultPD; return resultPD;
} }
//加载支持向量机(SVM)模型,用于车牌判断。
void CPlateRecognize::LoadSVM(std::string path) { void CPlateRecognize::LoadSVM(std::string path) {
PlateJudge::instance()->LoadModel(path); PlateJudge::instance()->LoadModel(path);
} }
//加载人工神经网络(ANN)模型,用于字符识别。
void CPlateRecognize::LoadANN(std::string path) { void CPlateRecognize::LoadANN(std::string path) {
CharsIdentify::instance()->LoadModel(path); CharsIdentify::instance()->LoadModel(path);
} }
//加载用于中文字符识别的ANN模型。
void CPlateRecognize::LoadChineseANN(std::string path) { void CPlateRecognize::LoadChineseANN(std::string path) {
CharsIdentify::instance()->LoadChineseModel(path); CharsIdentify::instance()->LoadChineseModel(path);
} }
//加载用于灰度中文字符识别的ANN模型。
void CPlateRecognize::LoadGrayChANN(std::string path) { void CPlateRecognize::LoadGrayChANN(std::string path) {
CharsIdentify::instance()->LoadGrayChANN(path); CharsIdentify::instance()->LoadGrayChANN(path);
} }
// 加载中文字符映射表
void CPlateRecognize::LoadChineseMapping(std::string path) { void CPlateRecognize::LoadChineseMapping(std::string path) {
CharsIdentify::instance()->LoadChineseMapping(path); CharsIdentify::instance()->LoadChineseMapping(path);
} }

Loading…
Cancel
Save