From 831103c2f64893100f9cf81732185378e253eaa4 Mon Sep 17 00:00:00 2001 From: gewenlin <3074038122@qq.com> Date: Thu, 18 Jan 2024 13:26:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E6=B3=A8=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/src/core/plate_locate.cpp | 75 +++++++++++++++++++++----------- src/src/core/plate_recognize.cpp | 17 ++++++-- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/src/core/plate_locate.cpp b/src/src/core/plate_locate.cpp index a6b394c..f9b25c2 100644 --- a/src/src/core/plate_locate.cpp +++ b/src/src/core/plate_locate.cpp @@ -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(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:"<=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 &candPlates, int index) { + //初始化两个 RotatedRect 类型的向量 rects_color_blue 和 rects_color_yellow,以及两个 CPlate 类型的向量 plates_blue 和 plates_yellow vector rects_color_blue; rects_color_blue.reserve(64); vector rects_color_yellow; rects_color_yellow.reserve(64); - + //这些向量用于存储找到的蓝色和黄色车牌的位置和信息。 vector plates_blue; plates_blue.reserve(64); vector 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 &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 &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 &candPlates, int index) { vector rects_sobel_all; + //引用传递的 CPlate 类的矢量,用于存储最后识别为候选车牌的结果。 rects_sobel_all.reserve(256); vector plates; @@ -942,11 +953,13 @@ int CPlateLocate::plateSobelLocate(Mat src, vector &candPlates, bound_rects.reserve(256); sobelFrtSearch(src, bound_rects); - + //使用 Sobel 算子处理输入的图像 src 并返回可能的边界矩形 bound_rects。 vector> 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 &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_ bound_rect = bound_rects_part[i]; @@ -984,12 +999,13 @@ int CPlateLocate::plateSobelLocate(Mat src, vector &candPlates, Rect_ safe_bound_rect(x, y, width, height); Mat bound_mat = src(safe_bound_rect); - + //调用 sobelSecSearchPart 函数,它可能进一步处理提取的子图并返回可能的车牌候选区域 rects_sobel vector 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 &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 &candPlates, int CPlateLocate::plateLocate(Mat src, vector &resultVec, int index) { + //对输入图像src执行车牌定位,并将定位到的车牌图像放入resultVec中 vector 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 &resultVec, int index) { } int CPlateLocate::plateLocate(Mat src, vector &resultVec, int index) { + //对输入图像src执行车牌定位,将定位到的车牌对象(CPlate)放入resultVec中 vector 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]); } diff --git a/src/src/core/plate_recognize.cpp b/src/src/core/plate_recognize.cpp index a351709..0e18a5b 100644 --- a/src/src/core/plate_recognize.cpp +++ b/src/src/core/plate_recognize.cpp @@ -19,8 +19,10 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe // 1. plate detect std::vector plateVec; + //Ƽ int resultPD = plateDetect(img, plateVec, img_index); if (resultPD == 0) { + //ʾͼã size_t num = plateVec.size(); for (size_t j = 0; j < num; j++) { CPlate& item = plateVec.at(j); @@ -28,12 +30,14 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe SHOW_IMAGE(plateMat, 0); // scale the rect to src; + //ż⵽ijƾλá item.setPlateScale(scale); RotatedRect rect = item.getPlatePos(); item.setPlatePos(scaleBackRRect(rect, 1.f / scale)); // get plate color Color color = item.getPlateColor(); + //óɫΪƶԡ if (color == UNKNOWN) { color = getPlateType(plateMat, true); item.setPlateColor(color); @@ -45,10 +49,14 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe // 2. chars recognize std::string plateIdentify = ""; + //ַʶ + // charsRecognise ַʶ + //ӵɫַǰγյijַ int resultCR = charsRecognise(item, plateIdentify); if (resultCR == 0) { std::string license = plateColor + ":" + plateIdentify; item.setPlateStr(license); + //ʶַijƶ item ӵ plateVecOut С plateVecOut.push_back(item); if (0) std::cout << "resultCR:" << resultCR << std::endl; } @@ -70,23 +78,24 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe } return resultPD; } - +//֧(SVM)ģͣڳжϡ void CPlateRecognize::LoadSVM(std::string path) { PlateJudge::instance()->LoadModel(path); } - +//˹(ANN)ģַͣʶ void CPlateRecognize::LoadANN(std::string path) { CharsIdentify::instance()->LoadModel(path); } - +//ַʶANNģ͡ void CPlateRecognize::LoadChineseANN(std::string path) { CharsIdentify::instance()->LoadChineseModel(path); } +//ڻҶַʶANNģ͡ void CPlateRecognize::LoadGrayChANN(std::string path) { CharsIdentify::instance()->LoadGrayChANN(path); } - +// ַӳ void CPlateRecognize::LoadChineseMapping(std::string path) { CharsIdentify::instance()->LoadChineseMapping(path); }