* improve the groundMSER segmention

* change readme and changelog
v1.6alpha
liuruoze 8 years ago
parent b0b0c05f08
commit 1636e8cb4e

@ -1,7 +1,91 @@
EasyPR版本更新
======
本次更新版本是1.6alpha版本,主要改进如下:
1. 采用灰度字符训练以及新的特征使中文字符正确率上升到了86%比上个版本提升了近14个百分点。
2. 借助于字符分割与识别算法的优化在general_test上的完整识别率(0-error)从原先的59%首次上升到现在的70%1-error则提升到了82%,提升巨大。
3. 在车牌判断模块中使用了新的SVM特征颜色+投影从而在保持鲁棒性的同时提升了正确率。定位指标中FScore从76%提升到82%.
4. 新增一种新的字符分割方法groundMSER字符分割方法。
5. 提供了近万张中文字符灰度图数据供训练并且在主界面中提供了一个方法从free大神的车牌集里提取中文与英文字符。
6. 提供了两万两千张的字符灰度图数据,供训练灰度字符模型使用。
7. 代码优化与升级许多bug修复。
8. Opencv3.2版本的支持编译前仅需要将config.h中将#define CV_VERSION_THREE_ZERO改为#define CV_VERSION_THREE_TWO即可。
9. 更加友好的linux与mac版本支持使用CMake即可顺利编译单独的utf-8与gbk的文件供分别的系统使用。
======
本次更新是EasyPR 1.5正式版本相比beta版本有以下几点更新
1.修正了SVM训练异常的问题现在1.5版本也可以自由的使用SVM训练了。这个问题确实是opencv的bug详见[讨论](https://github.com/opencv/opencv/issues/5054),在此感谢 @tka 同学的告知。
注意3.2的opencv也修正了这个问题如果你用3.2版本的话也可以。但是不清楚3.2版本是否会引入其他的问题在目前的EasyPR版本里即便用3.0或者3.1版本也可以规避训练异常的问题。
2.支持linux和mac编译如果碰到问题请在issue里提问。
3.增加一个无需配置opencv的[懒人版](http://git.oschina.net/easypr/EasyPR/attach_files)。仅仅支持vs2013也只能在debug和x86下运行其他情况的话还是得配置opencv。感谢范文捷同学的帮助。页面里的两个文件都要下载下载后用[7zip](http://www.7-zip.org/)解压。
其他的主要改进如下:
1.增加了一种新的基于文字定位的定位方法 (MSER), 在面对低对比度,低光照以及大图像上有较强的鲁棒性。
* 夜间的车牌图像
![夜间的车牌图像](resources/doc/res/night_1.jpg)
* 对比度非常低的图像
![对比度非常低的图像](resources/doc/res/contrast_1.jpg)
* 近距离的图像
![近距离的图像](resources/doc/res/near_1.jpg)
* 高分辨率的图像
### 更新
![高分辨率的图像](resources/doc/res/big_1.jpg)
2.更加合理的评价协议。结合新增的GroundTruth文件与ICDAR2003的协议使得整体评价指标更为合理。通用数据集里同时增加了近50张新图片。文字定位方法在面对这些复杂图片时比先前的SOBEL+COLOR的方法定位率提升了27个百分点。
实际运行时使用了文字定位与颜色定位的结合最终对256张的测试图片的测试结果如下
![v1.5版运行结果](resources/doc/res/v1.5_result.jpg)
3.使用了非极大值抑制算法去除相邻的车牌,使得最终输出变的合理。即便使用多个定位方法,最终也只会输出一个车牌,而且是可能性最大的车牌。
4.基于局部空间的大津阈值算法与自适应阈值算法,提升了文字分割与分子识别的准确率。
* 车牌图像
![车牌图像](resources/doc/res/not_avg_contrast.jpg)
* 普通大津阈值结果
![普通大津阈值结果](resources/doc/res/normal_ostu.jpg)
* 空间大津阈值结果
![空间大津阈值结果](resources/doc/res/spatial_ostu.jpg)
5.新的SVM模型与特征LBP提升了车牌判断的鲁棒性新的中文ANN识别模型提升了中文识别的整体准确率近15个百分点。
6.增加了Grid Search方法可以进行自动调参。
7.首次增加了多线程支持基于OpenMP的文字定位方法在最终的识别速度上比原先的单线程方法提高了接近2倍。
8.替换了一部分中文注释使得windows下的visual studio在面对全部以LF结尾的文件时也能成功通过编译。目前的程序只要opencv配置正确gitosc上通过zip下载下来的程序可以直接通过编译并运行。
关于本次改动的具体内容可以看博客中的[介绍](http://www.cnblogs.com/subconscious/p/5637735.html)。
======
本次更新是1.4 正式版,主要改进在于几个方面:
@ -25,7 +109,6 @@ EasyPR版本更新
======
本次更新是1.3beta版,主要改进在于提升了字符识别模块的准确性:
平均字符差距从0.7降低到0.4完整匹配度从68%左右上升到目前的81%平均执行时间从2秒降低到1.5秒。见下图:
@ -40,10 +123,8 @@ EasyPR版本更新
目前版本的问题是处理时间还是偏高1.3正式版本中会对这个问题进行fix。
======
本次更新是1.3alpha版,主要改进在于提升了字符识别模块的准确性:
平均字符差距从2.0降低到0.7完整匹配度从25%左右上升到目前的68%。
@ -60,10 +141,8 @@ EasyPR版本更新
目前版本的问题是处理时间大幅度上升1.3正式版本中会对这个问题进行fix。
======
本次更新是1.2版主要改进在于提升了车牌定位模块的准确性从70%左右到目前的94%,见下图:
![1.2版综合效果](resources/doc/res/testresult.png)
@ -86,10 +165,8 @@ EasyPR版本更新
* 与Linux版本做了整合可以实现跨平台编译。
======
目前EasyPR的版本是1.1相比上一个版本1.0,有以下更新(这次的更新内容较多,为了跟你现有的项目和代码不冲突,请
谨慎选择全部更新,最好新起一个目录试试新增的功能和内容):

@ -10,38 +10,28 @@ EasyPR是一个开源的中文车牌识别系统其目标是成为一个简
### 更新
当前master版本修订了两个小问题
本次更新版本是1.6alpha版本,主要有以下几点更新
1.train_auto异常的问题。
1. 代码优化与bug修复
2.opencv3.2的编译问题。现在opencv3.2的接口与3.1和3.0略有变动如果想要用3.2版本需要参照这个[issue](https://github.com/liuruoze/EasyPR/issues/152)里讨论的修改。
2. 增加了一些数据集,例如灰度字符供训练
3.增加灰度character数据
4.增加灰度中文数据,以及训练代码
5.新的分割方法groundER分割
6.大量bug修正与代码改进
下个版本会尝试继续优化整体系统与算法。
一个无需配置opencv的[懒人版](http://git.oschina.net/easypr/EasyPR/attach_files)。仅仅支持vs2013也只能在debug和x86下运行其他情况的话还是得配置opencv。感谢范文捷同学的帮助。页面里的两个文件都要下载下载后用[7zip](http://www.7-zip.org/)解压。
关于1.5版本改动的具体内容可以看博客中的[介绍](http://www.cnblogs.com/subconscious/p/5637735.html)。
3. 其他改动可见changlog.
### 跨平台
目前除了windows平台以外还有以下其他平台的EasyPR版本。一些平台的版本可能会暂时落后于主平台。
现在有一个无需配置opencv的1.5版本的[懒人版](http://git.oschina.net/easypr/EasyPR/attach_files)。仅仅支持vs2013也只能在debug和x86下运行其他情况的话还是得配置opencv。感谢范文捷同学的帮助。页面里的两个文件都要下载下载后用[7zip](http://www.7-zip.org/)解压。
|版本 | 开发者 | 版本 | 地址
|------|-------|-------|-------
| android | goldriver | 1.4 | [linuxxx/EasyPR_Android](https://github.com/linuxxx/EasyPR_Android)
| linux | Micooz | 1.5 | 已跟EasyPR整合
| linux | Micooz | 1.6 | 已跟EasyPR整合
| ios | zhoushiwei | 1.3 | [zhoushiwei/EasyPR-iOS](https://github.com/zhoushiwei/EasyPR-iOS)
| mac | zhoushiwei,Micooz | 1.5 | 已跟EasyPR整合
| mac | zhoushiwei,Micooz | 1.6 | 已跟EasyPR整合
| java | fan-wenjie | 1.2 | [fan-wenjie/EasyPR-Java](https://github.com/fan-wenjie/EasyPR-Java)
| 懒人版 | fan-wenjie | 1.5 | [git/oschina](http://git.oschina.net/easypr/EasyPR/attach_files)
### 兼容性
@ -163,6 +153,7 @@ EasyPR的resources/image/general_test文件夹下的图片数据遵循[GDSL协
| resources/train | 训练数据与说明
| resources/image | 测试用的图片
| resources/doc | 相关文档
| tmp | 需要自建,训练数据读取目录
以下表格是resources/image目录中子目录的解释:
@ -205,6 +196,15 @@ EasyPR的resources/image/general_test文件夹下的图片数据遵循[GDSL协
| chars.hpp | 字符识别相关
| plate.hpp | 车牌识别相关
以下表格是train目录下文件的解释:
|文件 | 解释
|------|----------
| ann_train.cpp | 训练二值化字符
| annCh_train.hpp | 训练中文灰度字符
| svm_train.hpp | 训练车牌判断
| create_data.hpp | 生成合成数据
### 使用
请参考[这里](Usage.md)
@ -235,6 +235,8 @@ EasyPR讨论QQ群号是一群366392603(已满)二群583022188
* fan-wenjie1.5版opencv整合版提供者
* Free1.6版数据提供者
### 鸣谢
taotao1233邱锦山唐大侠jsxyhelu如果有一天(zhoushiwei)学习奋斗袁承志圣城小石匠goldriverMicooz梦里时光Rain Wang任薛纪ahccom星夜落尘海豚嘎嘎(车主之家)刘超以及所有对EasyPR贡献数据的热心同学。
taotao1233邱锦山唐大侠jsxyhelu如果有一天(zhoushiwei)学习奋斗袁承志圣城小石匠goldriverMicooz梦里时光Rain Wang任薛纪ahccom星夜落尘海豚嘎嘎(车主之家),刘超,Free大神以及所有对EasyPR贡献数据的热心同学。

@ -95,9 +95,9 @@ static bool kDebug = false;
static const int kGrayCharWidth = 20;
static const int kGrayCharHeight = 32;
static const int kCharLBPGridX = 5;
static const int kCharLBPGridY = 8;
static const int kCharLBPPatterns = 32;
static const int kCharLBPGridX = 4;
static const int kCharLBPGridY = 4;
static const int kCharLBPPatterns = 16;
static const int kCharHiddenNeurans = 64;
@ -120,6 +120,8 @@ private:\
// Display the image.
#define SHOW_IMAGE(imgName, debug) \
if (debug) { \
namedWindow("imgName", WINDOW_AUTOSIZE); \
moveWindow("imgName", 500, 500); \
imshow("imgName", imgName); \
waitKey(0); \
destroyWindow("imgName"); \

@ -15,8 +15,8 @@ class CCharsSegment {
//! using ostu algotithm the segment chars in plate
int charsSegment(Mat input, std::vector<Mat>& resultVec, Color color = BLUE);
//! using project the segment chars in plate
int charsSegmentUsingProject(Mat input, std::vector<Mat>& resultVec, std::vector<Mat>& grayChars, Color color = BLUE);
//! using methods to segment chars in plate
int charsSegmentUsingOSTU(Mat input, std::vector<Mat>& resultVec, std::vector<Mat>& grayChars, Color color = BLUE);
int charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vector<Mat>& grayChars, Color color = BLUE);
//! using project

@ -3,6 +3,8 @@
#include "opencv2/opencv.hpp"
using namespace cv;
namespace easypr {
//! 获得车牌的特征数
@ -50,6 +52,8 @@ void getLBPplusHistFeatures(const cv::Mat& image, cv::Mat& features);
//! grayChar feauter
void getGrayCharFeatures(const cv::Mat& grayChar, cv::Mat& features);
void getGrayPlusLBP(const Mat& grayChar, Mat& features);
} /*! \namespace easypr*/
#endif // EASYPR_CORE_FEATURE_H_

@ -35,6 +35,7 @@ class SvmTrain : public ITrain {
std::vector<TrainItem> test_file_list_;
svmCallback extractFeature;
bool isPrepared = true;
};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -3128,3 +3128,123 @@ Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 177秒, 平均执行时间:0.691406秒
2017-04-24 22:40:39
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:68.5714%, 1-error:81.2245%, Chinese-precise:85.3061%
总共时间: 168秒, 平均执行时间:0.65625秒
2017-04-24 22:48:53
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 179秒, 平均执行时间:0.699219秒
2017-04-25 07:25:59
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:69.3878%, 1-error:82.0408%, Chinese-precise:84.898%
总共时间: 156秒, 平均执行时间:0.609375秒
2017-04-25 07:45:15
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:69.3878%, 1-error:82.0408%, Chinese-precise:84.898%
总共时间: 158秒, 平均执行时间:0.617188秒
2017-04-25 07:50:08
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 152秒, 平均执行时间:0.59375秒
2017-04-30 08:47:34
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 154秒, 平均执行时间:0.601563秒
2017-04-30 10:12:47
总图片数:1, Plates count:1, 未定位车牌:0, 检出率:100%
Recall:97.4057%, Precise:97.4057%, Fscore:97.4057%.
0-error:100%, 1-error:100%, Chinese-precise:100%
总共时间: 20秒, 平均执行时间:20秒
2017-04-30 10:13:46
总图片数:2, Plates count:2, 未定位车牌:0, 检出率:100%
Recall:98.662%, Precise:98.662%, Fscore:98.662%.
0-error:50%, 1-error:50%, Chinese-precise:50%
总共时间: 12秒, 平均执行时间:6秒
2017-04-30 10:17:45
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:67.6923%, 1-error:73.4615%, Chinese-precise:76.1538%
总共时间: 158秒, 平均执行时间:0.617188秒
2017-04-30 10:32:20
总图片数:2, Plates count:2, 未定位车牌:0, 检出率:100%
Recall:98.662%, Precise:98.662%, Fscore:98.662%.
0-error:50%, 1-error:50%, Chinese-precise:50%
总共时间: 2秒, 平均执行时间:1秒
2017-04-30 10:36:16
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:45.977%, 1-error:52.8736%, Chinese-precise:75.8621%
总共时间: 158秒, 平均执行时间:0.617188秒
2017-04-30 10:41:13
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:67.3077%, 1-error:73.0769%, Chinese-precise:76.1538%
总共时间: 169秒, 平均执行时间:0.660156秒
2017-04-30 10:47:55
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:54.9618%, 1-error:63.3588%, Chinese-precise:66.0305%
总共时间: 169秒, 平均执行时间:0.660156秒
2017-04-30 10:54:51
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:56.9231%, 1-error:71.1538%, Chinese-precise:74.6154%
总共时间: 193秒, 平均执行时间:0.753906秒
2017-04-30 11:12:08
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:67.3077%, 1-error:73.0769%, Chinese-precise:76.1538%
总共时间: 187秒, 平均执行时间:0.730469秒
2017-04-30 12:00:16
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:67.3077%, 1-error:73.0769%, Chinese-precise:76.1538%
总共时间: 162秒, 平均执行时间:0.632813秒
2017-04-30 12:04:03
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 153秒, 平均执行时间:0.597656秒
2017-04-30 12:21:43
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:63.1387%, 1-error:74.0876%, Chinese-precise:87.9562%
总共时间: 150秒, 平均执行时间:0.585938秒
2017-04-30 12:28:44
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:63.1387%, 1-error:74.0876%, Chinese-precise:87.9562%
总共时间: 151秒, 平均执行时间:0.589844秒
2017-04-30 12:32:22
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:63.8686%, 1-error:69.3431%, Chinese-precise:77.3723%
总共时间: 160秒, 平均执行时间:0.625秒
2017-04-30 12:37:26
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 211秒, 平均执行时间:0.824219秒
2017-04-30 13:53:08
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:68.2353%, 1-error:74.1176%, Chinese-precise:77.2549%
总共时间: 159秒, 平均执行时间:0.621094秒
2017-04-30 13:56:46
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:67.3077%, 1-error:73.0769%, Chinese-precise:76.1538%
总共时间: 160秒, 平均执行时间:0.625秒
2017-04-30 14:02:45
总图片数:256, Plates count:297, 未定位车牌:23, 检出率:92.2559%
Recall:85.1281%, Precise:80.5653%, Fscore:82.7839%.
0-error:70.6122%, 1-error:82.8571%, Chinese-precise:86.5306%
总共时间: 150秒, 平均执行时间:0.585938秒

File diff suppressed because it is too large Load Diff

@ -57,7 +57,7 @@ int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
color = getPlateType(tmpMat, true);
}
int result = m_charsSegment->charsSegmentUsingProject(plateMat, matChars, grayChars, color);
int result = m_charsSegment->charsSegmentUsingOSTU(plateMat, matChars, grayChars, color);
if (result == 0) {
int num = matChars.size();

@ -517,7 +517,7 @@ Mat preprocessCharMat(Mat in, int char_size) {
}
Mat clearLiuDingAndBorder(const Mat& grayImage, Color color) {
SHOW_IMAGE(grayImage, 1);
SHOW_IMAGE(grayImage, 0);
Mat img_threshold;
img_threshold = grayImage.clone();
spatial_ostu(img_threshold, 1, 1, color);
@ -526,7 +526,7 @@ Mat clearLiuDingAndBorder(const Mat& grayImage, Color color) {
clearBorder(img_threshold, cropRect);
Mat cropedGrayImage;
resize(grayImage(cropRect), cropedGrayImage, Size(kPlateResizeWidth, kPlateResizeHeight));
SHOW_IMAGE(cropedGrayImage, 1);
SHOW_IMAGE(cropedGrayImage, 0);
return cropedGrayImage;
}
@ -543,16 +543,22 @@ void NMStoCharacterByRatio(std::vector<CCharacter> &inVec, double overlap, const
float iou = computeIOU(rect, groundRect);
float w_ratio = (float)w / (float)gw;
float h_ratio = (float)h / (float)gh;
int w_diff = abs(w - gw);
int h_diff = abs(h - gh);
//float w_ratio = (float)w / (float)gw;
//float h_ratio = (float)h / (float)gh;
float w_ratio = 1 - (float)w_diff / (float)gw;
float h_ratio = 1 - (float)h_diff / (float)gh;
float a = 0.5f;
float b = 0.5f;
//cout << "str:" << character.getCharacterStr() << endl;
// if the charater is '1', its probalilty is redcued by its iou
if ("1" == character.getCharacterStr()) {
a = 0.2f;
b = 0.8f;
a = 0.3f; //0.2f;
b = 0.7f; //0.8f;
}
float c = 0.1f;
//float weighted_score = a * (float)score + b * w_ratio + c * h_ratio;
@ -608,7 +614,9 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
cvtColor(input, grayImage, CV_BGR2GRAY);
std::vector<cv::Mat> bgrSplit;
split(input, bgrSplit);
Mat grayChannel = grayImage; //clearLiuDingAndBorder(grayImage, color);
//Mat grayChannel = clearLiuDingAndBorder(grayImage, color); //clearLiuDingAndBorder(grayImage, color);
Mat grayChannel = grayImage;
// Mat cropedGrayImage = grayImage;
// generate all channgel images;
@ -709,20 +717,28 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
// find character
if (verifyCharRectSizes(rect)) {
Mat mserMat = adaptive_image_from_points(contour, rect, Size(char_size, char_size));
Mat charInput = preprocessCharMat(mserMat, char_size);
Rect charRect = rect;
Mat mserInput = preprocessCharMat(mserMat, char_size);
Rect charRect = rect;
Point center(charRect.tl().x + charRect.width / 2, charRect.tl().y + charRect.height / 2);
Mat tmpMat;
double ostu_level = cv::threshold(cimage(charRect), tmpMat, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
Mat grayCharMat = cimage(charRect);
Mat ostuMat;
switch (color) {
case BLUE: threshold(grayCharMat, ostuMat, 0, 255, CV_THRESH_BINARY + CV_THRESH_OTSU); break;
case YELLOW: threshold(grayCharMat, ostuMat, 0, 255, CV_THRESH_BINARY_INV + CV_THRESH_OTSU); break;
case WHITE: threshold(grayCharMat, ostuMat, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); break;
default: threshold(grayCharMat, ostuMat, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); break;
}
Mat ostuInput = preprocessChar(ostuMat);
// use judegMDOratio2 function to
// remove the small lines in character like "zh-cuan"
if (judegMDOratio2(cimage, rect, contour, mdoImage, 1.2f, true)) {
CCharacter charCandidate;
//cout << contour.size() << endl;
charCandidate.setCharacterPos(charRect);
charCandidate.setCharacterMat(charInput);
charCandidate.setCharacterMat(ostuInput); //charInput or ostuInput
charCandidate.setOstuLevel(ostu_level);
charCandidate.setCenterPoint(center);
int pos = getNearestIndex(center, groundCenters);
@ -736,19 +752,21 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
SHOW_IMAGE(showMSERImage(rect), 0);
}
}
SHOW_IMAGE(showMSERImage, 1);
SHOW_IMAGE(mdoImage, 1);
SHOW_IMAGE(showMSERImage, 0);
SHOW_IMAGE(mdoImage, 0);
// classify all the images;
CharsIdentify::instance()->classify(charVec);
Rect maxrect = groundRects.at(0);
// NMS to the seven groud truth rect
bool useGround = true;
if (useGround) {
for (auto charCandidate : charVec) {
int pos = charCandidate.getIndex();
charsVecVec.at(pos).push_back(charCandidate);
}
// NMS to the seven groud truth rect
charVec.clear();
Rect maxrect = groundRects.at(0);
for (size_t c = 0; c < charsVecVec.size(); c++) {
Mat testImage_2 = cimage.clone();
cvtColor(testImage_2, testImage_2, CV_GRAY2BGR);
@ -774,24 +792,28 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
charVec.push_back(charPos);
}
else {
if (charPosVec.size() != 0)
charVec.push_back(charPosVec.at(0));
if (charPosVec.size() != 0) {
CCharacter& inputChar = charPosVec.at(0);
charVec.push_back(inputChar);
Mat charMat = inputChar.getCharacterMat();
SHOW_IMAGE(charMat, 0);
}
}
for (auto charPos : charPosVec) {
Rect r = charPos.getCharacterPos();
if (r.area() > maxrect.area())
maxrect = r;
}
SHOW_IMAGE(testImage_3, 0);
}
}
else {
NMStoCharacterByRatio(charVec, 0.2f, maxrect);
}
if (charVec.size() < kCharsCountInOnePlate)
return -1;
if (charVec.size() < kCharsCountInOnePlate) return 0x03;
std::sort(charVec.begin(), charVec.end(),[](const CCharacter& r1, const CCharacter& r2) { return r1.getCharacterPos().x < r2.getCharacterPos().x; });
NMStoCharacterByRatio(charVec, 0.2f, maxrect);
std::sort(charVec.begin(), charVec.end(),
[](const CCharacter& r1, const CCharacter& r2) { return r1.getCharacterPos().x < r2.getCharacterPos().x; });
string predictLicense = "";
vector<Rect> sortedRect;
for (auto charCandidate : charVec) {
@ -805,16 +827,18 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
// find chinese rect
size_t specIndex = 0;
specIndex = GetSpecificRect(sortedRect);
//if (specIndex == 0)
// specIndex = 1;
SHOW_IMAGE(showImage(sortedRect[specIndex]), 1);
SHOW_IMAGE(showImage(sortedRect[specIndex]), 0);
Rect chineseRect;
if (specIndex < sortedRect.size())
chineseRect = GetChineseRect(sortedRect[specIndex]);
else
return 0x04;
vector<Rect> newSortedRect;
newSortedRect.push_back(chineseRect);
if (newSortedRect.size() == 0) return 0x05;
SHOW_IMAGE(showImage(chineseRect), 0);
RebuildRect(sortedRect, newSortedRect, specIndex);
@ -825,18 +849,8 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
Mat auxRoi(theImage, mr);
Mat newRoi;
if (i == 0) {
//mr = rectEnlarge(newSortedRect[i], cimage.cols, cimage.rows);
/*
Mat chineseRoi;
float slideLengthRatio = 0.1f;
if(!slideChineseGrayWindow(cimage, mr, chineseRoi, color, slideLengthRatio))
chineseRoi = cimage(mr);
Mat resizedChinese;
resize(chineseRoi, resizedChinese, Size(kGrayCharWidth, kGrayCharHeight));
grayChars.push_back(resizedChinese);
*/
Rect large_mr = rectEnlarge(mr, theImage.cols, theImage.rows);
//Rect large_mr = rectEnlarge(mr, theImage.cols, theImage.rows);
Rect large_mr = mr;
Mat grayChar(theImage, large_mr);
Mat grayChinese;
grayChinese.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
@ -866,20 +880,16 @@ int CCharsSegment::charsSegmentUsingMSER(Mat input, vector<Mat>& resultVec, vect
rectangle(showImage, mr, Scalar(0, 0, 255), 1);
resultVec.push_back(newRoi);
}
SHOW_IMAGE(showImage, 1);
SHOW_IMAGE(showImage, 0);
}
return 0;
}
int CCharsSegment::charsSegmentUsingProject(Mat input, vector<Mat>& resultVec, vector<Mat>& grayChars, Color color) {
int CCharsSegment::charsSegmentUsingOSTU(Mat input, vector<Mat>& resultVec, vector<Mat>& grayChars, Color color) {
if (!input.data) return 0x01;
//vector<int> out_indexs;
//projectSegment(input, color, out_indexs);
//charsSegmentUsingMSER(input, resultVec, grayChars, color);
Color plateType = color;
Mat input_grey;
cvtColor(input, input_grey, CV_BGR2GRAY);

@ -384,6 +384,62 @@ void getGrayPlusProject(const Mat& grayChar, Mat& features)
hconcat(feautreImg.reshape(1, 1), projectFeature.reshape(1, 1), features);
}
void getGrayPlusLBP(const Mat& grayChar, Mat& features)
{
// TODO: check channnels == 1
SHOW_IMAGE(grayChar, 0);
SHOW_IMAGE(255 - grayChar, 0);
// resize to uniform size, like 20x32
bool useResize = false;
bool useConvert = true;
bool useMean = true;
bool useLBP = true;
Mat char_mat;
if (useResize) {
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
}
else {
char_mat = grayChar;
}
SHOW_IMAGE(char_mat, 0);
// convert to float
Mat float_img;
if (useConvert) {
float scale = 1.f / 255;
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
}
else {
float_img = char_mat;
}
SHOW_IMAGE(float_img, 0);
// cut from mean, it can be optional
Mat mean_img;
if (useMean) {
float_img -= mean(float_img);
mean_img = float_img;
}
else {
mean_img = float_img;
}
SHOW_IMAGE(mean_img, 0);
// use lbp to get features, it can be changed to other
Mat originImage = mean_img.clone();
Mat lbpimage = libfacerec::olbp(mean_img);
SHOW_IMAGE(lbpimage, 0);
lbpimage = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
// 32x20 + 16x16
hconcat(mean_img.reshape(1, 1), lbpimage.reshape(1, 1), features);
}
void getLBPplusHistFeatures(const Mat& image, Mat& features) {
// TODO
Mat grayImage;

@ -14,16 +14,8 @@ CPlateRecognize::CPlateRecognize() {
// 2. chars recognize
int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVecOut, int img_index) {
// resize to uniform sizes
// TODO: groungTruth affect
float scale = 1.f;
Mat img = uniformResize(src, scale);
//Mat img = src;
if (0) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/phone/phone_img_" << img_index << ".jpg";
imwrite(ss.str(), img);
return -1;
}
// 1. plate detect
std::vector<CPlate> plateVec;

@ -18,7 +18,7 @@ namespace easypr {
type = 1;
kv_ = std::shared_ptr<Kv>(new Kv);
kv_->load("etc/province_mapping");
extractFeature = getGrayCharFeatures;
extractFeature = getGrayPlusProject;
}
void AnnChTrain::train()
@ -34,7 +34,7 @@ namespace easypr {
else
input_number = kGrayCharHeight * kGrayCharWidth;
//input_number += 64;
input_number += 64;
classNumber = kChineseNumber;
hidden_number = kCharHiddenNeurans;
@ -75,6 +75,14 @@ namespace easypr {
ann_->setBackpropWeightScale(0.1);
ann_->setBackpropMomentumScale(0.1);
auto files = Utils::getFiles(chars_folder_);
if (files.size() == 0) {
fprintf(stdout, "No file found in the train folder!\n");
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
fprintf(stdout, "Copy train data folder(like \"annCh\") under \"tmp\". \n");
return;
}
// using raw data or raw + synthic data.
trainVal(m_number_for_count);
}

@ -14,7 +14,9 @@ namespace easypr {
AnnTrain::AnnTrain(const char* chars_folder, const char* xml)
: chars_folder_(chars_folder), ann_xml_(xml) {
ann_ = cv::ml::ANN_MLP::create();
type = 1;
// type=0, all characters
// type=1, only chinese
type = 0;
kv_ = std::shared_ptr<Kv>(new Kv);
kv_->load("etc/province_mapping");
}
@ -56,6 +58,7 @@ void AnnTrain::train() {
layers.at<int>(2) = output_number;
}
else {
// Two-layers neural networks is hard to train, So do not try it
fprintf(stdout, ">> Use two-layers neural networks,\n");
fprintf(stdout, ">> First_hidden_neurons: %d \n", first_hidden_neurons);
fprintf(stdout, ">> Second_hidden_neurons: %d \n", second_hidden_neurons);
@ -74,8 +77,15 @@ void AnnTrain::train() {
ann_->setBackpropWeightScale(0.1);
ann_->setBackpropMomentumScale(0.1);
auto files = Utils::getFiles(chars_folder_);
if (files.size() == 0) {
fprintf(stdout, "No file found in the train folder!\n");
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
fprintf(stdout, "Copy train data folder(like \"ann\") under \"tmp\". \n");
return;
}
//using raw data or raw + synthic data.
//auto traindata = tdata();
auto traindata = sdata(350);
std::cout << "Training ANN model, please wait..." << std::endl;

@ -32,6 +32,12 @@ void SvmTrain::train() {
svm_->setP(0.1);
svm_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 20000, 0.0001));
if (train_file_list_.size() == 0) {
fprintf(stdout, "No file found in the train folder!\n");
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
fprintf(stdout, "Copy train data folder(like \"SVM\") under \"tmp\". \n");
return;
}
auto train_data = tdata();
fprintf(stdout, ">> Training SVM model, please wait...\n");
@ -184,8 +190,7 @@ cv::Ptr<cv::ml::TrainData> SvmTrain::tdata() {
samples.convertTo(samples_, CV_32FC1);
cv::Mat(responses).copyTo(responses_);
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
responses_);
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE, responses_);
}
} // namespace easypr

@ -154,6 +154,7 @@ namespace easypr {
// chinese character is wrong
float chinese_error_count = 0;
float chinese_error_rate = 0;
float chinese_precise_rate = 0;
// calucate the detect precise and recall
// use icdar 2003 evalution protoocal
@ -432,6 +433,7 @@ namespace easypr {
non_error_rate = non_error_count / count_recogin;
one_error_rate = one_error_count / count_recogin;
chinese_error_rate = chinese_error_count / count_recogin;
chinese_precise_rate = (count_recogin - chinese_error_count) / count_recogin;
}
double recall_2003_result = 0;
@ -459,9 +461,10 @@ namespace easypr {
cout << "Fscore" << "," << fscore_2003_result * 100 << "%" << ";" << endl;
cout << kv->get("char_recongize") << ": ";
//cout << "Recongtion rate:" << "," << (count_recogin / count_detect) * 100 << "%; " << endl;
cout << "0-error" << "," << non_error_rate * 100 << "%; ";
cout << "1-error" << "," << one_error_rate * 100 << "%; ";
cout << "Chinese-precise" << "," << (1 - chinese_error_rate) * 100 << "% " << endl;
cout << "Chinese-precise" << "," << chinese_precise_rate * 100 << "% " << endl;
double seconds = difftime(end, begin);
double avgsec = seconds / double(count_all);
@ -510,7 +513,7 @@ namespace easypr {
myfile << "0-error" << ":" << non_error_rate * 100 << "%, ";
myfile << "1-error" << ":" << one_error_rate * 100 << "%, ";
myfile << "Chinese-precise" << ":" << (1 - chinese_error_rate) * 100 << "% " << endl;
myfile << "Chinese-precise" << ":" << chinese_precise_rate * 100 << "% " << endl;
myfile << kv->get("seconds") << ": " << seconds << kv->get("sec") << ", ";
myfile << kv->get("seconds_average") << ":" << avgsec << kv->get("sec") << endl;

@ -107,7 +107,7 @@ int test_plate_recognize() {
CPlateRecognize pr;
pr.setLifemode(true);
pr.setDebug(false);
pr.setMaxPlates(4);
pr.setMaxPlates(1);
//pr.setDetectType(PR_DETECT_COLOR | PR_DETECT_SOBEL);
pr.setDetectType(easypr::PR_DETECT_CMSER);

Loading…
Cancel
Save