wuxiuping_branch
www-369 11 months ago
parent e8e1874816
commit d25d52f469

@ -467,19 +467,19 @@ namespace easypr {
// 该函数用于清除图像的边界
// 接受一个Mat类型的图像img和一个Rect类型的裁剪矩形cropRect作为参数
void clearBorder(const Mat &img, Rect& cropRect) {
// 获取图像的行数和列数
void clearBorder(const Mat &img, Rect& cropRect) {
// 获取图像的行数和列数
int r = img.rows;
int c = img.cols;
// 创建一个全零的单通道矩阵boder用于存储每一行是否为边界
// 创建一个全零的单通道矩阵boder用于存储每一行是否为边界
Mat boder = Mat::zeros(1, r, CV_8UC1);
// 设置一个阈值,如果一行中像素值没有变化的次数超过这个阈值,那么认为这一行是边界
// 设置一个阈值,如果一行中像素值没有变化的次数超过这个阈值,那么认为这一行是边界
const int noJunpCount_thresh = int(0.15f * c);
// if nojumpcount >
// 遍历图像的每一行计算每一行中像素值没有变化的次数如果这个次数超过阈值那么认为这一行是边界将boder对应的位置设为1
// 遍历图像的每一行计算每一行中像素值没有变化的次数如果这个次数超过阈值那么认为这一行是边界将boder对应的位置设为1
for (int i = 0; i < r; i++) {
int nojumpCount = 0;
int isBorder = 0;
@ -494,34 +494,35 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
boder.at<char>(i) = (char) isBorder;
}
// 设置上下边界的搜索范围只在图像的中间80%的区域内搜索边界
// 设置上下边界的搜索范围只在图像的中间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);
}
}
//用于清除图像中的噪声
//用于清除图像中的噪声—————函数的目的是找到图像中的有效区域,即去除上下边缘的噪声
//函数接受一个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,18 +659,19 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
}
//
//返回处理后的新图像dstMat
return dstMat;
}
//其目的是在输入图像中找到一个矩形区域该区域包含了图像中所有像素值大于20的部分
//函数接受一个 Mat 类型的引用 in 作为参数,返回一个 Rect 类型的对象
Rect GetCenterRect(Mat &in) {
Rect _rect;
Rect _rect; // 定义一个矩形对象
int top = 0;
int bottom = in.rows - 1;
// find the center rect
int top = 0; // 初始化矩形的上边界
int bottom = in.rows - 1; // 初始化矩形的下边界为图像的行数减1
// 从上到下遍历图像的每一行找到第一个包含像素值大于20的行将其行数设为矩形的上边界
for (int i = 0; i < in.rows; ++i) {
bool bFind = false;
for (int j = 0; j < in.cols; ++j) {
@ -671,11 +684,10 @@ void clearBorder(const Mat &img, Rect& cropRect) {
if (bFind) {
break;
}
}
for (int i = in.rows - 1;
i >= 0;
--i) {
// 从下到上遍历图像的每一行找到第一个包含像素值大于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) {
@ -687,12 +699,12 @@ void clearBorder(const Mat &img, Rect& cropRect) {
if (bFind) {
break;
}
}
int left = 0; // 初始化矩形的左边界
int right = in.cols - 1; // 初始化矩形的右边界为图像的列数减1
int left = 0;
int right = in.cols - 1;
// 从左到右遍历图像的每一列找到第一个包含像素值大于20的列将其列数设为矩形的左边界
for (int j = 0; j < in.cols; ++j) {
bool bFind = false;
for (int i = 0; i < in.rows; ++i) {
@ -705,17 +717,15 @@ void clearBorder(const Mat &img, Rect& cropRect) {
if (bFind) {
break;
}
}
for (int j = in.cols - 1;
j >= 0;
--j) {
// 从右到左遍历图像的每一列找到第一个包含像素值大于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;
}
}
@ -724,46 +734,54 @@ void clearBorder(const Mat &img, Rect& cropRect) {
}
}
// 设置矩形的位置和大小
_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;
}
@ -789,59 +811,59 @@ void clearBorder(const Mat &img, Rect& cropRect) {
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; // 返回处理后的图像
}
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
}
@ -849,21 +871,21 @@ void clearBorder(const Mat &img, Rect& cropRect) {
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; // 返回处理后的图像
}

Loading…
Cancel
Save