|
|
|
@ -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=0,S=0,V=255表示红色)。否则,将其设置为黑色(H=0,S=0,V=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;
|
|
|
|
|
|
|
|
|
|