|
|
|
@ -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<Mat> &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<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();
|
|
|
|
|
for (; it != inVec.end(); ++it) {
|
|
|
|
|
CPlate plateSrc = *it;
|
|
|
|
|
//std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl;
|
|
|
|
|
Rect rectSrc = plateSrc.getPlatePos().boundingRect();
|
|
|
|
|
|
|
|
|
|
//在内层循环中,对当前车牌对象之后的每一个车牌对象,也获取其位置的边界矩形
|
|
|
|
|
std::vector<CPlate>::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<CPlate> &inVec, std::vector<CPlate> &resultVec, int maxPlates) { //plateJudgeUsingNMS函数使用非极大值抑制进行车牌识别。
|
|
|
|
|
std::vector<CPlate> plateVec;
|
|
|
|
|
int num = inVec.size();
|
|
|
|
|
bool useCascadeJudge = true;
|
|
|
|
|
std::vector<CPlate> 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_<double>(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<CPlate> reDupPlateVec;
|
|
|
|
|
double overlap = 0.5;
|
|
|
|
|
std::vector<CPlate> 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<CPlate>::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,表示函数执行成功
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|