From 2ed8bbe60835664e907400082ad68a26bb545553 Mon Sep 17 00:00:00 2001 From: www-369 <2776296049@qq.com> Date: Mon, 4 Dec 2023 16:42:25 +0800 Subject: [PATCH] WXP0 --- src/src/core/plate_judge.cpp | 121 +++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/src/src/core/plate_judge.cpp b/src/src/core/plate_judge.cpp index 619a48c..84a76a2 100644 --- a/src/src/core/plate_judge.cpp +++ b/src/src/core/plate_judge.cpp @@ -6,43 +6,47 @@ #include "easypr/core/params.h" namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类只有一个实例 - +//定义了一个静态成员变量instance_,它是PlateJudge类的指针,初始化为nullptr。这是单例模式的关键,所有的PlateJudge实例都将共享这个变量 PlateJudge* PlateJudge::instance_ = nullptr; - PlateJudge* PlateJudge::instance() { - if (!instance_) { - instance_ = new PlateJudge; + +//确保PlateJudge类只有一个实例,当需要使用PlateJudge类时,只需要调用PlateJudge::instance()即可获取到这个唯一的实例 + PlateJudge* PlateJudge::instance() { //定义了一个静态成员函数instance(),它返回一个指向PlateJudge实例的指针 + if (!instance_) { //检查instance_是否为nullptr + instance_ = new PlateJudge; //如果是,那么就创建一个新的PlateJudge实例,并将instance_设置为指向这个新创建的实例 } - return instance_; + return instance_; //返回instance_,即指向PlateJudge实例的指针 } - PlateJudge::PlateJudge() { //PlateJudge决定了使用哪种特征提取方法。 - bool useLBP = false; - if (useLBP) { - LOAD_SVM_MODEL(svm_, kLBPSvmPath); - extractFeature = getLBPFeatures; + PlateJudge::PlateJudge() { //PlateJudge决定了使用哪种特征提取方法 + bool useLBP = false; //定义一个布尔变量useLBP,并初始化为false + if (useLBP) { //如果useLBP为true,即使用LBP特征提取方法 + LOAD_SVM_MODEL(svm_, kLBPSvmPath); //加载LBP的SVM模型 + extractFeature = getLBPFeatures; //设置特征提取函数为getLBPFeatures } - else { - LOAD_SVM_MODEL(svm_, kHistSvmPath); - extractFeature = getHistomPlusColoFeatures; + else { //如果useLBP为false,即使用直方图特征提取方法 + LOAD_SVM_MODEL(svm_, kHistSvmPath); //加载直方图的SVM模型 + extractFeature = getHistomPlusColoFeatures; //设置特征提取函数为getHistomPlusColoFeatures } } - void PlateJudge::LoadModel(std::string path) { //LoadModel函数用于加载SVM模型。 - if (path != std::string(kDefaultSvmPath)) { - if (!svm_->empty()) - svm_->clear(); - LOAD_SVM_MODEL(svm_, path); + void PlateJudge::LoadModel(std::string path) { //LoadModel函数用于加载SVM模型,函数接收一个字符串参数path,这个参数是SVM模型文件的路径 + if (path != std::string(kDefaultSvmPath)) { //检查输入的路径path是否与默认的SVM路径kDefaultSvmPath不同。如果不同,那么就需要加载新的SVM模型 + if (!svm_->empty()) //检查当前的SVM模型是否为空。如果不为空,那么在加载新的SVM模型之前,需要先清空当前的SVM模型 + svm_->clear(); //清空当前的SVM模型 + LOAD_SVM_MODEL(svm_, path); //加载新的SVM模型。LOAD_SVM_MODEL是一个宏,它接收两个参数:一个是SVM模型的指针,另一个是SVM模型文件的路径 } } // set the score of plate // 0 is plate, -1 is not. - int PlateJudge::plateSetScore(CPlate& plate) { //plateSetScore函数用于设置车牌的评分。 - Mat features; - extractFeature(plate.getPlateMat(), features); - float score = svm_->predict(features, noArray(), cv::ml::StatModel::Flags::RAW_OUTPUT); + int PlateJudge::plateSetScore(CPlate& plate) { //plateSetScore函数用于设置车牌的评分,接收一个CPlate类型的引用作为参数 + Mat features; //定义一个Mat类型的变量features,用于存储特征 + extractFeature(plate.getPlateMat(), features); //调用extractFeature方法提取车牌图像的特征,结果存储在features中 + float score = svm_->predict(features, noArray(), cv::ml::StatModel::Flags::RAW_OUTPUT); //使用SVM模型对特征进行预测,得到的结果存储在score中 //std::cout << "score:" << score << std::endl; + +//这是一个调试用的代码块,如果条件为真(此处为0,所以不会执行),则显示车牌图像,等待用户按键,然后销毁窗口 if (0) { imshow("plate", plate.getPlateMat()); waitKey(0); @@ -51,14 +55,15 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 // score is the distance of margin,below zero is plate, up is not // when score is below zero, the samll the value, the more possibliy to be a plate. plate.setPlateScore(score); - if (score < 0.5) return 0; + if (score < 0.5) return 0; //如果评分小于0.5,返回0,表示这是一个车牌;否则返回-1,表示这不是一个车牌 else return -1; } +//定义了一个名为plateJudge的成员函数,它属于PlateJudge类。该函数接收一个Mat类型的常量引用参数plateMat,这个参数是需要进行车牌判断的图像 int PlateJudge::plateJudge(const Mat& plateMat) { //plateJudge函数用于判断输入的图像是否为车牌。 - CPlate plate; - plate.setPlateMat(plateMat); - return plateSetScore(plate); + CPlate plate; //这行创建了一个CPlate类型的对象plate。CPlate是一个类,它可能包含车牌的相关信息,如车牌图像、车牌位置等 + plate.setPlateMat(plateMat); //调用了CPlate类的setPlateMat成员函数,将输入的图像plateMat设置为plate对象的车牌图像 + return plateSetScore(plate); //调用了PlateJudge类的plateSetScore成员函数,对plate对象进行评分,然后返回评分结果 } int PlateJudge::plateJudge(const std::vector &inVec, //inVec是输入的图像向量,resultVec是输出的结果向量。 @@ -107,18 +112,25 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 return 0; //结束循环并返回0,表示方法执行成功 } - // non-maximum suppression + // non-maximum suppression -->非极大值抑制 +//函数接收三个参数:一个CPlate对象的向量inVec(输入的车牌向量),一个CPlate对象的向量resultVec(输出的车牌向量),以及一个double类型的overlap(重叠阈值) void NMS(std::vector &inVec, std::vector &resultVec, double overlap) { //NMS函数实现了非极大值抑制,用于消除重叠的车牌。 - std::sort(inVec.begin(), inVec.end()); + std::sort(inVec.begin(), inVec.end()); //首先对输入的车牌向量进行排序 + +//然后遍历输入的车牌向量,对每一个车牌对象,获取其位置的边界矩形 std::vector::iterator it = inVec.begin(); for (; it != inVec.end(); ++it) { CPlate plateSrc = *it; //std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl; Rect rectSrc = plateSrc.getPlatePos().boundingRect(); + +//在内层循环中,对当前车牌对象之后的每一个车牌对象,也获取其位置的边界矩形 std::vector::iterator itc = it + 1; for (; itc != inVec.end();) { CPlate plateComp = *itc; Rect rectComp = plateComp.getPlatePos().boundingRect(); + +//计算两个边界矩形的交并比(Intersection over Union,IoU),如果IoU大于设定的重叠阈值,那么就从输入的车牌向量中删除当前的车牌对象;否则,继续处理下一个车牌对象 float iou = computeIOU(rectSrc, rectComp); if (iou > overlap) { itc = inVec.erase(itc); @@ -128,19 +140,23 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 } } } - resultVec = inVec; + resultVec = inVec; //最后,将处理后的车牌向量赋值给输出的车牌向量 } - // judge plate using nms + // judge plate using nms --> 使用非极大值抑制进行车牌识别 +// 定义了一个名为plateJudgeUsingNMS的成员函数,它属于PlateJudge类。该函数使用非极大值抑制进行车牌识别,接收一个CPlate对象的向量inVec(输入的车牌向量)、一个CPlate对象的向量resultVec(输出的车牌向量)和一个整数maxPlates(最大车牌数量)作为参数 int PlateJudge::plateJudgeUsingNMS(const std::vector &inVec, std::vector &resultVec, int maxPlates) { //plateJudgeUsingNMS函数使用非极大值抑制进行车牌识别。 - std::vector plateVec; - int num = inVec.size(); - bool useCascadeJudge = true; + std::vector plateVec; // 创建一个CPlate对象的向量,用于存储识别出的车牌 + int num = inVec.size(); // 获取输入向量的大小 + bool useCascadeJudge = true; // 定义一个布尔变量,表示是否使用级联判断 +// 遍历输入向量中的每一个元素 for (int j = 0; j < num; j++) { - CPlate plate = inVec[j]; - Mat inMat = plate.getPlateMat(); - int result = plateSetScore(plate); + CPlate plate = inVec[j]; // 获取当前的CPlate对象 + Mat inMat = plate.getPlateMat(); // 获取当前CPlate对象的车牌图像 + int result = plateSetScore(plate); // 对当前的CPlate对象进行评分 + +// 如果评分结果为0,表示这是一个车牌 if (0 == result) { if (0) { imshow("inMat", inMat); @@ -148,17 +164,22 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 destroyWindow("inMat"); } - if (plate.getPlateLocateType() == CMSER) { - int w = inMat.cols; - int h = inMat.rows; + if (plate.getPlateLocateType() == CMSER) { // 如果plate的定位类型为CMSER + int w = inMat.cols; // 获取图像的宽度 + int h = inMat.rows; // 获取图像的高度 + // 对图像进行裁剪 Mat tmpmat = inMat(Rect_(w * 0.05, h * 0.1, w * 0.9, h * 0.8)); - Mat tmpDes = inMat.clone(); - resize(tmpmat, tmpDes, Size(inMat.size())); - plate.setPlateMat(tmpDes); + Mat tmpDes = inMat.clone(); // 克隆图像 + resize(tmpmat, tmpDes, Size(inMat.size())); // 调整图像大小 + plate.setPlateMat(tmpDes); // 设置plate的车牌图像为调整后的图像 + + // 如果使用级联判断 if (useCascadeJudge) { - int resultCascade = plateSetScore(plate); + int resultCascade = plateSetScore(plate); // 对调整后的图像进行评分 + // 如果plate的定位类型不是CMSER,将plate的车牌图像设置为原图像 if (plate.getPlateLocateType() != CMSER) plate.setPlateMat(inMat); + // 如果级联评分结果为0,将plate添加到plateVec中 if (resultCascade == 0) { if (0) { imshow("tmpDes", tmpDes); @@ -168,22 +189,24 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 plateVec.push_back(plate); } } - else + else // 如果不使用级联判断,直接将plate添加到plateVec中 plateVec.push_back(plate); } - else + else // 如果plate的定位类型不是CMSER,直接将plate添加到plateVec中 plateVec.push_back(plate); } } - std::vector reDupPlateVec; - double overlap = 0.5; + std::vector reDupPlateVec; // 创建一个CPlate对象的向量,用于存储非极大值抑制后的结果 + double overlap = 0.5; // 定义重叠阈值 // double overlap = CParams::instance()->getParam1f(); // use NMS to get the result plates +// 使用非极大值抑制处理plateVec,结果存储在reDupPlateVec中 NMS(plateVec, reDupPlateVec, overlap); - // sort the plates due to their scores + // sort the plates due to their scores --> 根据评分对reDupPlateVec进行排序 std::sort(reDupPlateVec.begin(), reDupPlateVec.end()); // output the plate judge plates +// 遍历reDupPlateVec,将结果添加到resultVec中,直到达到最大车牌数量 std::vector::iterator it = reDupPlateVec.begin(); int count = 0; for (; it != reDupPlateVec.end(); ++it) { @@ -197,6 +220,6 @@ namespace easypr { //这部分代码实现了单例模式,确保PlateJudge类 if (count >= maxPlates) break; } - return 0; + return 0; // 返回0,表示函数执行成功 } }