pull/14/head
www-369 1 year ago
parent 0d03268573
commit e8e1874816

@ -7,16 +7,21 @@
#include <ctime>
namespace easypr {
Mat colorMatch(const Mat &src, Mat &match, const Color r,
//用于在HSV颜色空间中匹配特定的颜色。它可以用于识别图像中的蓝色、黄色和白色区域
Mat colorMatch(const Mat &src, Mat &match, const Color r, //函数接受四个参数一个源图像src一个输出图像match一个颜色枚举r和一个布尔值adaptive_minsv
const bool adaptive_minsv) {
// if use adaptive_minsv
// min value of s and v is adaptive to h
//定义了一些常量包括最大的饱和度和亮度值max_sv参考的最小饱和度和亮度值minref_sv和绝对的最小饱和度和亮度值minabs_sv
const float max_sv = 255;
const float minref_sv = 64;
const float minabs_sv = 95; //95;
//定义了蓝色、黄色和白色在HSV颜色空间中的H值范围
// H range of blue
const int min_blue = 100; // 100
@ -32,18 +37,21 @@ namespace easypr {
const int min_white = 0; // 15
const int max_white = 30; // 40
//将源图像从BGR颜色空间转换到HSV颜色空间
Mat src_hsv;
// convert to HSV space
cvtColor(src, src_hsv, CV_BGR2HSV);
//将HSV图像分割成三个通道对V通道进行直方图均衡化然后再合并回去
std::vector<cv::Mat> hsvSplit;
split(src_hsv, hsvSplit);
equalizeHist(hsvSplit[2], hsvSplit[2]);
merge(hsvSplit, src_hsv);
// match to find the color
//根据输入的颜色枚举r设置H值的范围
int min_h = 0;
int max_h = 0;
switch (r) {
@ -64,9 +72,11 @@ namespace easypr {
break;
}
//计算H值的差值和平均值
float diff_h = float((max_h - min_h) / 2);
float avg_h = min_h + diff_h;
//获取图像的通道数、行数和列数。如果图像是连续的那么将列数乘以行数行数设置为1
int channels = src_hsv.channels();
int nRows = src_hsv.rows;
@ -77,11 +87,15 @@ namespace easypr {
nRows = 1;
}
//对图像进行遍历对每个像素进行颜色匹配。如果像素的颜色匹配成功那么将其设置为红色255否则设置为黑色0
//定义变量用于后续的计算和循环
int i, j;
uchar* p;
float s_all = 0;
float v_all = 0;
float count = 0;
//双重循环遍历图像的每个像素。对于每个像素我们获取其在HSV颜色空间中的H色相、S饱和度和V亮度
for (i = 0; i < nRows; ++i) {
p = src_hsv.ptr<uchar>(i);
for (j = 0; j < nCols; j += 3) {
@ -89,13 +103,19 @@ namespace easypr {
int S = int(p[j + 1]); // 0-255
int V = int(p[j + 2]); // 0-255
//此处计算了所有像素的S和V值的总和以及像素的数量
s_all += S;
v_all += V;
count++;
//以下这段代码是颜色匹配的核心部分。
//首先检查像素的H值是否在预设的范围内。
//如果在范围内计算H值与平均H值的差值然后根据这个差值和预设的阈值来计算最小的S和V值。
//如果像素的S和V值都在这个范围内认为这个像素的颜色匹配成功。
bool colorMatched = false;
if (H > min_h && H < max_h) {
if (H > min_h && H < max_h) { ////如果在范围内计算H值与平均H值的差值
float Hdiff = 0;
if (H > avg_h)
Hdiff = H - avg_h;
@ -105,7 +125,7 @@ namespace easypr {
float Hdiff_p = float(Hdiff) / diff_h;
float min_sv = 0;
if (true == adaptive_minsv)
if (true == adaptive_minsv) //然后根据这个差值和预设的阈值来计算最小的S和V值
min_sv =
minref_sv -
minref_sv / 2 *
@ -114,10 +134,11 @@ namespace easypr {
else
min_sv = minabs_sv; // add
if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv))
if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv)) //如果像素的S和V值都在这个范围内认为这个像素的颜色匹配成功
colorMatched = true;
}
//如果像素的颜色匹配成功将其设置为红色在HSV颜色空间中H=0S=0V=255表示红色。否则将其设置为黑色H=0S=0V=0
if (colorMatched == true) {
p[j] = 0;
p[j + 1] = 0;
@ -135,7 +156,8 @@ namespace easypr {
// cout << "avg_v:" << v_all / count << endl;
// get the final binary
//将处理后的HSV图像分割成三个通道取出V通道作为灰度图像并返回这个灰度图像
//在这个灰度图像中,匹配的颜色区域被标记为白色,其余区域为黑色
Mat src_grey;
std::vector<cv::Mat> hsvSplit_done;
split(src_hsv, hsvSplit_done);
@ -143,13 +165,16 @@ namespace easypr {
match = src_grey;
return src_grey;
return src_grey; //函数返回一个灰度图像,其中匹配的颜色区域被标记为白色,其余区域为黑色
}
//该函数用于在图像中找到左右边界。它通过计算图像中的白色像素数量来确定边界
//函数接受一个Mat类型的引用bound_threshold以及两个整型引用posLeft和posRight作为参数
bool bFindLeftRightBound1(Mat &bound_threshold, int &posLeft, int &posRight) {
//首先计算图像行数的20%作为一个跨度span
float span = bound_threshold.rows * 0.2f;
//然后从左到右遍历图像每次跳过3列计算每个跨度内的白色像素数量
for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) {
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
@ -159,14 +184,15 @@ namespace easypr {
}
}
}
//如果白色像素的比例超过15%,则认为找到了左边界
if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.15) {
posLeft = i;
break;
}
}
span = bound_threshold.rows * 0.2f;
span = bound_threshold.rows * 0.2f; //再次计算跨度,用于寻找右边界
// 接着从右到左遍历图像每次跳过2列计算每个跨度内的白色像素数量
for (int i = bound_threshold.cols - 1; i > span; i -= 2) {
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
@ -177,11 +203,12 @@ namespace easypr {
}
}
// 如果白色像素的比例超过6%,则认为找到了右边界
if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.06) {
posRight = i;
if (posRight + 5 < bound_threshold.cols) {
if (posRight + 5 < bound_threshold.cols) { // 如果右边界加5后仍在图像列数范围内则右边界加5
posRight = posRight + 5;
} else {
} else { // 否则右边界设为图像列数减1
posRight = bound_threshold.cols - 1;
}
@ -189,17 +216,22 @@ namespace easypr {
}
}
// 最后,如果左边界小于右边界,返回真,否则返回假
if (posLeft < posRight) {
return true;
}
return false;
}
bool bFindLeftRightBound(Mat &bound_threshold, int &posLeft, int &posRight) {
//该函数用于在图像中找到左右边界。它通过计算图像中的白色像素数量来确定边界
//函数接受三个参数一个Mat类型的引用bound_threshold以及两个整型引用posLeft和posRight
//函数返回一个布尔值,表示是否成功找到左右边界
bool bFindLeftRightBound(Mat &bound_threshold, int &posLeft, int &posRight) {
//首先计算图像行数的20%作为一个跨度span
float span = bound_threshold.rows * 0.2f;
//然后从左到右遍历图像每次跳过2列计算每个跨度内的白色像素数量。如果白色像素的比例超过36%,则认为找到了左边界
for (int i = 0; i < bound_threshold.cols - span - 1; i += 2) {
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
@ -214,9 +246,9 @@ namespace easypr {
break;
}
}
span = bound_threshold.rows * 0.2f;
span = bound_threshold.rows * 0.2f; // 再次计算跨度,用于寻找右边界
//接着从右到左遍历图像每次跳过2列计算每个跨度内的白色像素数量。如果白色像素的比例超过26%,则认为找到了右边界
for (int i = bound_threshold.cols - 1; i > span; i -= 2) {
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
@ -232,18 +264,22 @@ namespace easypr {
break;
}
}
//最后,如果左边界小于右边界,返回真,否则返回假
if (posLeft < posRight) {
return true;
}
return false;
}
bool bFindLeftRightBound2(Mat &bound_threshold, int &posLeft, int &posRight) {
// 该函数接受一个Mat类型的引用bound_threshold和两个整型引用posLeft和posRight作为参数
// 函数返回一个布尔值,表示是否成功找到左右边界
bool bFindLeftRightBound2(Mat &bound_threshold, int &posLeft, int &posRight) {
//计算图像行数的20%作为一个跨度span
float span = bound_threshold.rows * 0.2f;
for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) {
for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) { //从左到右遍历图像每次跳过3列
// 在每个跨度内,计算白色像素的数量
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
for (int l = i; l < i + span; l++) {
@ -252,15 +288,17 @@ namespace easypr {
}
}
}
// 如果白色像素的比例超过32%,则认为找到了左边界
if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.32) {
posLeft = i;
break;
}
}
//再次计算跨度,用于寻找右边界
span = bound_threshold.rows * 0.2f;
for (int i = bound_threshold.cols - 1; i > span; i -= 3) {
for (int i = bound_threshold.cols - 1; i > span; i -= 3) { // 从右到左遍历图像每次跳过3列
// 在每个跨度内,计算白色像素的数量
int whiteCount = 0;
for (int k = 0; k < bound_threshold.rows; k++) {
for (int l = i; l > i - span; l--) {
@ -269,46 +307,54 @@ namespace easypr {
}
}
}
// 如果白色像素的比例超过22%,则认为找到了右边界
if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.22) {
posRight = i;
break;
}
}
// 如果左边界小于右边界,返回真,否则返回假
if (posLeft < posRight) {
return true;
}
return false;
}
// 该函数接受一个Mat类型的引用src一个颜色枚举r一个布尔值adaptive_minsv和一个浮点数引用percent作为参数
// 函数返回一个布尔值,表示颜色是否匹配
bool plateColorJudge(const Mat &src, const Color r, const bool adaptive_minsv,
float &percent) {
// 定义一个常量thresh表示阈值
const float thresh = 0.45f;
// 调用colorMatch函数将源图像转换为灰度图像
Mat src_gray;
colorMatch(src, src_gray, r, adaptive_minsv);
// 计算灰度图像中非零像素的比例
percent =
float(countNonZero(src_gray)) / float(src_gray.rows * src_gray.cols);
// cout << "percent:" << percent << endl;
// 如果非零像素的比例大于阈值,返回真,否则返回假
if (percent > thresh)
return true;
else
return false;
}
// 用于判断图像中的颜色类型(蓝色、黄色或白色)
// 接受两个参数一个Mat类型的引用src源图像和一个布尔值adaptive_minsv
// 函数返回一个布尔值,表示颜色是否匹配。
Color getPlateType(const Mat &src, const bool adaptive_minsv) {
// 定义两个变量max_percent用于存储最大的颜色匹配百分比max_color用于存储对应的颜色。初始值分别设为0和UNKNOW
float max_percent = 0;
Color max_color = UNKNOWN;
// 定义三个变量用于存储蓝色、黄色和白色的匹配百分比。初始值都设为0
float blue_percent = 0;
float yellow_percent = 0;
float white_percent = 0;
// 调用plateColorJudge函数判断源图像中的颜色是否为蓝色、黄色或白色。如果是返回对应的颜色
if (plateColorJudge(src, BLUE, adaptive_minsv, blue_percent) == true) {
// cout << "BLUE" << endl;
return BLUE;
@ -320,7 +366,7 @@ namespace easypr {
true) {
// cout << "WHITE" << endl;
return WHITE;
} else {
} else { // 如果源图像中的颜色既不是蓝色、也不是黄色、也不是白色,那么默认返回蓝色
//std::cout << "OTHER" << std::endl;
/*max_percent = blue_percent > yellow_percent ? blue_percent : yellow_percent;
@ -332,37 +378,45 @@ namespace easypr {
}
}
// 该函数用于清除图像中的噪声,提高图像处理的准确性
// 接受一个Mat类型的引用img作为参数
// 该函数主要通过计算图像中每一行的跳变次数即像素值从0变为255或从255变为0的次数来判断是否为噪声如果跳变次数小于某个阈值那么就将这一行的所有像素值设为0即黑色
void clearLiuDingOnly(Mat &img) {
const int x = 7;
Mat jump = Mat::zeros(1, img.rows, CV_32F);
for (int i = 0; i < img.rows; i++) {
int jumpCount = 0;
int whiteCount = 0;
for (int j = 0; j < img.cols - 1; j++) {
if (img.at<char>(i, j) != img.at<char>(i, j + 1)) jumpCount++;
if (img.at<uchar>(i, j) == 255) {
const int x = 7; // 设置阈值为7
Mat jump = Mat::zeros(1, img.rows, CV_32F); // 创建一个大小为图像行数类型为浮点数的Mat对象用于存储每一行的跳变次数
for (int i = 0; i < img.rows; i++) { // 遍历图像的每一行
int jumpCount = 0; // 初始化跳变次数为0
int whiteCount = 0; // 初始化白色像素的数量为0
for (int j = 0; j < img.cols - 1; j++) { // 遍历当前行的每一个像素
if (img.at<char>(i, j) != img.at<char>(i, j + 1)) jumpCount++; // 如果当前像素与下一个像素的值不同跳变次数加1
if (img.at<uchar>(i, j) == 255) { // 如果当前像素的值为255即白色白色像素的数量加1
whiteCount++;
}
}
// 将当前行的跳变次数存储到jump中
jump.at<float>(i) = (float) jumpCount;
}
for (int i = 0; i < img.rows; i++) {
if (jump.at<float>(i) <= x) {
for (int j = 0; j < img.cols; j++) {
img.at<char>(i, j) = 0;
for (int i = 0; i < img.rows; i++) { // 再次遍历图像的每一行
if (jump.at<float>(i) <= x) { // 如果当前行的跳变次数小于阈值
for (int j = 0; j < img.cols; j++) { // 遍历当前行的每一个像素
img.at<char>(i, j) = 0; // 将像素值设为0即黑色
}
}
}
}
//该函数用于清除图像中的噪声
//接受一个Mat类型的引用img作为参数返回一个布尔值表示是否成功清除噪声
bool clearLiuDing(Mat &img) {
// 初始化一个浮点数向量fJump一个整数whiteCount用于计数白色像素一个常量x设为7以及一个全零的Mat对象jump大小为1行列数为图像的行数
std::vector<float> fJump;
int whiteCount = 0;
const int x = 7;
Mat jump = Mat::zeros(1, img.rows, CV_32F);
// 遍历图像的每一行计算每一行的跳变次数即像素值从0变为255或从255变为0的次数和白色像素的数量将跳变次数存储到jump中
for (int i = 0; i < img.rows; i++) {
int jumpCount = 0;
@ -377,6 +431,7 @@ namespace easypr {
jump.at<float>(i) = (float) jumpCount;
}
// 遍历jump将其值添加到fJump中如果jump的值在16到45之间iCount加1
int iCount = 0;
for (int i = 0; i < img.rows; i++) {
fJump.push_back(jump.at<float>(i));
@ -388,6 +443,7 @@ namespace easypr {
}
// if not is not plate
// 如果iCount占图像行数的比例小于或等于40%或者白色像素占图像总像素的比例小于15%或大于50%,则返回假,表示未能成功清除噪声
if (iCount * 1.0 / img.rows <= 0.40) {
return false;
}
@ -397,6 +453,7 @@ namespace easypr {
return false;
}
// 遍历图像的每一行如果该行的跳变次数小于或等于x则将该行的所有像素值设为0即黑色。最后返回真表示成功清除噪声
for (int i = 0; i < img.rows; i++) {
if (jump.at<float>(i) <= x) {
for (int j = 0; j < img.cols; j++) {
@ -408,13 +465,21 @@ 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;
@ -429,29 +494,31 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
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;
}
}
// 从下到上搜索下边界
for (int i = r - 1; i > maxtop; i--) {
if (boder.at<char>(i) == 1) {
maxMatTop = i;
}
}
// 根据找到的上下边界创建裁剪矩形
cropRect = Rect(0, minMatTop, c, maxMatTop - minMatTop + 1);
}
//用于清除图像中的噪声
void clearLiuDing(Mat mask, int &top, int &bottom) {
const int x = 7;

Loading…
Cancel
Save