style: change to use utf-8 encoding, compatible with damn visual studio

v1.6alpha
Micooz 9 years ago
parent fc49dbdee1
commit 5d3cd897f0

@ -1,69 +1,54 @@
#ifndef EASYPR_CONFIG_H_
#define EASYPR_CONFIG_H_
namespace easypr {
static const char* kDefaultSvmPath = "resources/model/svm.xml";
static const char* kDefaultAnnPath = "resources/model/ann.xml";
typedef enum {
kForward = 1, // correspond to "has plate"
kInverse = 0 // correspond to "no plate"
} SvmLabel;
static const float kSvmPercentage = 0.7f;
static const int kPredictSize = 10;
static const int kNeurons = 40;
static const char *kChars[] = {
"0", "1", "2",
"3", "4", "5",
"6", "7", "8",
"9",
/* 10 */
"A", "B", "C",
"D", "E", "F",
"G", "H", /* {"I", "I"} */
"J", "K", "L",
"M", "N", /* {"O", "O"} */
"P", "Q", "R",
"S", "T", "U",
"V", "W", "X",
"Y", "Z",
/* 24 */
"zh_cuan" , "zh_e" , "zh_gan" ,
"zh_gan1" , "zh_gui" , "zh_gui1" ,
"zh_hei" , "zh_hu" , "zh_ji" ,
"zh_jin" , "zh_jing" , "zh_jl" ,
"zh_liao" , "zh_lu" , "zh_meng" ,
"zh_min" , "zh_ning" , "zh_qing" ,
"zh_qiong", "zh_shan" , "zh_su" ,
"zh_sx" , "zh_wan" , "zh_xiang",
"zh_xin" , "zh_yu" , "zh_yu1" ,
"zh_yue" , "zh_yun" , "zh_zang" ,
"zh_zhe"
/* 31 */
};
static const std::map<std::string, std::string> kCharsMap = {
{"zh_cuan" , ""}, {"zh_e" , ""}, {"zh_gan" , ""},
{"zh_gan1" , ""}, {"zh_gui" , ""}, {"zh_gui1" , ""},
{"zh_hei" , ""}, {"zh_hu" , ""}, {"zh_ji" , ""},
{"zh_jin" , ""}, {"zh_jing" , ""}, {"zh_jl" , ""},
{"zh_liao" , ""}, {"zh_lu" , ""}, {"zh_meng" , ""},
{"zh_min" , ""}, {"zh_ning" , ""}, {"zh_qing" , ""},
{"zh_qiong", ""}, {"zh_shan" , ""}, {"zh_su" , ""},
{"zh_sx" , ""}, {"zh_wan" , ""}, {"zh_xiang", ""},
{"zh_xin" , ""}, {"zh_yu" , ""}, {"zh_yu1" , ""},
{"zh_yue" , ""}, {"zh_yun" , ""}, {"zh_zang" , ""},
{"zh_zhe" , ""}
/* 31 */
};
static const int kCharsTotalNumber = 65;
static bool kDebug = false;
}
#ifndef EASYPR_CONFIG_H_
#define EASYPR_CONFIG_H_
namespace easypr {
static const char* kDefaultSvmPath = "resources/model/svm.xml";
static const char* kDefaultAnnPath = "resources/model/ann.xml";
typedef enum {
kForward = 1, // correspond to "has plate"
kInverse = 0 // correspond to "no plate"
} SvmLabel;
static const float kSvmPercentage = 0.7f;
static const int kPredictSize = 10;
static const int kNeurons = 40;
static const char *kChars[] = {
"0", "1", "2",
"3", "4", "5",
"6", "7", "8",
"9",
/* 10 */
"A", "B", "C",
"D", "E", "F",
"G", "H", /* {"I", "I"} */
"J", "K", "L",
"M", "N", /* {"O", "O"} */
"P", "Q", "R",
"S", "T", "U",
"V", "W", "X",
"Y", "Z",
/* 24 */
"zh_cuan" , "zh_e" , "zh_gan" ,
"zh_gan1" , "zh_gui" , "zh_gui1" ,
"zh_hei" , "zh_hu" , "zh_ji" ,
"zh_jin" , "zh_jing" , "zh_jl" ,
"zh_liao" , "zh_lu" , "zh_meng" ,
"zh_min" , "zh_ning" , "zh_qing" ,
"zh_qiong", "zh_shan" , "zh_su" ,
"zh_sx" , "zh_wan" , "zh_xiang",
"zh_xin" , "zh_yu" , "zh_yu1" ,
"zh_yue" , "zh_yun" , "zh_zang" ,
"zh_zhe"
/* 31 */
};
static const int kCharsTotalNumber = 65;
static bool kDebug = false;
}
#endif // EASYPR_CONFIG_H_

@ -25,16 +25,18 @@ class CCharsRecognise {
int charsRecognise(cv::Mat plate, std::string& plateLicense);
//! 获得车牌颜色
//! 获得车牌颜色
inline std::string getPlateColor(cv::Mat input) const {
std::string color = "未知";
std::string color = "未知";
Color result = getPlateType(input, true);
if (BLUE == result) color = "蓝牌";
if (YELLOW == result) color = "黄牌";
if (BLUE == result) color = "蓝牌";
if (YELLOW == result) color = "黄牌";
return color;
}
//! 设置变量
//! 设置变量
inline void setLiuDingSize(int param) {
m_charsSegment->setLiuDingSize(param);
}
@ -55,7 +57,8 @@ class CCharsRecognise {
}
private:
//!字符分割
//!字符分割
CCharsSegment* m_charsSegment;
};

@ -1,53 +1,47 @@
//////////////////////////////////////////////////////////////////////////
// Name: chars_segment Header
// Version: 1.0
// Date: 2014-09-19
// Author: liuruoze
// Copyright: liuruoze
// Reference: Mastering OpenCV with Practical Computer Vision Projects
// Reference: CSDN Bloger taotao1233
// Desciption:
// Defines CCharsSegment
//////////////////////////////////////////////////////////////////////////
#ifndef EASYPR_CORE_CHARSSEGMENT_H_
#define EASYPR_CORE_CHARSSEGMENT_H_
#include "easypr/core/core_func.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
class CCharsSegment {
public:
CCharsSegment();
//! 字符分割
//! 字符分割
int charsSegment(Mat input, std::vector<Mat>& resultVec);
//! 字符尺寸验证
//! 字符尺寸验证
bool verifyCharSizes(Mat r);
//! 字符预处理
//! 字符预处理
Mat preprocessChar(Mat in);
//! 根据特殊车牌来构造猜测中文字符的位置和大小
//! 根据特殊车牌来构造猜测中文字符的位置和大小
Rect GetChineseRect(const Rect rectSpe);
//! 找出指示城市的字符的Rect例如苏A7003X就是A的位置
//! 找出指示城市的字符的Rect例如苏A7003X就是A的位置
int GetSpecificRect(const std::vector<Rect>& vecRect);
//! 这个函数做两个事情
// 1.把特殊字符Rect左边的全部Rect去掉后面再重建中文字符的位置。
// 2.从特殊字符Rect开始依次选择6个Rect多余的舍去。
//! 这个函数做两个事情
// 1.把特殊字符Rect左边的全部Rect去掉后面再重建中文字符的位置。
// 2.从特殊字符Rect开始依次选择6个Rect多余的舍去。
int RebuildRect(const std::vector<Rect>& vecRect, std::vector<Rect>& outRect,
int specIndex);
//! 将Rect按位置从左到右进行排序
//! 将Rect按位置从左到右进行排序
int SortRect(const std::vector<Rect>& vecRect, std::vector<Rect>& out);
//! 设置变量
//! 设置变量
inline void setLiuDingSize(int param) { m_LiuDingSize = param; }
inline void setColorThreshold(int param) { m_ColorThreshold = param; }
@ -56,41 +50,51 @@ class CCharsSegment {
inline void setWhitePercent(float param) { m_WhitePercent = param; }
inline float getWhitePercent() const { return m_WhitePercent; }
//! 是否开启调试模式常量默认0代表关闭
//! 是否开启调试模式常量默认0代表关闭
static const int DEFAULT_DEBUG = 1;
//! preprocessChar所用常量
//! preprocessChar所用常量
static const int CHAR_SIZE = 20;
static const int HORIZONTAL = 1;
static const int VERTICAL = 0;
//! preprocessChar所用常量
//! preprocessChar所用常量
static const int DEFAULT_LIUDING_SIZE = 7;
static const int DEFAULT_MAT_WIDTH = 136;
static const int DEFAULT_COLORTHRESHOLD = 150;
//! 是否开启调试模式
//! 是否开启调试模式
inline void setDebug(int param) { m_debug = param; }
//! 获取调试模式状态
//! 获取调试模式状态
inline int getDebug() { return m_debug; }
private:
//!柳钉判断参数
//!柳钉判断参数
int m_LiuDingSize;
//!车牌大小参数
//!车牌大小参数
int m_theMatWidth;
//!车牌颜色判断参数
//!车牌颜色判断参数
int m_ColorThreshold;
float m_BluePercent;
float m_WhitePercent;
//! 是否开启调试模式0关闭非0开启
//! 是否开启调试模式0关闭非0开启
int m_debug;
};
} /* \namespace easypr */
}
#endif // EASYPR_CORE_CHARSSEGMENT_H_

