|
|
|
@ -809,6 +809,8 @@ namespace easypr {
|
|
|
|
|
return show;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 对输入图像进行预处理
|
|
|
|
|
// 预处理包括创建一个单位矩阵,计算图像的最大维度,对输入图像进行仿射变换,然后将变换后的图像缩放到指定的字符大小
|
|
|
|
|
Mat preprocessChar(Mat in, int char_size) {
|
|
|
|
|
// Remap image
|
|
|
|
|
int h = in.rows; // 获取输入图像的行数
|
|
|
|
@ -831,6 +833,9 @@ namespace easypr {
|
|
|
|
|
return out; // 返回处理后的图像
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 接受一个特定的矩形,然后计算新的宽度(为原宽度的110%)
|
|
|
|
|
// 新的x坐标(为原x坐标减去新宽度的110%),如果新的x坐标小于0,将其设置为0
|
|
|
|
|
// 然后创建一个新的矩形,并返回
|
|
|
|
|
Rect GetChineseRect(const Rect rectSpe) {
|
|
|
|
|
int height = rectSpe.height; // 获取特定矩形的高度
|
|
|
|
|
float newwidth = rectSpe.width * 1.10f; // 计算新的宽度,为原宽度的110%
|
|
|
|
@ -845,6 +850,7 @@ namespace easypr {
|
|
|
|
|
return a; // 返回新的矩形
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 接受一个矩形,然后计算字符的宽高比,矩形的宽高比……
|
|
|
|
|
bool verifyCharSizes(Rect r) {
|
|
|
|
|
// Char sizes 45x90
|
|
|
|
|
float aspect = 45.0f / 90.0f; // 计算字符的宽高比
|
|
|
|
@ -866,7 +872,9 @@ namespace easypr {
|
|
|
|
|
return false; // 否则返回false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算图像宽度和最大宽度的比例,图像高度和最大高度的比例,获取宽度和高度比例中的最大值
|
|
|
|
|
// 计算新的宽度和新的高度,将图像缩放到新的大小,设置缩放比例
|
|
|
|
|
// 如果图像的大小已经小于或等于最大大小,直接返回原图像,设置缩放比例为1.0
|
|
|
|
|
Mat scaleImage(const Mat &image, const Size &maxSize, double &scale_ratio) {
|
|
|
|
|
Mat ret;
|
|
|
|
|
|
|
|
|
@ -890,49 +898,49 @@ namespace easypr {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Scale back RotatedRect
|
|
|
|
|
// 将一个旋转矩形(RotatedRect)按照给定的缩放比例进行反向缩放,返回一个新的旋转矩形
|
|
|
|
|
RotatedRect scaleBackRRect(const RotatedRect &rr, const float scale_ratio) {
|
|
|
|
|
float width = rr.size.width * scale_ratio;
|
|
|
|
|
float height = rr.size.height * scale_ratio;
|
|
|
|
|
float x = rr.center.x * scale_ratio;
|
|
|
|
|
float y = rr.center.y * scale_ratio;
|
|
|
|
|
RotatedRect mserRect(Point2f(x, y), Size2f(width, height), rr.angle);
|
|
|
|
|
float width = rr.size.width * scale_ratio; // 根据缩放比例计算原始宽度
|
|
|
|
|
float height = rr.size.height * scale_ratio; // 根据缩放比例计算原始高度
|
|
|
|
|
float x = rr.center.x * scale_ratio; // 根据缩放比例计算原始中心点的x坐标
|
|
|
|
|
float y = rr.center.y * scale_ratio; // 根据缩放比例计算原始中心点的y坐标
|
|
|
|
|
RotatedRect mserRect(Point2f(x, y), Size2f(width, height), rr.angle); // 创建一个新的RotatedRect对象,使用原始的尺寸和角度
|
|
|
|
|
|
|
|
|
|
return mserRect;
|
|
|
|
|
return mserRect; // 返回新的RotatedRect对象
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证一个矩形是否符合预设的车牌尺寸,包括面积和宽高比,如果符合则返回true,否则返回false
|
|
|
|
|
bool verifyPlateSize(Rect mr) {
|
|
|
|
|
float error = 0.6f;
|
|
|
|
|
float error = 0.6f; // 设置误差范围
|
|
|
|
|
// Spain car plate size: 52x11 aspect 4,7272
|
|
|
|
|
// China car plate size: 440mm*140mm,aspect 3.142857
|
|
|
|
|
|
|
|
|
|
// Real car plate size: 136 * 32, aspect 4
|
|
|
|
|
float aspect = 3.75;
|
|
|
|
|
float aspect = 3.75; // 设置期望的宽高比
|
|
|
|
|
|
|
|
|
|
// Set a min and max area. All other patchs are discarded
|
|
|
|
|
// int min= 1*aspect*1; // minimum area
|
|
|
|
|
// int max= 2000*aspect*2000; // maximum area
|
|
|
|
|
int min = 34 * 8 * 1; // minimum area
|
|
|
|
|
int max = 34 * 8 * 200; // maximum area
|
|
|
|
|
int min = 34 * 8 * 1; // 设置最小面积
|
|
|
|
|
int max = 34 * 8 * 200; // 设置最大面积
|
|
|
|
|
|
|
|
|
|
// Get only patchs that match to a respect ratio.
|
|
|
|
|
float rmin = aspect - aspect * error;
|
|
|
|
|
float rmax = aspect + aspect * error;
|
|
|
|
|
|
|
|
|
|
float area = float(mr.height * mr.width);
|
|
|
|
|
float r = (float) mr.width / (float) mr.height;
|
|
|
|
|
if (r < 1) r = (float) mr.height / (float) mr.width;
|
|
|
|
|
float rmin = aspect - aspect * error; // 计算最小宽高比
|
|
|
|
|
float rmax = aspect + aspect * error; // 计算最大宽高比
|
|
|
|
|
|
|
|
|
|
// cout << "area:" << area << endl;
|
|
|
|
|
// cout << "r:" << r << endl;
|
|
|
|
|
float area = float(mr.height * mr.width); // 计算矩形的面积
|
|
|
|
|
float r = (float) mr.width / (float) mr.height; // 计算矩形的宽高比
|
|
|
|
|
if (r < 1) r = (float) mr.height / (float) mr.width; // 如果宽高比小于1,取其倒数
|
|
|
|
|
|
|
|
|
|
// 判断矩形的面积和宽高比是否在指定的范围内
|
|
|
|
|
if ((area < min || area > max) || (r < rmin || r > rmax))
|
|
|
|
|
return false;
|
|
|
|
|
return false; // 如果不在指定范围内,返回false
|
|
|
|
|
else
|
|
|
|
|
return true;
|
|
|
|
|
return true; // 如果在指定范围内,返回true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 接收一个RotatedRect对象和一个布尔值showDebug作为参数
|
|
|
|
|
// 该函数的主要目的是验证一个旋转矩形(通常是车牌)是否符合预设的尺寸和比例
|
|
|
|
|
bool verifyRotatedPlateSizes(RotatedRect mr, bool showDebug) {
|
|
|
|
|
float error = 0.65f;
|
|
|
|
|
float error = 0.65f; // 定义误差值error,以及车牌的宽高比aspect
|
|
|
|
|
// Spain car plate size: 52x11 aspect 4,7272
|
|
|
|
|
// China car plate size: 440mm*140mm,aspect 3.142857
|
|
|
|
|
|
|
|
|
@ -946,6 +954,7 @@ namespace easypr {
|
|
|
|
|
//int max = 34 * 8 * 200; // maximum area
|
|
|
|
|
|
|
|
|
|
// Get only patchs that match to a respect ratio.
|
|
|
|
|
// 计算最小和最大的面积min和max,以及最小和最大的宽高比aspect_min和aspect_max
|
|
|
|
|
float aspect_min = aspect - aspect * error;
|
|
|
|
|
float aspect_max = aspect + aspect * error;
|
|
|
|
|
|
|
|
|
@ -954,27 +963,28 @@ namespace easypr {
|
|
|
|
|
|
|
|
|
|
float min = float(width_min * width_min / aspect_max); // minimum area
|
|
|
|
|
float max = float(width_max * width_max / aspect_min); // maximum area
|
|
|
|
|
|
|
|
|
|
// 获取旋转矩形的宽度、高度、面积、宽高比和角度
|
|
|
|
|
float width = mr.size.width;
|
|
|
|
|
float height = mr.size.height;
|
|
|
|
|
float area = width * height;
|
|
|
|
|
|
|
|
|
|
float ratio = width / height;
|
|
|
|
|
float angle = mr.angle;
|
|
|
|
|
if (ratio < 1) {
|
|
|
|
|
if (ratio < 1) { // 如果宽高比小于1,交换宽度和高度的值,并调整角度
|
|
|
|
|
swap(width, height);
|
|
|
|
|
ratio = width / height;
|
|
|
|
|
|
|
|
|
|
angle = 90.f + angle;
|
|
|
|
|
//std::cout << "angle:" << angle << std::endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 定义角度的最小和最大值
|
|
|
|
|
float angle_min = -60.f;
|
|
|
|
|
float angle_max = 60.f;
|
|
|
|
|
|
|
|
|
|
//std::cout << "aspect_min:" << aspect_min << std::endl;
|
|
|
|
|
//std::cout << "aspect_max:" << aspect_max << std::endl;
|
|
|
|
|
|
|
|
|
|
// 通过一系列的条件判断,验证旋转矩形是否符合预设的尺寸和比例
|
|
|
|
|
// 如果不符合,返回false;如果符合,返回true
|
|
|
|
|
if (area < min || area > max) {
|
|
|
|
|
if (0 && showDebug) {
|
|
|
|
|
std::cout << "area < min || area > max: " << area << std::endl;
|
|
|
|
@ -1007,27 +1017,30 @@ namespace easypr {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! non-maximum suppression
|
|
|
|
|
// 该函数实现了非极大值抑制(Non-Maximum Suppression,NMS)的功能
|
|
|
|
|
// 非极大值抑制是一种常用于目标检测中的技术,用于消除多余的重叠区域
|
|
|
|
|
// 该函数主要删除inVec中重叠度过高的CCharacter对象,只保留最具代表性的对象
|
|
|
|
|
void NMStoCharacter(std::vector<CCharacter> &inVec, double overlap) {
|
|
|
|
|
|
|
|
|
|
std::sort(inVec.begin(), inVec.end());
|
|
|
|
|
|
|
|
|
|
// 函数接受两个参数,一个是CCharacter对象的向量inVec,另一个是重叠阈值overlap
|
|
|
|
|
std::sort(inVec.begin(), inVec.end()); // 对inVec进行排序
|
|
|
|
|
// 遍历inVec中的每一个CCharacter对象
|
|
|
|
|
std::vector<CCharacter>::iterator it = inVec.begin();
|
|
|
|
|
for (; it != inVec.end(); ++it) {
|
|
|
|
|
CCharacter charSrc = *it;
|
|
|
|
|
CCharacter charSrc = *it; // 对于每一个CCharacter对象,获取其位置信息rectSrc
|
|
|
|
|
//std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl;
|
|
|
|
|
Rect rectSrc = charSrc.getCharacterPos();
|
|
|
|
|
|
|
|
|
|
// 遍历当前CCharacter对象之后的所有CCharacter对象
|
|
|
|
|
std::vector<CCharacter>::iterator itc = it + 1;
|
|
|
|
|
|
|
|
|
|
for (; itc != inVec.end();) {
|
|
|
|
|
CCharacter charComp = *itc;
|
|
|
|
|
CCharacter charComp = *itc; // 对于每一个后续的CCharacter对象,也获取其位置信息rectComp
|
|
|
|
|
Rect rectComp = charComp.getCharacterPos();
|
|
|
|
|
//Rect rectInter = rectSrc & rectComp;
|
|
|
|
|
//Rect rectUnion = rectSrc | rectComp;
|
|
|
|
|
//double r = double(rectInter.area()) / double(rectUnion.area());
|
|
|
|
|
|
|
|
|
|
// 计算当前CCharacter对象与后续CCharacter对象的交并比(Intersection over Union,IoU)
|
|
|
|
|
float iou = computeIOU(rectSrc, rectComp);
|
|
|
|
|
|
|
|
|
|
// 如果IoU大于设定的阈值overlap,则删除后续的CCharacter对象。否则,继续检查下一个CCharacter对象
|
|
|
|
|
if (iou > overlap) {
|
|
|
|
|
itc = inVec.erase(itc);
|
|
|
|
|
} else {
|
|
|
|
|