pull/12/head
www-369 12 months ago
parent 7428e17395
commit 2ed8bbe608

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

Loading…
Cancel
Save