@ -1,55 +1,61 @@
#ifndef EASYPR_CORE_COREFUNC_H_
#define EASYPR_CORE_COREFUNC_H_
#include <opencv2/opencv.hpp>
using namespace cv;
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
enum Color { BLUE, YELLOW, WHITE, UNKNOWN };
enum LocateType { SOBEL, COLOR, OTHER };
//! 根据一幅图像与颜色模板获取对应的二值图
//! 输入RGB图像, 颜色模板(蓝色、黄色)
//! 输出灰度图只有0和255两个值255代表匹配0代表不匹配
Mat colorMatch(const Mat& src, Mat& match, const Color r,
const bool adaptive_minsv);
//! 判断一个车牌的颜色
//! 输入车牌mat与颜色模板
//! 返回true或fasle
bool plateColorJudge(const Mat& src, const Color r, const bool adaptive_minsv,
float& percent);
bool bFindLeftRightBound(Mat& bound_threshold, int& posLeft, int& posRight);
bool bFindLeftRightBound1(Mat& bound_threshold, int& posLeft, int& posRight);
bool bFindLeftRightBound2(Mat& bound_threshold, int& posLeft, int& posRight);
//去除车牌上方的钮钉
//计算每行元素的阶跃数如果小于X认为是柳丁将此行全部填0涂黑
// X的推荐值为可根据实际调整
bool clearLiuDing(Mat& img);
void clearLiuDingOnly(Mat& img);
void clearLiuDing(Mat mask, int& top, int& bottom);
//! 获得车牌颜色
Color getPlateType(const Mat& src, const bool adaptive_minsv);
//! 直方图均衡
Mat histeq(Mat in);
Mat features(Mat in, int sizeData);
Rect GetCenterRect(Mat& in);
Mat CutTheRect(Mat& in, Rect& rect);
int ThresholdOtsu(Mat mat);
//! 获取垂直和水平方向直方图
Mat ProjectedHistogram(Mat img, int t);
} /*! \namespace easypr*/
#ifndef EASYPR_CORE_COREFUNC_H_
#define EASYPR_CORE_COREFUNC_H_
#include <opencv2/opencv.hpp>
using namespace cv;
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
enum Color { BLUE, YELLOW, WHITE, UNKNOWN };
enum LocateType { SOBEL, COLOR, OTHER };
//! 根据一幅图像与颜色模板获取对应的二值图
//! 输入RGB图像, 颜色模板(蓝色、黄色)
//! 输出灰度图只有0和255两个值255代表匹配0代表不匹配
Mat colorMatch(const Mat& src, Mat& match, const Color r,
const bool adaptive_minsv);
//! 判断一个车牌的颜色
//! 输入车牌mat与颜色模板
//! 返回true或fasle
bool plateColorJudge(const Mat& src, const Color r, const bool adaptive_minsv,
float& percent);
bool bFindLeftRightBound(Mat& bound_threshold, int& posLeft, int& posRight);
bool bFindLeftRightBound1(Mat& bound_threshold, int& posLeft, int& posRight);
bool bFindLeftRightBound2(Mat& bound_threshold, int& posLeft, int& posRight);
//去除车牌上方的钮钉
//计算每行元素的阶跃数如果小于X认为是柳丁将此行全部填0涂黑
// X的推荐值为可根据实际调整
bool clearLiuDing(Mat& img);
void clearLiuDingOnly(Mat& img);
void clearLiuDing(Mat mask, int& top, int& bottom);
//! 获得车牌颜色
Color getPlateType(const Mat& src, const bool adaptive_minsv);
//! 直方图均衡
Mat histeq(Mat in);
Mat features(Mat in, int sizeData);
Rect GetCenterRect(Mat& in);
Mat CutTheRect(Mat& in, Rect& rect);
int ThresholdOtsu(Mat mat);
//! 获取垂直和水平方向直方图
Mat ProjectedHistogram(Mat img, int t);
} /*! \namespace easypr*/
#endif // EASYPR_CORE_COREFUNC_H_

@ -1,30 +1,36 @@
#ifndef EASYPR_CORE_FEATURE_H_
#define EASYPR_CORE_FEATURE_H_
#include <opencv2/opencv.hpp>
namespace easypr {
//! 获得车牌的特征数
cv::Mat getTheFeatures(cv::Mat in);
//! EasyPR的getFeatures回调函数
//! 用于从车牌的image生成svm的训练特征features
typedef void (*svmCallback)(const cv::Mat& image, cv::Mat& features);
//! EasyPR的getFeatures回调函数
//! 本函数是获取垂直和水平的直方图图值
void getHistogramFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取SIFT特征子
void getSIFTFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取HOG特征子
void getHOGFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取HSV空间量化的直方图特征子
void getHSVHistFeatures(const cv::Mat& image, cv::Mat& features);
} /*! \namespace easypr*/
#ifndef EASYPR_CORE_FEATURE_H_
#define EASYPR_CORE_FEATURE_H_
#include <opencv2/opencv.hpp>
namespace easypr {
//! 获得车牌的特征数
cv::Mat getTheFeatures(cv::Mat in);
//! EasyPR的getFeatures回调函数
//! 用于从车牌的image生成svm的训练特征features
typedef void (*svmCallback)(const cv::Mat& image, cv::Mat& features);
//! EasyPR的getFeatures回调函数
//! 本函数是获取垂直和水平的直方图图值
void getHistogramFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取SIFT特征子
void getSIFTFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取HOG特征子
void getHOGFeatures(const cv::Mat& image, cv::Mat& features);
//! 本函数是获取HSV空间量化的直方图特征子
void getHSVHistFeatures(const cv::Mat& image, cv::Mat& features);
} /*! \namespace easypr*/
#endif // EASYPR_CORE_FEATURE_H_

