core_func.cpp评注WXP2.0 #21

Merged
p74f62umr merged 2 commits from wuxiuping_branch into develop 11 months ago

@ -467,61 +467,62 @@ namespace easypr {
// 该函数用于清除图像的边界
// 接受一个Mat类型的图像img和一个Rect类型的裁剪矩形cropRect作为参数
void clearBorder(const Mat &img, Rect& cropRect) {
// 获取图像的行数和列数
int r = img.rows;
int c = img.cols;
// 创建一个全零的单通道矩阵boder用于存储每一行是否为边界
Mat boder = Mat::zeros(1, r, CV_8UC1);
// 设置一个阈值,如果一行中像素值没有变化的次数超过这个阈值,那么认为这一行是边界
const int noJunpCount_thresh = int(0.15f * c);
// if nojumpcount >
// 遍历图像的每一行计算每一行中像素值没有变化的次数如果这个次数超过阈值那么认为这一行是边界将boder对应的位置设为1
for (int i = 0; i < r; i++) {
int nojumpCount = 0;
int isBorder = 0;
for (int j = 0; j < c - 1; j++) {
if (img.at<char>(i, j) == img.at<char>(i, j + 1))
nojumpCount++;
if (nojumpCount > noJunpCount_thresh) {
nojumpCount = 0;
isBorder = 1;
break;
void clearBorder(const Mat &img, Rect& cropRect) {
// 获取图像的行数和列数
int r = img.rows;
int c = img.cols;
// 创建一个全零的单通道矩阵boder用于存储每一行是否为边界
Mat boder = Mat::zeros(1, r, CV_8UC1);
// 设置一个阈值,如果一行中像素值没有变化的次数超过这个阈值,那么认为这一行是边界
const int noJunpCount_thresh = int(0.15f * c);
// if nojumpcount >
// 遍历图像的每一行计算每一行中像素值没有变化的次数如果这个次数超过阈值那么认为这一行是边界将boder对应的位置设为1
for (int i = 0; i < r; i++) {
int nojumpCount = 0;
int isBorder = 0;
for (int j = 0; j < c - 1; j++) {
if (img.at<char>(i, j) == img.at<char>(i, j + 1))
nojumpCount++;
if (nojumpCount > noJunpCount_thresh) {
nojumpCount = 0;
isBorder = 1;
break;
}
}
boder.at<char>(i) = (char) isBorder;
}
boder.at<char>(i) = (char) isBorder;
}
// 设置上下边界的搜索范围只在图像的中间80%的区域内搜索边界
const int mintop = int(0.1f * r);
const int maxtop = int(0.9f * r);
// 初始化裁剪矩形的上下边界
int minMatTop = 0;
int maxMatTop = r - 1;
// 从上到下搜索上边界
for (int i = 0; i < mintop; i++) {
if (boder.at<char>(i) == 1) {
minMatTop = i;
// 设置上下边界的搜索范围只在图像的中间80%的区域内搜索边界
const int mintop = int(0.1f * r);
const int maxtop = int(0.9f * r);
// 初始化裁剪矩形的上下边界
int minMatTop = 0;
int maxMatTop = r - 1;
// 从上到下搜索上边界
for (int i = 0; i < mintop; i++) {
if (boder.at<char>(i) == 1) {
minMatTop = i;
}
}
}
// 从下到上搜索下边界
for (int i = r - 1; i > maxtop; i--) {
if (boder.at<char>(i) == 1) {
maxMatTop = i;
// 从下到上搜索下边界
for (int i = r - 1; i > maxtop; i--) {
if (boder.at<char>(i) == 1) {
maxMatTop = i;
}
}
}
// 根据找到的上下边界创建裁剪矩形
cropRect = Rect(0, minMatTop, c, maxMatTop - minMatTop + 1);
// 根据找到的上下边界创建裁剪矩形
cropRect = Rect(0, minMatTop, c, maxMatTop - minMatTop + 1);
}
}
//用于清除图像中的噪声
//用于清除图像中的噪声—————函数的目的是找到图像中的有效区域,即去除上下边缘的噪声
//函数接受一个Mat类型的图像mask和两个整数引用top和bottom作为参数
void clearLiuDing(Mat mask, int &top, int &bottom) {
const int x = 7;
const int x = 7; //定义一个常量x值为7
//遍历图像的上半部分计算每一行的跳变次数即像素值从0变为255或从255变为0的次数和白色像素的数量
for (int i = 0; i < mask.rows / 2; i++) {
int whiteCount = 0;
int jumpCount = 0;
@ -532,18 +533,20 @@ void clearBorder(const Mat &img, Rect& cropRect) {
whiteCount++;
}
}
//如果跳变次数小于x且白色像素占该行的比例大于15%或者白色像素的数量小于4那么将top设为当前行数
if ((jumpCount < x && whiteCount * 1.0 / mask.cols > 0.15) ||
whiteCount < 4) {
top = i;
}
}
//将top减1如果top小于0那么将top设为0
top -= 1;
if (top < 0) {
top = 0;
}
// ok, find top and bottom boudnadry
//遍历图像的下半部分,计算每一行的跳变次数和白色像素的数量
for (int i = mask.rows - 1; i >= mask.rows / 2; i--) {
int jumpCount = 0;
int whiteCount = 0;
@ -553,92 +556,101 @@ void clearBorder(const Mat &img, Rect& cropRect) {
whiteCount++;
}
}
//如果跳变次数小于x且白色像素占该行的比例大于15%或者白色像素的数量小于4那么将bottom设为当前行数
if ((jumpCount < x && whiteCount * 1.0 / mask.cols > 0.15) ||
whiteCount < 4) {
bottom = i;
}
}
//将bottom加1如果bottom大于等于图像的行数那么将bottom设为图像的行数减1
bottom += 1;
if (bottom >= mask.rows) {
bottom = mask.rows - 1;
}
//如果top大于等于bottom那么将top设为0bottom设为图像的行数减1
if (top >= bottom) {
top = 0;
bottom = mask.rows - 1;
}
}
int ThresholdOtsu(Mat mat) {
int height = mat.rows;
int width = mat.cols;
int ThresholdOtsu(Mat mat) { // 定义函数ThresholdOtsu参数为OpenCV的Mat类型返回值为int类型
int height = mat.rows; // 获取图像的行数
int width = mat.cols; // 获取图像的列数
// histogram
float histogram[256] = {0};
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
unsigned char p = (unsigned char) ((mat.data[i * mat.step[0] + j]));
histogram[p]++;
float histogram[256] = {0}; // 初始化一个长度为256的浮点数数组用于存储图像的灰度直方图
for (int i = 0; i < height; i++) { // 遍历图像的每一行
for (int j = 0; j < width; j++) { // 遍历图像的每一列
unsigned char p = (unsigned char) ((mat.data[i * mat.step[0] + j])); // 获取当前像素的灰度值
histogram[p]++; // 对应的灰度直方图的值加1
}
}
// normalize histogram
int size = height * width;
for (int i = 0; i < 256; i++) {
histogram[i] = histogram[i] / size;
int size = height * width; // 计算图像的总像素数
for (int i = 0; i < 256; i++) { // 遍历灰度直方图
histogram[i] = histogram[i] / size; // 将灰度直方图的每一个值除以总像素数,得到每个灰度值的概率
}
// average pixel value
float avgValue = 0;
for (int i = 0; i < 256; i++) {
avgValue += i * histogram[i];
float avgValue = 0; // 初始化平均灰度值为0
for (int i = 0; i < 256; i++) { // 遍历灰度直方图
avgValue += i * histogram[i]; // 计算平均灰度值
}
int thresholdV;
float maxVariance = 0;
float w = 0, u = 0;
for (int i = 0; i < 256; i++) {
w += histogram[i];
u += i * histogram[i];
int thresholdV; // 初始化阈值
float maxVariance = 0; // 初始化最大方差为0
float w = 0, u = 0; // 初始化两个变量w和u分别用于存储阈值以下和阈值以上的像素数的累积和
for (int i = 0; i < 256; i++) { // 遍历灰度直方图
w += histogram[i]; // 计算阈值以下的像素数的累积和
u += i * histogram[i]; // 计算阈值以上的像素数的累积和
float t = avgValue * w - u;
float variance = t * t / (w * (1 - w));
if (variance > maxVariance) {
maxVariance = variance;
thresholdV = i;
float t = avgValue * w - u; // 计算类间方差
float variance = t * t / (w * (1 - w)); // 计算类间方差
if (variance > maxVariance) { // 如果当前的类间方差大于最大方差
maxVariance = variance; // 更新最大方差
thresholdV = i; // 更新阈值
}
}
return thresholdV;
return thresholdV; // 返回阈值
}
//该函数接受一个Mat类型的输入图像并返回一个经过直方图均衡化处理的图像
Mat histeq(Mat in) {
Mat out(in.size(), in.type());
if (in.channels() == 3) {
Mat out(in.size(), in.type()); // 创建一个与输入图像同样大小和类型的Mat对象out用于存储处理后的图像
if (in.channels() == 3) { //判断输入图像的通道数如果为3即为彩色图像执行以下操作
//创建一个Mat对象hsv和一个Mat向量hsvSplit然后使用cvtColor函数将输入图像从BGR色彩空间转换为HSV色彩空间结果存储在hsv中
Mat hsv;
std::vector<cv::Mat> hsvSplit;
cvtColor(in, hsv, CV_BGR2HSV);
//使用split函数将hsv图像的三个通道分离到hsvSplit向量中然后对V通道亮度进行直方图均衡化
split(hsv, hsvSplit);
equalizeHist(hsvSplit[2], hsvSplit[2]);
//使用merge函数将处理后的三个通道合并回hsv然后使用cvtColor函数将hsv图像从HSV色彩空间转换回BGR色彩空间结果存储在out中
merge(hsvSplit, hsv);
cvtColor(hsv, out, CV_HSV2BGR);
} else if (in.channels() == 1) {
} else if (in.channels() == 1) { //如果输入图像的通道数为1即为灰度图像执行以下操作
//直接对输入图像进行直方图均衡化结果存储在out中
equalizeHist(in, out);
}
return out;
return out; // 返回处理后的图像
}
#define HORIZONTAL 1
#define VERTICAL 0
Mat CutTheRect(Mat &in, Rect &rect) {
//从输入图像中裁剪出一个指定的矩形区域,并将其放置在一个新的正方形图像的中心位置
Mat CutTheRect(Mat &in, Rect &rect) { //接受一个Mat类型的引用in和一个Rect类型的引用rect作为参数
int size = in.cols; // (rect.width>rect.height)?rect.width:rect.height;
//创建了一个新的Mat对象dstMat其大小为sizexsize类型为CV_8UC18位无符号单通道并将其所有元素设置为0
Mat dstMat(size, size, CV_8UC1);
dstMat.setTo(Scalar(0, 0, 0));
//计算矩形rect在新图像中的起始位置(x, y)
int x = (int) floor((float) (size - rect.width) / 2.0f);
int y = (int) floor((float) (size - rect.height) / 2.0f);
//遍历矩形rect中的每一个像素并将其复制到新图像dstMat的相应位置
for (int i = 0; i < rect.height; ++i) {
for (int j = 0; j < rect.width; ++j) {
@ -647,123 +659,129 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
}
//
//返回处理后的新图像dstMat
return dstMat;
}
//其目的是在输入图像中找到一个矩形区域该区域包含了图像中所有像素值大于20的部分
//函数接受一个 Mat 类型的引用 in 作为参数,返回一个 Rect 类型的对象
Rect GetCenterRect(Mat &in) {
Rect _rect;
int top = 0;
int bottom = in.rows - 1;
Rect _rect; // 定义一个矩形对象
// find the center rect
int top = 0; // 初始化矩形的上边界
int bottom = in.rows - 1; // 初始化矩形的下边界为图像的行数减1
for (int i = 0; i < in.rows; ++i) {
bool bFind = false;
for (int j = 0; j < in.cols; ++j) {
if (in.data[i * in.step[0] + j] > 20) {
top = i;
bFind = true;
// 从上到下遍历图像的每一行找到第一个包含像素值大于20的行将其行数设为矩形的上边界
for (int i = 0; i < in.rows; ++i) {
bool bFind = false;
for (int j = 0; j < in.cols; ++j) {
if (in.data[i * in.step[0] + j] > 20) {
top = i;
bFind = true;
break;
}
}
if (bFind) {
break;
}
}
if (bFind) {
break;
}
}
for (int i = in.rows - 1;
i >= 0;
--i) {
bool bFind = false;
for (int j = 0; j < in.cols; ++j) {
if (in.data[i * in.step[0] + j] > 20) {
bottom = i;
bFind = true;
// 从下到上遍历图像的每一行找到第一个包含像素值大于20的行将其行数设为矩形的下边界
for (int i = in.rows - 1; i >= 0; --i) {
bool bFind = false;
for (int j = 0; j < in.cols; ++j) {
if (in.data[i * in.step[0] + j] > 20) {
bottom = i;
bFind = true;
break;
}
}
if (bFind) {
break;
}
}
if (bFind) {
break;
}
}
int left = 0; // 初始化矩形的左边界
int right = in.cols - 1; // 初始化矩形的右边界为图像的列数减1
int left = 0;
int right = in.cols - 1;
for (int j = 0; j < in.cols; ++j) {
bool bFind = false;
for (int i = 0; i < in.rows; ++i) {
if (in.data[i * in.step[0] + j] > 20) {
left = j;
bFind = true;
// 从左到右遍历图像的每一列找到第一个包含像素值大于20的列将其列数设为矩形的左边界
for (int j = 0; j < in.cols; ++j) {
bool bFind = false;
for (int i = 0; i < in.rows; ++i) {
if (in.data[i * in.step[0] + j] > 20) {
left = j;
bFind = true;
break;
}
}
if (bFind) {
break;
}
}
if (bFind) {
break;
}
}
for (int j = in.cols - 1;
j >= 0;
--j) {
bool bFind = false;
for (int i = 0; i < in.rows; ++i) {
if (in.data[i * in.step[0] + j] > 20) {
right = j;
bFind = true;
// 从右到左遍历图像的每一列找到第一个包含像素值大于20的列将其列数设为矩形的右边界
for (int j = in.cols - 1; j >= 0; --j) {
bool bFind = false;
for (int i = 0; i < in.rows; ++i) {
if (in.data[i * in.step[0] + j] > 20) {
right = j;
bFind = true;
break;
}
}
if (bFind) {
break;
}
}
if (bFind) {
break;
}
}
_rect.x = left;
_rect.y = top;
_rect.width = right - left + 1;
_rect.height = bottom - top + 1;
// 设置矩形的位置和大小
_rect.x = left;
_rect.y = top;
_rect.width = right - left + 1;
_rect.height = bottom - top + 1;
return _rect;
return _rect; // 返回矩形对象
}
//计算图像中大于给定阈值的像素数量
float countOfBigValue(Mat &mat, int iValue) {
float iCount = 0.0;
// 如果图像的行数大于1遍历每一行
if (mat.rows > 1) {
for (int i = 0; i < mat.rows; ++i) {
// 如果当前像素的值大于阈值计数器加1
if (mat.data[i * mat.step[0]] > iValue) {
iCount += 1.0;
}
}
return iCount;
} else {
// 如果图像的行数不大于1遍历每一列
for (int i = 0; i < mat.cols; ++i) {
// 如果当前像素的值大于阈值计数器加1
if (mat.data[i] > iValue) {
iCount += 1.0;
}
}
return iCount;
}
}
//计算图像的投影直方图
Mat ProjectedHistogram(Mat img, int t, int threshold) {
// 根据参数t的值确定直方图的长度
int sz = (t) ? img.rows : img.cols;
// 创建一个长度为sz的零矩阵用于存储直方图
Mat mhist = Mat::zeros(1, sz, CV_32F);
for (int j = 0; j < sz; j++) {
// 根据参数t的值确定是对图像的行还是列进行操作
Mat data = (t) ? img.row(j) : img.col(j);
// 计算大于阈值的像素数量,并存储在直方图中
mhist.at<float>(j) = countOfBigValue(data, threshold);
}
// Normalize histogram
// 归一化直方图
double min, max;
minMaxLoc(mhist, &min, &max);
@ -773,13 +791,17 @@ void clearBorder(const Mat &img, Rect& cropRect) {
return mhist;
}
//显示直方图
Mat showHistogram(const Mat &hist) {
int height = 32;
int width = hist.cols;
// 创建一个高度为32宽度为直方图长度的零矩阵用于显示直方图
Mat show = Mat::zeros(height, width, CV_8UC1);
for (int i = 0; i < width; i++) {
// 计算直方图的每一列的长度
int len = int((float) height * hist.at<float>(i));
for (int j = height - 1; j >= 0; j--) {
// 将直方图的每一列的长度以像素的形式显示出来
if (height - j <= len)
show.at<char>(j, i) = (char) 255;
}
@ -787,130 +809,138 @@ void clearBorder(const Mat &img, Rect& cropRect) {
return show;
}
// 对输入图像进行预处理
// 预处理包括创建一个单位矩阵,计算图像的最大维度,对输入图像进行仿射变换,然后将变换后的图像缩放到指定的字符大小
Mat preprocessChar(Mat in, int char_size) {
// Remap image
int h = in.rows;
int w = in.cols;
int h = in.rows; // 获取输入图像的行数
int w = in.cols; // 获取输入图像的列数
int charSize = char_size;
int charSize = char_size; // 获取字符大小
Mat transformMat = Mat::eye(2, 3, CV_32F);
int m = max(w, h);
transformMat.at<float>(0, 2) = float(m / 2 - w / 2);
Mat transformMat = Mat::eye(2, 3, CV_32F); // 创建一个2x3的单位矩阵
int m = max(w, h); // 获取图像的最大维度
transformMat.at<float>(0, 2) = float(m / 2 - w / 2); // 计算并设置变换矩阵的平移部分
transformMat.at<float>(1, 2) = float(m / 2 - h / 2);
Mat warpImage(m, m, in.type());
Mat warpImage(m, m, in.type()); // 创建一个新的图像大小为m*m类型与输入图像相同
warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR,
BORDER_CONSTANT, Scalar(0));
BORDER_CONSTANT, Scalar(0)); // 对输入图像进行仿射变换
Mat out;
cv::resize(warpImage, out, Size(charSize, charSize));
cv::resize(warpImage, out, Size(charSize, charSize)); // 将变换后的图像缩放到指定的字符大小
return out;
return out; // 返回处理后的图像
}
// 接受一个特定的矩形然后计算新的宽度为原宽度的110%
// 新的x坐标为原x坐标减去新宽度的110%如果新的x坐标小于0将其设置为0
// 然后创建一个新的矩形,并返回
Rect GetChineseRect(const Rect rectSpe) {
int height = rectSpe.height;
float newwidth = rectSpe.width * 1.10f;
int x = rectSpe.x;
int y = rectSpe.y;
int height = rectSpe.height; // 获取特定矩形的高度
float newwidth = rectSpe.width * 1.10f; // 计算新的宽度为原宽度的110%
int x = rectSpe.x; // 获取特定矩形的x坐标
int y = rectSpe.y; // 获取特定矩形的y坐标
int newx = x - int(newwidth * 1.10f);
newx = newx > 0 ? newx : 0;
int newx = x - int(newwidth * 1.10f); // 计算新的x坐标为原x坐标减去新宽度的110%
newx = newx > 0 ? newx : 0; // 如果新的x坐标小于0将其设置为0
Rect a(newx, y, int(newwidth), height);
Rect a(newx, y, int(newwidth), height); // 创建一个新的矩形
return a;
return a; // 返回新的矩形
}
// 接受一个矩形,然后计算字符的宽高比,矩形的宽高比……
bool verifyCharSizes(Rect r) {
// Char sizes 45x90
float aspect = 45.0f / 90.0f;
float charAspect = (float) r.width / (float) r.height;
float error = 0.35f;
float minHeight = 25.f;
float maxHeight = 50.f;
float aspect = 45.0f / 90.0f; // 计算字符的宽高比
float charAspect = (float) r.width / (float) r.height; // 计算矩形的宽高比
float error = 0.35f; // 设置误差范围
float minHeight = 25.f; // 设置最小高度
float maxHeight = 50.f; // 设置最大高度
// We have a different aspect ratio for number 1, and it can be ~0.2
float minAspect = 0.05f;
float maxAspect = aspect + aspect * error;
float minAspect = 0.05f; // 设置最小宽高比
float maxAspect = aspect + aspect * error; // 设置最大宽高比
// bb area
int bbArea = r.width * r.height;
int bbArea = r.width * r.height; // 计算矩形的面积
if (charAspect > minAspect && charAspect < maxAspect /*&&
r.rows >= minHeight && r.rows < maxHeight*/)
return true;
return true; // 如果矩形的宽高比在最小和最大宽高比之间返回true
else
return false;
return false; // 否则返回false
}
// 计算图像宽度和最大宽度的比例,图像高度和最大高度的比例,获取宽度和高度比例中的最大值
// 计算新的宽度和新的高度,将图像缩放到新的大小,设置缩放比例
// 如果图像的大小已经小于或等于最大大小直接返回原图像设置缩放比例为1.0
Mat scaleImage(const Mat &image, const Size &maxSize, double &scale_ratio) {
Mat ret;
if (image.cols > maxSize.width || image.rows > maxSize.height) {
double widthRatio = image.cols / (double) maxSize.width;
double heightRatio = image.rows / (double) maxSize.height;
double m_real_to_scaled_ratio = max(widthRatio, heightRatio);
double widthRatio = image.cols / (double) maxSize.width; // 计算图像宽度和最大宽度的比例
double heightRatio = image.rows / (double) maxSize.height; // 计算图像高度和最大高度的比例
double m_real_to_scaled_ratio = max(widthRatio, heightRatio); // 获取宽度和高度比例中的最大值
int newWidth = int(image.cols / m_real_to_scaled_ratio);
int newHeight = int(image.rows / m_real_to_scaled_ratio);
int newWidth = int(image.cols / m_real_to_scaled_ratio); // 计算新的宽度
int newHeight = int(image.rows / m_real_to_scaled_ratio); // 计算新的高度
cv::resize(image, ret, Size(newWidth, newHeight), 0, 0);
scale_ratio = m_real_to_scaled_ratio;
cv::resize(image, ret, Size(newWidth, newHeight), 0, 0); // 将图像缩放到新的大小
scale_ratio = m_real_to_scaled_ratio; // 设置缩放比例
} else {
ret = image;
scale_ratio = 1.0;
ret = image; // 如果图像的大小已经小于或等于最大大小,直接返回原图像
scale_ratio = 1.0; // 设置缩放比例为1.0
}
return ret;
return ret; // 返回处理后的图像
}
// 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*140mmaspect 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 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;
// 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*140mmaspect 3.142857
@ -924,6 +954,7 @@ void clearBorder(const Mat &img, Rect& cropRect) {
//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;
@ -932,27 +963,28 @@ void clearBorder(const Mat &img, Rect& cropRect) {
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;
@ -985,27 +1017,30 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
//! non-maximum suppression
// 该函数实现了非极大值抑制Non-Maximum SuppressionNMS的功能
// 非极大值抑制是一种常用于目标检测中的技术,用于消除多余的重叠区域
// 该函数主要删除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 UnionIoU
float iou = computeIOU(rectSrc, rectComp);
// 如果IoU大于设定的阈值overlap则删除后续的CCharacter对象。否则继续检查下一个CCharacter对象
if (iou > overlap) {
itc = inVec.erase(itc);
} else {

Loading…
Cancel
Save