|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
#include "easypr/core/chars_segment.h"
|
|
|
|
|
#include "easypr/core/chars_identify.h"
|
|
|
|
|
#include "easypr/core/core_func.h"
|
|
|
|
|
#include "easypr/core/params.h"
|
|
|
|
|
#include "easypr/config.h"
|
|
|
|
|
|
|
|
|
@ -14,8 +15,6 @@ CCharsSegment::CCharsSegment() {
|
|
|
|
|
m_LiuDingSize = DEFAULT_LIUDING_SIZE;
|
|
|
|
|
m_theMatWidth = DEFAULT_MAT_WIDTH;
|
|
|
|
|
|
|
|
|
|
//!车牌颜色判断参数
|
|
|
|
|
|
|
|
|
|
m_ColorThreshold = DEFAULT_COLORTHRESHOLD;
|
|
|
|
|
m_BluePercent = DEFAULT_BLUEPERCEMT;
|
|
|
|
|
m_WhitePercent = DEFAULT_WHITEPERCEMT;
|
|
|
|
@ -23,7 +22,6 @@ CCharsSegment::CCharsSegment() {
|
|
|
|
|
m_debug = DEFAULT_DEBUG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 字符尺寸验证
|
|
|
|
|
|
|
|
|
|
bool CCharsSegment::verifyCharSizes(Mat r) {
|
|
|
|
|
// Char sizes 45x90
|
|
|
|
@ -49,15 +47,12 @@ bool CCharsSegment::verifyCharSizes(Mat r) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 字符预处理
|
|
|
|
|
|
|
|
|
|
Mat CCharsSegment::preprocessChar(Mat in) {
|
|
|
|
|
// Remap image
|
|
|
|
|
int h = in.rows;
|
|
|
|
|
int w = in.cols;
|
|
|
|
|
|
|
|
|
|
//统一每个字符的大小
|
|
|
|
|
|
|
|
|
|
int charSize = CHAR_SIZE;
|
|
|
|
|
|
|
|
|
|
Mat transformMat = Mat::eye(2, 3, CV_32F);
|
|
|
|
@ -69,8 +64,6 @@ Mat CCharsSegment::preprocessChar(Mat in) {
|
|
|
|
|
warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR,
|
|
|
|
|
BORDER_CONSTANT, Scalar(0));
|
|
|
|
|
|
|
|
|
|
//! 将所有的字符调整成统一的尺寸
|
|
|
|
|
|
|
|
|
|
Mat out;
|
|
|
|
|
resize(warpImage, out, Size(charSize, charSize));
|
|
|
|
|
|
|
|
|
@ -233,7 +226,6 @@ bool slideChineseWindow(Mat& image, Rect mr, Mat& newRoi, Color plateType, float
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//! 字符分割与排序
|
|
|
|
|
int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color) {
|
|
|
|
|
if (!input.data) return 0x01;
|
|
|
|
|
|
|
|
|
@ -250,9 +242,7 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
|
|
|
|
|
Mat img_threshold;
|
|
|
|
|
|
|
|
|
|
// 二值化
|
|
|
|
|
// 根据车牌的不同颜色使用不同的阈值判断方法
|
|
|
|
|
// TODO:使用MSER来提取这些轮廓
|
|
|
|
|
|
|
|
|
|
//if (BLUE == plateType) {
|
|
|
|
|
// // cout << "BLUE" << endl;
|
|
|
|
|
// img_threshold = input_grey.clone();
|
|
|
|
@ -295,15 +285,12 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
destroyWindow("plate");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 去除车牌上方的柳钉以及下方的横线等干扰
|
|
|
|
|
// 并且也判断了是否是车牌
|
|
|
|
|
// 并且在此对字符的跳变次数以及字符颜色所占的比重做了是否是车牌的判别条件
|
|
|
|
|
// 如果不是车牌,返回ErrorCode=0x02
|
|
|
|
|
// remove liuding and hor lines
|
|
|
|
|
// also judge weather is plate use jump count
|
|
|
|
|
|
|
|
|
|
if (!clearLiuDing(img_threshold)) return 0x02;
|
|
|
|
|
//clearLiuDing(img_threshold);
|
|
|
|
|
|
|
|
|
|
// 在二值化图像中提取轮廓
|
|
|
|
|
|
|
|
|
|
Mat img_contours;
|
|
|
|
|
img_threshold.copyTo(img_contours);
|
|
|
|
@ -317,8 +304,6 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
vector<vector<Point> >::iterator itc = contours.begin();
|
|
|
|
|
vector<Rect> vecRect;
|
|
|
|
|
|
|
|
|
|
// 将不符合特定尺寸的字符块排除出去
|
|
|
|
|
|
|
|
|
|
while (itc != contours.end()) {
|
|
|
|
|
Rect mr = boundingRect(Mat(*itc));
|
|
|
|
|
Mat auxRoi(img_threshold, mr);
|
|
|
|
@ -327,27 +312,17 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
++itc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果找不到任何字符块,则返回ErrorCode=0x03
|
|
|
|
|
|
|
|
|
|
if (vecRect.size() == 0) return 0x03;
|
|
|
|
|
|
|
|
|
|
// 对符合尺寸的图块按照从左到右进行排序;
|
|
|
|
|
// 直接使用stl的sort方法,更有效率
|
|
|
|
|
|
|
|
|
|
vector<Rect> sortedRect(vecRect);
|
|
|
|
|
std::sort(sortedRect.begin(), sortedRect.end(),
|
|
|
|
|
[](const Rect& r1, const Rect& r2) { return r1.x < r2.x; });
|
|
|
|
|
|
|
|
|
|
size_t specIndex = 0;
|
|
|
|
|
|
|
|
|
|
// 获得特殊字符对应的Rectt,如苏A的"A"
|
|
|
|
|
|
|
|
|
|
specIndex = GetSpecificRect(sortedRect);
|
|
|
|
|
|
|
|
|
|
// 根据特定Rect向左反推出中文字符
|
|
|
|
|
// 这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能
|
|
|
|
|
// 退过特定算法来指定
|
|
|
|
|
|
|
|
|
|
Rect chineseRect;
|
|
|
|
|
if (specIndex < sortedRect.size())
|
|
|
|
|
chineseRect = GetChineseRect(sortedRect[specIndex]);
|
|
|
|
@ -361,19 +336,13 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
destroyWindow("plate");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//新建一个全新的排序Rect
|
|
|
|
|
//将中文字符Rect第一个加进来,因为它肯定是最左边的
|
|
|
|
|
//其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符
|
|
|
|
|
|
|
|
|
|
vector<Rect> newSortedRect;
|
|
|
|
|
newSortedRect.push_back(chineseRect);
|
|
|
|
|
RebuildRect(sortedRect, newSortedRect, specIndex);
|
|
|
|
|
|
|
|
|
|
if (newSortedRect.size() == 0) return 0x05;
|
|
|
|
|
|
|
|
|
|
// 开始截取每个字符
|
|
|
|
|
bool useSlideWindow = true;
|
|
|
|
|
|
|
|
|
|
bool useAdapThreshold = true;
|
|
|
|
|
//bool useAdapThreshold = CParams::instance()->getParam1b();
|
|
|
|
|
|
|
|
|
@ -381,9 +350,6 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
Rect mr = newSortedRect[i];
|
|
|
|
|
|
|
|
|
|
// Mat auxRoi(img_threshold, mr);
|
|
|
|
|
|
|
|
|
|
// 使用灰度图来截取图块,然后依次对每个图块进行大津阈值来二值化
|
|
|
|
|
|
|
|
|
|
Mat auxRoi(input_grey, mr);
|
|
|
|
|
Mat newRoi;
|
|
|
|
|
|
|
|
|
@ -411,7 +377,6 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
threshold(auxRoi, newRoi, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 归一化大小
|
|
|
|
|
newRoi = preprocessChar(newRoi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -428,14 +393,12 @@ int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec, Color color)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 每个字符图块输入到下面的步骤进行处理
|
|
|
|
|
resultVec.push_back(newRoi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 根据特殊车牌来构造猜测中文字符的位置和大小
|
|
|
|
|
|
|
|
|
|
Rect CCharsSegment::GetChineseRect(const Rect rectSpe) {
|
|
|
|
|
int height = rectSpe.height;
|
|
|
|
@ -451,8 +414,6 @@ Rect CCharsSegment::GetChineseRect(const Rect rectSpe) {
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 找出指示城市的字符的Rect,例如苏A7003X,就是"A"的位置
|
|
|
|
|
|
|
|
|
|
int CCharsSegment::GetSpecificRect(const vector<Rect>& vecRect) {
|
|
|
|
|
vector<int> xpositions;
|
|
|
|
|
int maxHeight = 0;
|
|
|
|
@ -474,9 +435,8 @@ int CCharsSegment::GetSpecificRect(const vector<Rect>& vecRect) {
|
|
|
|
|
Rect mr = vecRect[i];
|
|
|
|
|
int midx = mr.x + mr.width / 2;
|
|
|
|
|
|
|
|
|
|
//如果一个字符有一定的大小,并且在整个车牌的1/7到2/7之间,则是我们要找的特殊字符
|
|
|
|
|
//当前字符和下个字符的距离在一定的范围内
|
|
|
|
|
|
|
|
|
|
// use known knowledage to find the specific character
|
|
|
|
|
// position in 1/7 and 2/7
|
|
|
|
|
if ((mr.width > maxWidth * 0.8 || mr.height > maxHeight * 0.8) &&
|
|
|
|
|
(midx < int(m_theMatWidth / 7) * 2 &&
|
|
|
|
|
midx > int(m_theMatWidth / 7) * 1)) {
|
|
|
|
@ -487,10 +447,6 @@ int CCharsSegment::GetSpecificRect(const vector<Rect>& vecRect) {
|
|
|
|
|
return specIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//! 这个函数做两个事情
|
|
|
|
|
// 1.把特殊字符Rect左边的全部Rect去掉,后面再重建中文字符的位置。
|
|
|
|
|
// 2.从特殊字符Rect开始,依次选择6个Rect,多余的舍去。
|
|
|
|
|
|
|
|
|
|
int CCharsSegment::RebuildRect(const vector<Rect>& vecRect,
|
|
|
|
|
vector<Rect>& outRect, int specIndex) {
|
|
|
|
|
int count = 6;
|
|
|
|
|