@ -1,56 +1,61 @@
//////////////////////////////////////////////////////////////////////////
// Name: plate Header
// Version: 1.0
// Date: 2015-03-12
// Author: liuruoze
// Copyright: liuruoze
// Desciption:
// An abstract class for car plate.
//////////////////////////////////////////////////////////////////////////
#ifndef EASYPR_CORE_PLATE_H_
#define EASYPR_CORE_PLATE_H_
#include "core_func.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
class CPlate {
public:
bool bColored;
//! 构造函数
CPlate();
//! 设置与读取变量
inline void setPlateMat(Mat param) { m_plateMat = param; }
inline Mat getPlateMat() const { return m_plateMat; }
inline void setPlatePos(RotatedRect param) { m_platePos = param; }
inline RotatedRect getPlatePos() const { return m_platePos; }
inline void setPlateStr(String param) { m_plateStr = param; }
inline String getPlateStr() const { return m_plateStr; }
inline void setPlateLocateType(LocateType param) { m_locateType = param; }
inline LocateType getPlateLocateType() const { return m_locateType; }
private:
//! 车牌的图块
Mat m_plateMat;
//! 车牌在原图的位置
RotatedRect m_platePos;
//! 车牌字符串
String m_plateStr;
//! 车牌定位的方法
LocateType m_locateType;
};
} /*! \namespace easypr*/
//////////////////////////////////////////////////////////////////////////
// Name: plate Header
// Version: 1.0
// Date: 2015-03-12
// Author: liuruoze
// Copyright: liuruoze
// Desciption:
// An abstract class for car plate.
//////////////////////////////////////////////////////////////////////////
#ifndef EASYPR_CORE_PLATE_H_
#define EASYPR_CORE_PLATE_H_
#include "core_func.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
class CPlate {
public:
bool bColored;
//! 构造函数
CPlate();
//! 设置与读取变量
inline void setPlateMat(Mat param) { m_plateMat = param; }
inline Mat getPlateMat() const { return m_plateMat; }
inline void setPlatePos(RotatedRect param) { m_platePos = param; }
inline RotatedRect getPlatePos() const { return m_platePos; }
inline void setPlateStr(String param) { m_plateStr = param; }
inline String getPlateStr() const { return m_plateStr; }
inline void setPlateLocateType(LocateType param) { m_locateType = param; }
inline LocateType getPlateLocateType() const { return m_locateType; }
private:
//! 车牌的图块
Mat m_plateMat;
//! 车牌在原图的位置
RotatedRect m_platePos;
//! 车牌字符串
String m_plateStr;
//! 车牌定位的方法
LocateType m_locateType;
};
} /*! \namespace easypr*/
#endif // EASYPR_CORE_PLATE_H_

@ -1,24 +1,9 @@
//////////////////////////////////////////////////////////////////////////
// Name: plate_detect Header
// Version: 1.2
// Date: 2014-09-28
// MDate: 2015-03-13
// Author: liuruoze
// Copyright: liuruoze
// Reference: Mastering OpenCV with Practical Computer Vision Projects
// Reference: CSDN Bloger taotao1233
// Desciption:
// Defines CPlateDetect
//////////////////////////////////////////////////////////////////////////
#ifndef EASYPR_CORE_PLATEDETECT_H_
#define EASYPR_CORE_PLATEDETECT_H_
#include "easypr/core/plate_locate.h"
#include "easypr/core/plate_judge.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
class CPlateDetect {
@ -27,29 +12,37 @@ class CPlateDetect {
~CPlateDetect();
//! 深度车牌检测使用颜色与二次Sobel法综合
int plateDetect(Mat src, std::vector<CPlate>& resultVec,
//! 深度车牌检测使用颜色与二次Sobel法综合
int plateDetect(Mat src, std::vector<CPlate> &resultVec,
bool showDetectArea = true, int index = 0);
//! 展示中间的结果
int showResult(const Mat& result);
//! 展示中间的结果
int showResult(const Mat &result);
//! 装载SVM模型
//! 装载SVM模型
void LoadSVM(std::string s);
//! 生活模式与工业模式切换
//! 生活模式与工业模式切换
inline void setPDLifemode(bool param) { m_plateLocate->setLifemode(param); }
//! 是否开启调试模式
//! 是否开启调试模式
inline void setPDDebug(bool param) { m_plateLocate->setDebug(param); }
//! 获取调试模式状态
//! 获取调试模式状态
inline bool getPDDebug() { return m_plateLocate->getDebug(); }
//! 设置与读取变量
//! 设置与读取变量
inline void setGaussianBlurSize(int param) {
m_plateLocate->setGaussianBlurSize(param);
}
inline int getGaussianBlurSize() const {
return m_plateLocate->getGaussianBlurSize();
}
@ -57,6 +50,7 @@ class CPlateDetect {
inline void setMorphSizeWidth(int param) {
m_plateLocate->setMorphSizeWidth(param);
}
inline int getMorphSizeWidth() const {
return m_plateLocate->getMorphSizeWidth();
}
@ -64,6 +58,7 @@ class CPlateDetect {
inline void setMorphSizeHeight(int param) {
m_plateLocate->setMorphSizeHeight(param);
}
inline int getMorphSizeHeight() const {
return m_plateLocate->getMorphSizeHeight();
}
@ -71,32 +66,40 @@ class CPlateDetect {
inline void setVerifyError(float param) {
m_plateLocate->setVerifyError(param);
}
inline float getVerifyError() const {
return m_plateLocate->getVerifyError();
}
inline void setVerifyAspect(float param) {
m_plateLocate->setVerifyAspect(param);
}
inline float getVerifyAspect() const {
return m_plateLocate->getVerifyAspect();
}
inline void setVerifyMin(int param) { m_plateLocate->setVerifyMin(param); }
inline void setVerifyMax(int param) { m_plateLocate->setVerifyMax(param); }
inline void setJudgeAngle(int param) { m_plateLocate->setJudgeAngle(param); }
inline void setMaxPlates(int param) { m_maxPlates = param; }
inline int getMaxPlates() const { return m_maxPlates; }
private:
//! 设置一幅图中最多有多少车牌
//! 设置一幅图中最多有多少车牌
int m_maxPlates;
//! 车牌定位
//! 车牌定位
CPlateLocate* m_plateLocate;
};
} /*! \namespace easypr*/
}
#endif // EASYPR_CORE_PLATEDETECT_H_

@ -11,14 +11,17 @@ class PlateJudge {
public:
static PlateJudge* instance();
//! 对多幅车牌进行SVM判断
int plateJudge(const std::vector<CPlate>&, std::vector<CPlate>&);
//! 对多幅车牌进行SVM判断
//! 车牌判断
int plateJudge(const std::vector<Mat>&, std::vector<Mat>&);
int plateJudge(const std::vector<CPlate> &, std::vector<CPlate> &);
//! 车牌判断(一副图像)
int plateJudge(const Mat& inMat, int& result);
//! 车牌判断
int plateJudge(const std::vector<Mat> &, std::vector<Mat> &);
//! 车牌判断(一副图像)
int plateJudge(const Mat &inMat, int &result);
private:
PlateJudge();

@ -26,66 +26,81 @@ class CPlateLocate {
public:
CPlateLocate();
//! Sobel第一次搜索
//! 不限制大小和形状获取的BoundRect进入下一步
//! Sobel第一次搜索
//! 不限制大小和形状获取的BoundRect进入下一步
int sobelFrtSearch(const Mat& src, std::vector<Rect_<float>>& outRects);
//! Sobel第二次搜索
//! 对大小和形状做限制,生成参考坐标
//! Sobel第二次搜索
//! 对大小和形状做限制,生成参考坐标
int sobelSecSearch(Mat& bound, Point2f refpoint,
std::vector<RotatedRect>& outRects);
int sobelSecSearchPart(Mat& bound, Point2f refpoint,
std::vector<RotatedRect>& outRects);
//! 抗扭斜处理
//! 抗扭斜处理
int deskew(const Mat& src, const Mat& src_b,
std::vector<RotatedRect>& inRects, std::vector<CPlate>& outPlates);
//! 是否偏斜
//! 输入二值化图像,输出判断结果
//! 是否偏斜
//! 输入二值化图像,输出判断结果
bool isdeflection(const Mat& in, const double angle, double& slope);
//! Sobel运算
//! 输入彩色图像,输出二值化图像
//! Sobel运算
//! 输入彩色图像,输出二值化图像
int sobelOper(const Mat& in, Mat& out, int blurSize, int morphW, int morphH);
//! 计算一个安全的Rect
//! 计算一个安全的Rect
bool calcSafeRect(const RotatedRect& roi_rect, const Mat& src,
Rect_<float>& safeBoundRect);
//! 旋转操作
//! 旋转操作
bool rotation(Mat& in, Mat& out, const Size rect_size, const Point2f center,
const double angle);
//! 扭变操作
//! 扭变操作
void affine(const Mat& in, Mat& out, const double slope);
//! 颜色定位法
//! 颜色定位法
int plateColorLocate(Mat src, std::vector<CPlate>& candPlates, int index = 0);
//! Sobel定位法
//! Sobel定位法
int plateSobelLocate(Mat src, std::vector<CPlate>& candPlates, int index = 0);
int sobelOperT(const Mat& in, Mat& out, int blurSize, int morphW, int morphH);
//! Color搜索
//! Color搜索
int colorSearch(const Mat& src, const Color r, Mat& out,
std::vector<RotatedRect>& outRects, int index = 0);
//! 未使用函数与代码
//! 开始------------
//! 结束------------
//! 未使用函数与代码
//! 未使用函数与代码
//! 开始------------
//! 结束------------
//! 未使用函数与代码
//! 车牌定位
//! 车牌定位
int plateLocate(Mat, std::vector<Mat>&, int = 0);
//! 车牌的尺寸验证
//! 车牌的尺寸验证
bool verifySizes(RotatedRect mr);
//! 生活模式与工业模式切换
//! 生活模式与工业模式切换
void setLifemode(bool param);
//! 设置与读取变量
//! 设置与读取变量
inline void setGaussianBlurSize(int param) { m_GaussianBlurSize = param; }
inline int getGaussianBlurSize() const { return m_GaussianBlurSize; }
@ -105,13 +120,16 @@ class CPlateLocate {
inline void setJudgeAngle(int param) { m_angle = param; }
//! 是否开启调试模式
//! 是否开启调试模式
inline void setDebug(bool param) { m_debug = param; }
//! 获取调试模式状态
//! 获取调试模式状态
inline bool getDebug() { return m_debug; }
//! PlateLocate所用常量
//! PlateLocate所用常量
static const int DEFAULT_GAUSSIANBLUR_SIZE = 5;
static const int SOBEL_SCALE = 1;
static const int SOBEL_DELTA = 0;
@ -121,39 +139,48 @@ class CPlateLocate {
static const int DEFAULT_MORPH_SIZE_WIDTH = 17; // 17
static const int DEFAULT_MORPH_SIZE_HEIGHT = 3; // 3
//! showResultMat所用常量
//! showResultMat所用常量
static const int WIDTH = 136;
static const int HEIGHT = 36;
static const int TYPE = CV_8UC3;
//! verifySize所用常量
//! verifySize所用常量
static const int DEFAULT_VERIFY_MIN = 1; // 3
static const int DEFAULT_VERIFY_MAX = 24; // 20
//! 角度判断所用常量
//! 角度判断所用常量
static const int DEFAULT_ANGLE = 60; // 30
//! 是否开启调试模式常量默认0代表关闭
//! 是否开启调试模式常量默认0代表关闭
static const int DEFAULT_DEBUG = 1;
protected:
//! 高斯模糊所用变量
//! 高斯模糊所用变量
int m_GaussianBlurSize;
//! 连接操作所用变量
//! 连接操作所用变量
int m_MorphSizeWidth;
int m_MorphSizeHeight;
//! verifySize所用变量
//! verifySize所用变量
float m_error;
float m_aspect;
int m_verifyMin;
int m_verifyMax;
//! 角度判断所用变量
//! 角度判断所用变量
int m_angle;
//! 是否开启调试模式0关闭非0开启
//! 是否开启调试模式0关闭非0开启
bool m_debug;
};

@ -24,13 +24,16 @@ class CPlateRecognize : public CPlateDetect, public CCharsRecognise {
public:
CPlateRecognize();
//! 车牌检测与字符识别
//! 车牌检测与字符识别
int plateRecognize(Mat src, std::vector<std::string> &licenseVec);
//! 生活模式与工业模式切换
//! 生活模式与工业模式切换
inline void setLifemode(bool param) { CPlateDetect::setPDLifemode(param); }
//! 是否开启调试模式
//! 是否开启调试模式
inline void setDebug(bool param) { CPlateDetect::setPDDebug(param); }
};

@ -1,21 +1,23 @@
// 生成GDTS的文件
// 在捐赠数据到GDTS数据集里请先用这里的方法对图像进行预处理
// EasyPR开源项目非常注重保护原始图片的版权
// 所有的捐赠数据通过GDSL协议进行约定保证使用人仅用于非商业性目的
#ifndef EASYPR_CORE_GDTS_H_
#define EASYPR_CORE_GDTS_H_
#include <opencv2/opencv.hpp>
namespace easypr {
namespace preprocess {
// EasyPR的图像预处理函数进行模糊化与裁剪化处理
cv::Mat imageProcess(cv::Mat src);
int generate_gdts();
}
}
// 生成GDTS的文件
// 在捐赠数据到GDTS数据集里请先用这里的方法对图像进行预处理
// EasyPR开源项目非常注重保护原始图片的版权
// 所有的捐赠数据通过GDSL协议进行约定保证使用人仅用于非商业性目的
#ifndef EASYPR_CORE_GDTS_H_
#define EASYPR_CORE_GDTS_H_
#include <opencv2/opencv.hpp>
namespace easypr {
namespace preprocess {
// EasyPR的图像预处理函数进行模糊化与裁剪化处理
cv::Mat imageProcess(cv::Mat src);
int generate_gdts();
}
}
#endif // EASYPR_CORE_GDTS_H_

@ -1,25 +1,25 @@
#ifndef EASYPR_TRAIN_ANNTRAIN_H_
#define EASYPR_TRAIN_ANNTRAIN_H_
#include "easypr/train/train.h"
namespace easypr {
class AnnTrain : public ITrain {
public:
explicit AnnTrain(const char* chars_folder, const char* xml);
virtual void train();
virtual void test();
private:
virtual cv::Ptr<cv::ml::TrainData> tdata();
cv::Ptr<cv::ml::ANN_MLP> ann_;
const char* ann_xml_;
const char* chars_folder_;
};
}
#ifndef EASYPR_TRAIN_ANNTRAIN_H_
#define EASYPR_TRAIN_ANNTRAIN_H_
#include "easypr/train/train.h"
namespace easypr {
class AnnTrain : public ITrain {
public:
explicit AnnTrain(const char* chars_folder, const char* xml);
virtual void train();
virtual void test();
private:
virtual cv::Ptr<cv::ml::TrainData> tdata();
cv::Ptr<cv::ml::ANN_MLP> ann_;
const char* ann_xml_;
const char* chars_folder_;
};
}
#endif // EASYPR_TRAIN_ANNTRAIN_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,73 +1,80 @@
// 这个文件定义了EasyPR里所有特征生成的函数
// 所属命名空间为easypr
// 这个部分中的特征由easypr的开发者修改
#include "easypr/core/feature.h"
#include "easypr/core/core_func.h"
/*! \namespace easypr
Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
//! 获取垂直和水平的直方图图值
Mat getTheFeatures(Mat in) {
const int VERTICAL = 0;
const int HORIZONTAL = 1;
// Histogram features
Mat vhist = ProjectedHistogram(in, VERTICAL);
Mat hhist = ProjectedHistogram(in, HORIZONTAL);
// Last 10 is the number of moments components
int numCols = vhist.cols + hhist.cols;
Mat out = Mat::zeros(1, numCols, CV_32F);
// Asign values to feature,样本特征为水平、垂直直方图
int j = 0;
for (int i = 0; i < vhist.cols; i++) {
out.at<float>(j) = vhist.at<float>(i);
j++;
}
for (int i = 0; i < hhist.cols; i++) {
out.at<float>(j) = hhist.at<float>(i);
j++;
}
return out;
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取垂直和水平的直方图图值
void getHistogramFeatures(const Mat& image, Mat& features) {
Mat grayImage;
cvtColor(image, grayImage, CV_RGB2GRAY);
//grayImage = histeq(grayImage);
Mat img_threshold;
threshold(grayImage, img_threshold, 0, 255,
CV_THRESH_OTSU + CV_THRESH_BINARY);
features = getTheFeatures(img_threshold);
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取SITF特征子
void getSIFTFeatures(const Mat& image, Mat& features) {
//待完善
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取HOG特征子
void getHOGFeatures(const Mat& image, Mat& features) {
//待完善
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取HSV空间量化的直方图特征子
void getHSVHistFeatures(const Mat& image, Mat& features) {
// TODO
}
} /* \namespace easypr */
// 这个文件定义了EasyPR里所有特征生成的函数
// 所属命名空间为easypr
// 这个部分中的特征由easypr的开发者修改
#include "easypr/core/feature.h"
#include "easypr/core/core_func.h"
namespace easypr {
//! 获取垂直和水平的直方图图值
Mat getTheFeatures(Mat in) {
const int VERTICAL = 0;
const int HORIZONTAL = 1;
// Histogram features
Mat vhist = ProjectedHistogram(in, VERTICAL);
Mat hhist = ProjectedHistogram(in, HORIZONTAL);
// Last 10 is the number of moments components
int numCols = vhist.cols + hhist.cols;
Mat out = Mat::zeros(1, numCols, CV_32F);
// Asign values to feature,样本特征为水平、垂直直方图
int j = 0;
for (int i = 0; i < vhist.cols; i++) {
out.at<float>(j) = vhist.at<float>(i);
j++;
}
for (int i = 0; i < hhist.cols; i++) {
out.at<float>(j) = hhist.at<float>(i);
j++;
}
return out;
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取垂直和水平的直方图图值
void getHistogramFeatures(const Mat& image, Mat& features) {
Mat grayImage;
cvtColor(image, grayImage, CV_RGB2GRAY);
//grayImage = histeq(grayImage);
Mat img_threshold;
threshold(grayImage, img_threshold, 0, 255,
CV_THRESH_OTSU + CV_THRESH_BINARY);
features = getTheFeatures(img_threshold);
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取SITF特征子
void getSIFTFeatures(const Mat& image, Mat& features) {
//待完善
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取HOG特征子
void getHOGFeatures(const Mat& image, Mat& features) {
//待完善
}
//! EasyPR的getFeatures回调函数
//! 本函数是获取HSV空间量化的直方图特征子
void getHSVHistFeatures(const Mat& image, Mat& features) {
// TODO
}
}

@ -5,6 +5,6 @@ Namespace where all the C++ EasyPR functionality resides
*/
namespace easypr {
CPlate::CPlate() { bColored = true; }
CPlate::CPlate() { bColored = true; }
} /*! \namespace easypr*/

@ -5,16 +5,16 @@ namespace easypr {
CPlateDetect::CPlateDetect() {
m_plateLocate = new CPlateLocate();
// 默认EasyPR在一幅图中定位最多3个车
// 默认EasyPR在一幅图中定位最多3个车
m_maxPlates = 3;
}
CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); }
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate>& resultVec,
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec,
bool showDetectArea, int index) {
std::vector<Mat> resultPlates;
std::vector<CPlate> color_Plates;
std::vector<CPlate> sobel_Plates;
std::vector<CPlate> color_result_Plates;
@ -22,7 +22,8 @@ int CPlateDetect::plateDetect(Mat src, std::vector<CPlate>& resultVec,
std::vector<CPlate> all_result_Plates;
//如果颜色查找找到n个以上包含n个的车牌就不再进行Sobel查找了。
//如果颜色查找找到n个以上包含n个的车牌就不再进行Sobel查找了。
const int color_find_max = m_maxPlates;
m_plateLocate->plateColorLocate(src, color_Plates, index);
@ -35,7 +36,8 @@ int CPlateDetect::plateDetect(Mat src, std::vector<CPlate>& resultVec,
all_result_Plates.push_back(plate);
}
//颜色和边界闭操作同时采用
//颜色和边界闭操作同时采用
{
m_plateLocate->plateSobelLocate(src, sobel_Plates, index);
PlateJudge::instance()->plateJudge(sobel_Plates, sobel_result_Plates);
@ -43,12 +45,6 @@ int CPlateDetect::plateDetect(Mat src, std::vector<CPlate>& resultVec,
for (size_t i = 0; i < sobel_result_Plates.size(); i++) {
CPlate plate = sobel_result_Plates[i];
if (0) {
imshow("plate_mat", plate.getPlateMat());
waitKey(0);
destroyWindow("plate_mat");
}
plate.bColored = false;
plate.setPlateLocateType(SOBEL);
@ -57,14 +53,16 @@ int CPlateDetect::plateDetect(Mat src, std::vector<CPlate>& resultVec,
}
for (size_t i = 0; i < all_result_Plates.size(); i++) {
// 把截取的车牌图像依次放到左上角
// 把截取的车牌图像依次放到左上角
CPlate plate = all_result_Plates[i];
resultVec.push_back(plate);
}
return 0;
}
int CPlateDetect::showResult(const Mat& result) {
int CPlateDetect::showResult(const Mat &result) {
namedWindow("EasyPR", CV_WINDOW_AUTOSIZE);
const int RESULTWIDTH = 640; // 640 930

@ -14,20 +14,22 @@ PlateJudge* PlateJudge::instance() {
PlateJudge::PlateJudge() { svm_ = ml::SVM::load<ml::SVM>(kDefaultSvmPath); }
//! 对单幅图像进行SVM判断
int PlateJudge::plateJudge(const Mat& inMat, int& result) {
//! 对单幅图像进行SVM判断
int PlateJudge::plateJudge(const Mat &inMat, int &result) {
Mat features;
getHistogramFeatures(inMat, features);
float response = svm_->predict(features);
result = (int)response;
result = (int) response;
return 0;
}
//! 对多幅图像进行SVM判断
int PlateJudge::plateJudge(const std::vector<Mat>& inVec,
std::vector<Mat>& resultVec) {
//! 对多幅图像进行SVM判断
int PlateJudge::plateJudge(const std::vector<Mat> &inVec,
std::vector<Mat> &resultVec) {
int num = inVec.size();
for (int j = 0; j < num; j++) {
Mat inMat = inVec[j];
@ -40,9 +42,10 @@ int PlateJudge::plateJudge(const std::vector<Mat>& inVec,
return 0;
}
//! 对多幅车牌进行SVM判断
int PlateJudge::plateJudge(const std::vector<CPlate>& inVec,
std::vector<CPlate>& resultVec) {
//! 对多幅车牌进行SVM判断
int PlateJudge::plateJudge(const std::vector<CPlate> &inVec,
std::vector<CPlate> &resultVec) {
int num = inVec.size();
for (int j = 0; j < num; j++) {
CPlate inPlate = inVec[j];
@ -56,7 +59,9 @@ int PlateJudge::plateJudge(const std::vector<CPlate>& inVec,
else {
int w = inMat.cols;
int h = inMat.rows;
//再取中间部分判断一次
//再取中间部分判断一次
Mat tmpmat = inMat(Rect_<double>(w * 0.05, h * 0.1, w * 0.9, h * 0.8));
Mat tmpDes = inMat.clone();
resize(tmpmat, tmpDes, Size(inMat.size()));

File diff suppressed because it is too large Load Diff

@ -3,41 +3,49 @@
namespace easypr {
CPlateRecognize::CPlateRecognize() {}
CPlateRecognize::CPlateRecognize() { }
// !车牌识别模块
// !车牌识别模块
int CPlateRecognize::plateRecognize(Mat src,
std::vector<std::string> &licenseVec) {
// 车牌方块集合
// 车牌方块集合
std::vector<CPlate> plateVec;
// 进行深度定位使用颜色信息与二次Sobel
// 进行深度定位使用颜色信息与二次Sobel
int resultPD = plateDetect(src, plateVec, kDebug, 0);
if (resultPD == 0) {
size_t num = plateVec.size();
int index = 0;
//依次识别每个车牌内的符号
//依次识别每个车牌内的符号
for (size_t j = 0; j < num; j++) {
CPlate item = plateVec[j];
Mat plate = item.getPlateMat();
//获取车牌颜色
//获取车牌颜色
std::string plateType = getPlateColor(plate);
//获取车牌号
//获取车牌号
std::string plateIdentify = "";
int resultCR = charsRecognise(plate, plateIdentify);
if (resultCR == 0)
{
if (resultCR == 0) {
std::string license = plateType + ":" + plateIdentify;
licenseVec.push_back(license);
}
}
//完整识别过程到此结束
//如果是Debug模式则还需要将定位的图片显示在原图左上角
//完整识别过程到此结束
//如果是Debug模式则还需要将定位的图片显示在原图左上角
if (getPDDebug()) {
Mat result;
src.copyTo(result);
@ -69,7 +77,8 @@ int CPlateRecognize::plateRecognize(Mat src,
8);
}
//显示定位框的图片
//显示定位框的图片
showResult(result);
}
}

@ -1,76 +1,86 @@
#include "easypr/preprocess/gdts.h"
#include "easypr/preprocess/deface.h"
#include "easypr/util/util.h"
namespace easypr {
namespace preprocess {
// TODO 将下面的路径改成你的原始图片路径
// 图片不要多10-30张就足够了EasyPR对GDTS数据集的使用不以量为主要指标
// 只要这些图片足够反映你数据集的主要特征即可
const char* src_path = "F:/data/easypr-data/tmp-1";
// TODO 将下面的路径改成你希望生成捐赠给GDTS数据存放的新路径
const char* dst_path = "F:/data/easypr-data/tmp-2";
int generate_gdts() {
// 获取人脸识别文件
cv::CascadeClassifier cascade;
std::string cascadeName =
"resources/model/haarcascade_frontalface_alt_tree.xml";
////获取该路径下的所有文件
auto files = Utils::getFiles(src_path);
size_t size = files.size();
if (0 == size) {
std::cout << "No File Found!" << std::endl;
return 0;
}
std::cout << "Begin to prepare generate_gdts!" << std::endl;
for (size_t i = 0; i < size; i++) {
std::string filepath = files[i].c_str();
std::cout << "------------------" << std::endl;
std::cout << filepath << std::endl;
// EasyPR读取原图片
cv::Mat src = cv::imread(filepath);
// EasyPR开始对图片进行模糊化与裁剪化处理
cv::Mat img = imageProcess(src);
// EasyPR开始对图片进行人脸识别处理
cv::Mat dst = detectAndMaskFace(img, cascade, 1.5);
// 将图片导出到新路径
std::vector<std::string> spilt_path = Utils::splitString(filepath, '\\');
size_t spiltsize = spilt_path.size();
std::string filename = "";
if (spiltsize != 0) filename = spilt_path[spiltsize - 1];
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << dst_path << "/" << filename;
utils::imwrite(ss.str(), dst);
}
return 0;
}
// EasyPR的图像预处理函数进行模糊化与裁剪化处理
cv::Mat imageProcess(cv::Mat img) {
int width = img.size().width;
int height = img.size().height;
cv::Rect_<double> rect(width * 0.01, height * 0.01, width * 0.99,
height * 0.99);
cv::Mat dst = img(rect);
// GaussianBlur( dst, dst, Size(1, 1), 0, 0, BORDER_DEFAULT );
return dst;
}
}
}
#include "easypr/preprocess/gdts.h"
#include "easypr/preprocess/deface.h"
#include "easypr/util/util.h"
namespace easypr {
namespace preprocess {
// TODO 将下面的路径改成你的原始图片路径
// 图片不要多10-30张就足够了EasyPR对GDTS数据集的使用不以量为主要指标
// 只要这些图片足够反映你数据集的主要特征即可
const char* src_path = "F:/data/easypr-data/tmp-1";
// TODO 将下面的路径改成你希望生成捐赠给GDTS数据存放的新路径
const char* dst_path = "F:/data/easypr-data/tmp-2";
int generate_gdts() {
// 获取人脸识别文件
cv::CascadeClassifier cascade;
std::string cascadeName =
"resources/model/haarcascade_frontalface_alt_tree.xml";
////获取该路径下的所有文件
auto files = Utils::getFiles(src_path);
size_t size = files.size();
if (0 == size) {
std::cout << "No File Found!" << std::endl;
return 0;
}
std::cout << "Begin to prepare generate_gdts!" << std::endl;
for (size_t i = 0; i < size; i++) {
std::string filepath = files[i].c_str();
std::cout << "------------------" << std::endl;
std::cout << filepath << std::endl;
// EasyPR读取原图片
cv::Mat src = cv::imread(filepath);
// EasyPR开始对图片进行模糊化与裁剪化处理
cv::Mat img = imageProcess(src);
// EasyPR开始对图片进行人脸识别处理
cv::Mat dst = detectAndMaskFace(img, cascade, 1.5);
// 将图片导出到新路径
std::vector<std::string> spilt_path = Utils::splitString(filepath, '\\');
size_t spiltsize = spilt_path.size();
std::string filename = "";
if (spiltsize != 0) filename = spilt_path[spiltsize - 1];
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << dst_path << "/" << filename;
utils::imwrite(ss.str(), dst);
}
return 0;
}
// EasyPR的图像预处理函数进行模糊化与裁剪化处理
cv::Mat imageProcess(cv::Mat img) {
int width = img.size().width;
int height = img.size().height;
cv::Rect_<double> rect(width * 0.01, height * 0.01, width * 0.99,
height * 0.99);
cv::Mat dst = img(rect);
// GaussianBlur( dst, dst, Size(1, 1), 0, 0, BORDER_DEFAULT );
return dst;
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,123 +1,123 @@
#include "easypr/train/ann_train.h"
#include "easypr/config.h"
#include "easypr/core/chars_identify.h"
#include "easypr/core/core_func.h"
#include "easypr/util/util.h"
namespace easypr {
AnnTrain::AnnTrain(const char* chars_folder, const char* xml)
: chars_folder_(chars_folder), ann_xml_(xml) {
ann_ = cv::ml::ANN_MLP::create();
}
void AnnTrain::train() {
cv::Mat layers(1, 3, CV_32SC1);
layers.at<int>(0) = 120; // the input layer
layers.at<int>(1) = kNeurons; // the neurons
layers.at<int>(2) = kCharsTotalNumber; // the output layer
ann_->setLayerSizes(layers);
ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1);
ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP);
ann_->setBackpropWeightScale(0.1);
ann_->setBackpropMomentumScale(0.1);
auto traindata = tdata();
std::cout << "Training ANN model, please wait..." << std::endl;
long start = utils::getTimestamp();
ann_->train(traindata);
long end = utils::getTimestamp();
std::cout << "Training done. Time elapse: " << (end - start) << "ms"
<< std::endl;
ann_->save(ann_xml_);
std::cout << "Your ANN Model was saved to " << ann_xml_ << std::endl;
}
void AnnTrain::test() {
assert(chars_folder_);
for (int i = 0; i < kCharsTotalNumber; ++i) {
auto char_key = kChars[i];
char sub_folder[512] = {0};
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
fprintf(stdout, ">> Testing characters %s in %s \n", char_key, sub_folder);
auto chars_files = utils::getFiles(sub_folder);
int corrects = 0, sum = 0;
std::vector<std::pair<std::string, std::string>> error_files;
for (auto file : chars_files) {
auto img = cv::imread(file, 0); // a grayscale image
std::pair<std::string, std::string> ch =
CharsIdentify::instance()->identify(img);
if (ch.first == char_key) {
++corrects;
} else {
error_files.push_back(
std::make_pair(utils::getFileName(file), ch.second));
}
++sum;
}
fprintf(stdout, ">> [sum: %d, correct: %d, rate: %.2f]\n", sum, corrects,
(float)corrects / (sum == 0 ? 1 : sum));
std::string error_string;
auto end = error_files.end();
if (error_files.size() >= 10) {
end -= static_cast<size_t>(error_files.size() * (1 - 0.1));
}
for (auto k = error_files.begin(); k != end; ++k) {
auto kv = *k;
error_string.append(" ").append(kv.first).append(": ").append(
kv.second);
if (k != end - 1) {
error_string.append(",\n");
} else {
error_string.append("\n ...");
}
}
fprintf(stdout, ">> [\n%s\n ]\n", error_string.c_str());
}
}
cv::Ptr<cv::ml::TrainData> AnnTrain::tdata() {
assert(chars_folder_);
cv::Mat samples;
std::vector<int> labels;
std::cout << "Collecting chars in " << chars_folder_ << std::endl;
for (int i = 0; i < kCharsTotalNumber; ++i) {
auto char_key = kChars[i];
char sub_folder[512] = {0};
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
std::cout << " >> Featuring characters " << char_key << " in "
<< sub_folder << std::endl;
auto chars_files = utils::getFiles(sub_folder);
for (auto file : chars_files) {
auto img = cv::imread(file, 0); // a grayscale image
auto fps = features(img, kPredictSize);
samples.push_back(fps);
labels.push_back(i);
}
}
cv::Mat samples_;
samples.convertTo(samples_, CV_32F);
cv::Mat train_classes =
cv::Mat::zeros((int)labels.size(), kCharsTotalNumber, CV_32F);
for (int i = 0; i < train_classes.rows; ++i) {
train_classes.at<float>(i, labels[i]) = 1.f;
}
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
train_classes);
}
}
#include "easypr/train/ann_train.h"
#include "easypr/config.h"
#include "easypr/core/chars_identify.h"
#include "easypr/core/core_func.h"
#include "easypr/util/util.h"
namespace easypr {
AnnTrain::AnnTrain(const char* chars_folder, const char* xml)
: chars_folder_(chars_folder), ann_xml_(xml) {
ann_ = cv::ml::ANN_MLP::create();
}
void AnnTrain::train() {
cv::Mat layers(1, 3, CV_32SC1);
layers.at<int>(0) = 120; // the input layer
layers.at<int>(1) = kNeurons; // the neurons
layers.at<int>(2) = kCharsTotalNumber; // the output layer
ann_->setLayerSizes(layers);
ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1);
ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP);
ann_->setBackpropWeightScale(0.1);
ann_->setBackpropMomentumScale(0.1);
auto traindata = tdata();
std::cout << "Training ANN model, please wait..." << std::endl;
long start = utils::getTimestamp();
ann_->train(traindata);
long end = utils::getTimestamp();
std::cout << "Training done. Time elapse: " << (end - start) << "ms"
<< std::endl;
ann_->save(ann_xml_);
std::cout << "Your ANN Model was saved to " << ann_xml_ << std::endl;
}
void AnnTrain::test() {
assert(chars_folder_);
for (int i = 0; i < kCharsTotalNumber; ++i) {
auto char_key = kChars[i];
char sub_folder[512] = {0};
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
fprintf(stdout, ">> Testing characters %s in %s \n", char_key, sub_folder);
auto chars_files = utils::getFiles(sub_folder);
int corrects = 0, sum = 0;
std::vector<std::pair<std::string, std::string>> error_files;
for (auto file : chars_files) {
auto img = cv::imread(file, 0); // a grayscale image
std::pair<std::string, std::string> ch =
CharsIdentify::instance()->identify(img);
if (ch.first == char_key) {
++corrects;
} else {
error_files.push_back(
std::make_pair(utils::getFileName(file), ch.second));
}
++sum;
}
fprintf(stdout, ">> [sum: %d, correct: %d, rate: %.2f]\n", sum, corrects,
(float)corrects / (sum == 0 ? 1 : sum));
std::string error_string;
auto end = error_files.end();
if (error_files.size() >= 10) {
end -= static_cast<size_t>(error_files.size() * (1 - 0.1));
}
for (auto k = error_files.begin(); k != end; ++k) {
auto kv = *k;
error_string.append(" ").append(kv.first).append(": ").append(
kv.second);
if (k != end - 1) {
error_string.append(",\n");
} else {
error_string.append("\n ...");
}
}
fprintf(stdout, ">> [\n%s\n ]\n", error_string.c_str());
}
}
cv::Ptr<cv::ml::TrainData> AnnTrain::tdata() {
assert(chars_folder_);
cv::Mat samples;
std::vector<int> labels;
std::cout << "Collecting chars in " << chars_folder_ << std::endl;
for (int i = 0; i < kCharsTotalNumber; ++i) {
auto char_key = kChars[i];
char sub_folder[512] = {0};
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
std::cout << " >> Featuring characters " << char_key << " in "
<< sub_folder << std::endl;
auto chars_files = utils::getFiles(sub_folder);
for (auto file : chars_files) {
auto img = cv::imread(file, 0); // a grayscale image
auto fps = features(img, kPredictSize);
samples.push_back(fps);
labels.push_back(i);
}
}
cv::Mat samples_;
samples.convertTo(samples_, CV_32F);
cv::Mat train_classes =
cv::Mat::zeros((int)labels.size(), kCharsTotalNumber, CV_32F);
for (int i = 0; i < train_classes.rows; ++i) {
train_classes.at<float>(i, labels[i]) = 1.f;
}
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
train_classes);
}
}

@ -1,8 +1,8 @@
#include "easypr/train/train.h"
namespace easypr {
ITrain::ITrain() {}
ITrain::~ITrain() {}
}
#include "easypr/train/train.h"
namespace easypr {
ITrain::ITrain() {}
ITrain::~ITrain() {}
}

@ -5,6 +5,7 @@
#include <ctime>
#include <fstream>
#include <list>
#include <memory>
using namespace std;
@ -13,16 +14,21 @@ namespace easypr {
namespace demo {
int accuracyTest(const char* test_path) {
std::shared_ptr<easypr::Kv> kv(new easypr::Kv);
kv->load("etc/chinese_mapping");
auto files = Utils::getFiles(test_path);
CPlateRecognize pr;
// 设置Debug模式
// 设置Debug模式
pr.setDebug(false);
pr.setLifemode(true);
// 设置要处理的一张图片中最多有多少车牌
// 设置要处理的一张图片中最多有多少车牌
pr.setMaxPlates(4);
int size = files.size();
@ -34,42 +40,58 @@ int accuracyTest(const char* test_path) {
cout << "Begin to test the easypr accuracy!" << endl;
// 总的测试图片数量
// 总的测试图片数量
int count_all = 0;
// 错误的图片数量
// 错误的图片数量
int count_err = 0;
// 未识别的图片数量
// 未识别的图片数量
int count_norecogin = 0;
// not recognized pictures
std::list<std::string> not_recognized_files;
// 总的字符差距
// 总的字符差距
float diff_all = 0;
// 平均字符差距
// 平均字符差距
float diff_avg = 0;
// 完全匹配的识别次数
// 完全匹配的识别次数
float match_count = 0;
// 完全匹配的识别次数所占识别图片中的比例
// 完全匹配的识别次数所占识别图片中的比例
float match_rate = 0;
// 开始和结束时间
// 开始和结束时间
time_t begin, end;
time(&begin);
for (int i = 0; i < size; i++) {
string filepath = files[i].c_str();
// EasyPR开始判断车牌
// EasyPR开始判断车牌
Mat src = imread(filepath);
// 如果是非图像文件,直接过去
// 如果是非图像文件,直接过去
if (!src.data) continue;
cout << "------------------" << endl;
// 获取真实的车牌
// 获取真实的车牌
string plateLicense = Utils::getFileName(filepath);
cout << "原牌:" << plateLicense << endl;
cout << kv->get("original_plate") << ":" << plateLicense << endl;
vector<string> plateVec;
int result = pr.plateRecognize(src, plateVec);
@ -77,19 +99,22 @@ int accuracyTest(const char* test_path) {
int num = plateVec.size();
if (num == 0) {
cout << "无车牌" << endl;
if (plateLicense != "无车牌") {
cout << kv->get("empty_plate") << endl;
if (plateLicense != kv->get("empty_plate")) {
not_recognized_files.push_back(plateLicense);
count_norecogin++;
}
} else if (num > 1) {
// 多车牌使用diff最小的那个记录
// 多车牌使用diff最小的那个记录
int mindiff = 10000;
for (int j = 0; j < num; j++) {
cout << plateVec[j] << " (" << j + 1 << ")" << endl;
string colorplate = plateVec[j];
// 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小"
// 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小"
vector<string> spilt_plate = Utils::splitString(colorplate, ':');
int size = spilt_plate.size();
@ -100,29 +125,36 @@ int accuracyTest(const char* test_path) {
}
}
cout << "差距:" << mindiff << "个字符" << endl;
cout << kv->get("diff") << ":" << mindiff << kv->get("char") << endl;
if (mindiff == 0) {
// 完全匹配
// 完全匹配
match_count++;
}
diff_all = diff_all + mindiff;
} else {
// 单车牌只计算一次diff
// 单车牌只计算一次diff
for (int j = 0; j < num; j++) {
cout << plateVec[j] << endl;
string colorplate = plateVec[j];
// 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小"
// 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小"
vector<string> spilt_plate = Utils::splitString(colorplate, ':');
int size = spilt_plate.size();
if (size == 2 && spilt_plate[1] != "") {
int diff = utils::levenshtein_distance(plateLicense,
spilt_plate[size - 1]);
cout << "差距:" << diff << "个字符" << endl;
cout << kv->get("diff") << ":" << diff << kv->get("char") << endl;
if (diff == 0) {
// 完全匹配
// 完全匹配
match_count++;
}
diff_all = diff_all + diff;
@ -130,7 +162,7 @@ int accuracyTest(const char* test_path) {
}
}
} else {
cout << "错误码:" << result << endl;
cout << kv->get("error_code") << ":" << result << endl;
count_err++;
}
count_all++;
@ -141,13 +173,13 @@ int accuracyTest(const char* test_path) {
cout << "Easypr accuracy test end!" << endl;
cout << "------------------" << endl;
cout << endl;
cout << "统计参数:" << endl;
cout << "总图片数:" << count_all << ", ";
cout << "未识出图片:" << count_norecogin << ", ";
cout << kv->get("summaries") << ":" << endl;
cout << kv->get("sum_pictures") << ":" << count_all << ", ";
cout << kv->get("unrecognized") << ":" << count_norecogin << ", ";
float count_recogin = float(count_all - (count_err + count_norecogin));
float count_rate = count_recogin / count_all;
cout << "定位率:" << count_rate * 100 << "% " << endl;
cout << kv->get("locate_rate") << ":" << count_rate * 100 << "% " << endl;
if (count_recogin > 0) {
diff_avg = diff_all / count_recogin;
@ -157,17 +189,17 @@ int accuracyTest(const char* test_path) {
match_rate = match_count / count_recogin * 100;
}
cout << "平均字符差距:" << diff_avg << ", ";
cout << "完全匹配数:" << match_count << ", ";
cout << "完全匹配率:" << match_rate << "% " << endl;
cout << kv->get("diff_average") << ":" << diff_avg << ", ";
cout << kv->get("full_match") << ":" << match_count << ", ";
cout << kv->get("full_rate") << ":" << match_rate << "% " << endl;
double seconds = difftime(end, begin);
double avgsec = seconds / double(count_all);
cout << "总时间:" << seconds << ", ";
cout << "平均执行时间:" << avgsec << "" << endl;
cout << kv->get("seconds") << ":" << seconds << "min, ";
cout << kv->get("seconds_average") << ":" << avgsec << "sec" << endl;
cout << "未识出图片:" << endl;
cout << kv->get("unrecognized") << ":" << endl;
for (auto it = not_recognized_files.begin(); it != not_recognized_files.end();
++it) {
@ -187,20 +219,22 @@ int accuracyTest(const char* test_path) {
strftime(buf, sizeof(buf), "%Y-%m-%d %X", now);
myfile << string(buf) << endl;
myfile << "总图片数:" << count_all << "张, ";
myfile << "未识出图片:" << count_norecogin << "张, ";
myfile << "定位率:" << count_rate * 100 << "% " << endl;
myfile << "平均字符差距:" << diff_avg << "个, ";
myfile << "完全匹配数:" << match_count << "张, ";
myfile << "完全匹配率:" << match_rate << "% " << endl;
myfile << "总时间:" << seconds << "秒, ";
myfile << "平均执行时间:" << avgsec << "" << endl;
myfile << kv->get("sum_pictures") << ":" << count_all << ", ";
myfile << kv->get("unrecognized") << ":" << count_norecogin << ", ";
myfile << kv->get("locate_rate") << ":" << count_rate * 100 << "% "
<< endl;
myfile << kv->get("diff_average") << ":" << diff_avg << ", ";
myfile << kv->get("full_match") << ":" << match_count << ", ";
myfile << kv->get("full_rate") << ":" << match_rate << "% " << endl;
myfile << kv->get("seconds") << ":" << seconds << "sec, ";
myfile << kv->get("seconds_average") << ":" << avgsec << "sec" << endl;
myfile.close();
} else {
cout << "Unable to open file";
}
return 0;
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save