* add simple synthetic generate function and diagnose to character recognition rate.

v1.6alpha
liuruoze 9 years ago
parent b9b249eba5
commit 893797bcb2

@ -1792,3 +1792,38 @@ Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:220秒, 平均执行时间:1.1秒
2016-06-25 15:25:30
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:214秒, 平均执行时间:1.07秒
2016-06-25 15:41:53
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:60.0939%, 1-error:84.507%, Chinese error:28.6385%
总时间:217秒, 平均执行时间:1.085秒
2016-06-25 16:03:33
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:222秒, 平均执行时间:1.11秒
2016-06-25 16:43:53
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:63.3803%, 1-error:86.8545%, Chinese error:26.7606%
总时间:224秒, 平均执行时间:1.12秒
2016-06-25 16:58:43
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:233秒, 平均执行时间:1.165秒
2016-06-25 17:36:25
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:230秒, 平均执行时间:1.15秒
2016-06-25 18:07:01
总图片数:200, Plates count:238, 未识出图片:25, 定位率:89.4958%
Recall:83.2581%, Precise:86.2716%, Fscore:84.7381%.
0-error:66.6667%, 1-error:89.2019%, Chinese error:21.1268%
总时间:245秒, 平均执行时间:1.225秒

@ -16,6 +16,7 @@
#include "easypr/core/chars_identify.h"
#include "easypr/core/character.hpp"
#include "easypr/util/util.h"
#include "easypr/core/plate.hpp"
namespace easypr {
@ -26,6 +27,7 @@ class CCharsRecognise {
~CCharsRecognise();
int charsRecognise(cv::Mat plate, std::string& plateLicense);
int charsRecognise(CPlate& plate, std::string& plateLicense);
//! 获得车牌颜色
@ -38,7 +40,7 @@ class CCharsRecognise {
color = utils::utf8_to_gbk(color.c_str());
#endif
return color;
}
}
//! 设置变量

@ -59,7 +59,6 @@ Color getPlateType(const Mat& src, const bool adaptive_minsv);
//! 直方图均衡
Mat histeq(Mat in);
Mat charFeatures(Mat in, int sizeData);
Rect GetCenterRect(Mat& in);
Mat CutTheRect(Mat& in, Rect& rect);
int ThresholdOtsu(Mat mat);
@ -108,6 +107,12 @@ Rect adaptive_charrect_from_rect(const Rect& rect);
bool calcSafeRect(const RotatedRect& roi_rect, const Mat& src,
Rect_<float>& safeBoundRect);
// shift an image
Mat translateImg(Mat img, int offsetx, int offsety);
// rotate an image
Mat rotateImg(Mat source, float angle);
} /*! \namespace easypr*/
#endif // EASYPR_CORE_COREFUNC_H_

@ -34,6 +34,9 @@ void getHSVHistFeatures(const cv::Mat& image, cv::Mat& features);
//! LBP feature
void getLBPFeatures(const cv::Mat& image, cv::Mat& features);
//! get character feature
cv::Mat charFeatures(cv::Mat in, int sizeData);
} /*! \namespace easypr*/
#endif // EASYPR_CORE_FEATURE_H_

@ -39,6 +39,7 @@ namespace easypr {
m_distVec = plate.m_distVec;
m_mserCharVec = plate.m_mserCharVec;
m_reutCharVec = plate.m_reutCharVec;
}
CPlate& operator=(const CPlate& plate) {
@ -57,6 +58,7 @@ namespace easypr {
m_distVec = plate.m_distVec;
m_mserCharVec = plate.m_mserCharVec;
m_reutCharVec = plate.m_reutCharVec;
}
return *this;
}
@ -94,17 +96,13 @@ namespace easypr {
inline void setPlatDistVec(Vec2i param) { m_distVec = param; }
inline Vec2i getPlateDistVec() const { return m_distVec; }
inline void setMserCharacter(const std::vector<CCharacter>& param) {
m_mserCharVec = param;
}
inline void setMserCharacter(const std::vector<CCharacter>& param) { m_mserCharVec = param; }
inline void addMserCharacter(CCharacter param) { m_mserCharVec.push_back(param); }
inline std::vector<CCharacter> getCopyOfMserCharacters() { return m_mserCharVec; }
inline void addMserCharacter(CCharacter param) {
m_mserCharVec.push_back(param);
}
inline std::vector<CCharacter> getCopyOfMserCharacters() {
return m_mserCharVec;
}
inline void setReutCharacter(const std::vector<CCharacter>& param) { m_reutCharVec = param; }
inline void addReutCharacter(CCharacter param) { m_reutCharVec.push_back(param); }
inline std::vector<CCharacter> getCopyOfReutCharacters() { return m_reutCharVec; }
bool operator < (const CPlate& plate) const
{
@ -145,6 +143,9 @@ namespace easypr {
std::vector<CCharacter> m_mserCharVec;
std::vector<CCharacter> m_slwdCharVec;
std::vector<CCharacter> m_ostuCharVec;
std::vector<CCharacter> m_reutCharVec;
int m_charCount;
//! distVec

@ -16,6 +16,7 @@ class AnnTrain : public ITrain {
virtual void test();
std::pair<std::string, std::string> identifyChinese(cv::Mat input);
std::pair<std::string, std::string> identify(cv::Mat input);
private:
virtual cv::Ptr<cv::ml::TrainData> tdata();

File diff suppressed because it is too large Load Diff

@ -2,6 +2,7 @@
#include "easypr/core/character.hpp"
#include "easypr/config.h"
#include "easypr/core/core_func.h"
#include "easypr/core/feature.h"
namespace easypr {

@ -1,4 +1,5 @@
#include "easypr/core/chars_recognise.h"
#include "easypr/core/character.hpp"
#include "easypr/util/util.h"
namespace easypr {
@ -22,7 +23,7 @@ int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) {
int num = matChars.size();
for (int j = 0; j < num; j++)
{
Mat charMat = matChars[j];
Mat charMat = matChars.at(j);
bool isChinses = false;
if (j == 0)
isChinses = true;
@ -36,6 +37,44 @@ int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) {
}
return result;
}
int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
std::vector<Mat> matChars;
Mat plateMat = plate.getPlateMat();
int result = m_charsSegment->charsSegment(plateMat, matChars);
//std::cout << "charsSegment:" << result << std::endl;
if (result == 0) {
//for (auto block : matChars) {
// auto character = CharsIdentify::instance()->identify(block);
// plateLicense.append(character.second);
//}
int num = matChars.size();
for (int j = 0; j < num; j++)
{
Mat charMat = matChars.at(j);
bool isChinses = false;
if (j == 0)
isChinses = true;
auto character = CharsIdentify::instance()->identify(charMat, isChinses);
plateLicense.append(character.second);
CCharacter charResult;
charResult.setCharacterMat(charMat);
charResult.setCharacterStr(character.second);
plate.addReutCharacter(charResult);
}
}
if (plateLicense.size() < 7) {
return -1;
}
return result;
}
}

@ -685,50 +685,6 @@ Rect GetCenterRect(Mat &in) {
return _rect;
}
Mat charFeatures(Mat in, int sizeData) {
//抠取中间区域
Rect _rect = GetCenterRect(in);
Mat tmpIn = CutTheRect(in, _rect);
// Mat tmpIn = in.clone();
// Low data feature
Mat lowData;
resize(tmpIn, lowData, Size(sizeData, sizeData));
// Histogram features
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
// Last 10 is the number of moments components
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
Mat out = Mat::zeros(1, numCols, CV_32F);
// Asign values to
// feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量
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++;
}
for (int x = 0; x < lowData.cols; x++) {
for (int y = 0; y < lowData.rows; y++) {
out.at<float>(j) += (float) lowData.at <unsigned char> (x, y);
j++;
}
}
//std::cout << out << std::endl;
return out;
}
float countOfBigValue(Mat &mat, int iValue) {
float iCount = 0.0;
if (mat.rows > 1) {
@ -1894,6 +1850,23 @@ Mat adaptive_image_from_points(const std::vector<Point>& points,
return result;
}
// shift an image
Mat translateImg(Mat img, int offsetx, int offsety){
Mat dst;
Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, offsetx, 0, 1, offsety);
warpAffine(img, dst, trans_mat, img.size());
return dst;
}
// rotate an image
Mat rotateImg(Mat source, float angle){
Point2f src_center(source.cols / 2.0F, source.rows / 2.0F);
Mat rot_mat = getRotationMatrix2D(src_center, angle, 1.0);
Mat dst;
warpAffine(source, dst, rot_mat, source.size());
return dst;
}
//! 计算一个安全的Rect
//! 如果不存在返回false

@ -91,4 +91,51 @@ void getLBPFeatures(const Mat& image, Mat& features) {
features = lbp_hist;
}
Mat charFeatures(Mat in, int sizeData) {
const int VERTICAL = 0;
const int HORIZONTAL = 1;
// cut the cetner, will afect 5% perices.
Rect _rect = GetCenterRect(in);
Mat tmpIn = CutTheRect(in, _rect);
//Mat tmpIn = in.clone();
// Low data feature
Mat lowData;
resize(tmpIn, lowData, Size(sizeData, sizeData));
// Histogram features
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
// Last 10 is the number of moments components
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
Mat out = Mat::zeros(1, numCols, CV_32F);
// Asign values to
// feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量
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++;
}
for (int x = 0; x < lowData.cols; x++) {
for (int y = 0; y < lowData.rows; y++) {
out.at<float>(j) += (float)lowData.at <unsigned char>(x, y);
j++;
}
}
//std::cout << out << std::endl;
return out;
}
}

@ -85,7 +85,7 @@ namespace easypr {
cv::rectangle(result, outputRect, Scalar(0, 0, 255));
}
if (1)
if (0)
{
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/plate_" << index << "_" << i << ".jpg";

@ -109,6 +109,7 @@ namespace easypr {
int PlateJudge::plateJudgeUsingNMS(const std::vector<CPlate> &inVec, std::vector<CPlate> &resultVec, int maxPlates) {
std::vector<CPlate> plateVec;
int num = inVec.size();
bool outputResult = false;
for (int j = 0; j < num; j++) {
CPlate plate = inVec[j];
@ -123,6 +124,13 @@ namespace easypr {
int result = plateSetScore(plate);
if (result == 0) {
plateVec.push_back(plate);
if (outputResult) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/plate/has" << "/" << plate.getPlatePos().center << "_"
<< plate.getPlatePos().size << "_" << plate.getPlatePos().angle << "_"
<< plate.getPlateScore() << ".jpg";
imwrite(ss.str(), inMat);
}
}
else {
int w = inMat.cols;
@ -140,6 +148,22 @@ namespace easypr {
if (resultCascade == 0) {
plateVec.push_back(plate);
if (outputResult) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/plate/has" << "/" << plate.getPlatePos().center << "_"
<< plate.getPlatePos().size << "_" << plate.getPlatePos().angle << "_"
<< plate.getPlateScore() << ".jpg";
imwrite(ss.str(), tmpDes);
}
}
else {
if (outputResult) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/plate/no" << "/" << plate.getPlatePos().center << "_"
<< plate.getPlatePos().size << "_" << plate.getPlatePos().angle << "_"
<< plate.getPlateScore() << ".jpg";
imwrite(ss.str(), tmpDes);
}
}
}
}

@ -296,12 +296,9 @@ int CPlateRecognize::plateRecognizeAsText(Mat src, std::vector<CPlate> &licenseV
// !车牌识别模块
int CPlateRecognize::plateRecognize(Mat src,
std::vector<CPlate> &licenseVec, int index) {
int CPlateRecognize::plateRecognize(Mat src, std::vector<CPlate> &licenseVec, int index) {
// 车牌集合
std::vector<CPlate> plateVec;
// 对图像进行统一缩放,确保图像不要过大
@ -321,19 +318,17 @@ int CPlateRecognize::plateRecognize(Mat src,
int index = 0;
//依次识别每个车牌内的符号
for (size_t j = 0; j < num; j++) {
CPlate item = plateVec[j];
CPlate item = plateVec.at(j);
Mat plate = item.getPlateMat();
//获取车牌颜色
std::string plateType = getPlateColor(plate);
//获取车牌号
std::string plateIdentify = "";
int resultCR = charsRecognise(plate, plateIdentify);
int resultCR = charsRecognise(item, plateIdentify);
if (resultCR == 0) {
std::string license = plateType + ":" + plateIdentify;
item.setPlateStr(license);

@ -1,6 +1,7 @@
#include "easypr/train/ann_train.h"
#include "easypr/config.h"
#include "easypr/core/chars_identify.h"
#include "easypr/core/feature.h"
#include "easypr/core/core_func.h"
#include "easypr/util/util.h"
#include <numeric>
@ -64,7 +65,6 @@ void AnnTrain::train() {
layers.at<int>(3) = output_number;
}
ann_->setLayerSizes(layers);
ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1);
ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP);
@ -74,13 +74,13 @@ void AnnTrain::train() {
//using raw data or raw + synthic data.
//auto traindata = tdata();
auto traindata = sdata();
auto traindata = sdata(100);
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::cout << "Training done. Time elapse: " << (end - start) / (1000 * 60) << "minute"
<< std::endl;
ann_->save(ann_xml_);
@ -92,7 +92,6 @@ void AnnTrain::train() {
std::pair<std::string, std::string> AnnTrain::identifyChinese(cv::Mat input) {
cv::Mat feature = charFeatures(input, kPredictSize);
float maxVal = -2;
int result = -1;
cv::Mat output(1, kChineseNumber, CV_32FC1);
@ -115,6 +114,36 @@ std::pair<std::string, std::string> AnnTrain::identifyChinese(cv::Mat input) {
return std::make_pair(s, province);
}
std::pair<std::string, std::string> AnnTrain::identify(cv::Mat input) {
cv::Mat feature = charFeatures(input, kPredictSize);
float maxVal = -2;
int result = -1;
cv::Mat output(1, kCharsTotalNumber, CV_32FC1);
ann_->predict(feature, output);
for (int j = 0; j < kCharsTotalNumber; j++) {
float val = output.at<float>(j);
// std::cout << "j:" << j << "val:" << val << std::endl;
if (val > maxVal) {
maxVal = val;
result = j;
}
}
auto index = result;
if (index < kCharactersNumber) {
return std::make_pair(kChars[index], kChars[index]);
}
else {
const char* key = kChars[index];
std::string s = key;
std::string province = kv_->get(s);
return std::make_pair(s, province);
}
}
void AnnTrain::test() {
assert(chars_folder_);
@ -137,7 +166,11 @@ void AnnTrain::test() {
for (auto file : chars_files) {
auto img = cv::imread(file, 0); // a grayscale image
std::pair<std::string, std::string> ch = identifyChinese(img);
std::pair<std::string, std::string> ch;
if (type == 0) ch = identify(img);
if (type == 1) ch = identifyChinese(img);
if (ch.first == char_key) {
++corrects;
++corrects_all;
@ -179,7 +212,21 @@ void AnnTrain::test() {
}
cv::Mat getSyntheticImage(const Mat& image) {
int rand_type = rand();
Mat result = image.clone();
if (rand_type % 2 == 0) {
int ran_x = rand() % 7 - 3;
int ran_y = rand() % 7 - 3;
result = translateImg(result, ran_x, ran_y);
}
else if (rand_type % 2 != 0) {
float angle = float(rand() % 15 - 7);
result = rotateImg(result, angle);
}
return result;
}
@ -193,9 +240,9 @@ cv::Ptr<cv::ml::TrainData> AnnTrain::sdata(size_t number_for_count) {
if (type == 0) classNumber = kCharsTotalNumber;
if (type == 1) classNumber = kChineseNumber;
srand((unsigned)time(0));
for (int i = 0; i < classNumber; ++i) {
srand((unsigned)time(0));
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
char sub_folder[512] = { 0 };
@ -219,6 +266,11 @@ cv::Ptr<cv::ml::TrainData> AnnTrain::sdata(size_t number_for_count) {
auto img = matVec.at(ran_num);
auto simg = getSyntheticImage(img);
matVec.push_back(simg);
if (1) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << sub_folder << "/" << i << "_" << t << "_" << ran_num << ".jpg";
imwrite(ss.str(), simg);
}
}
fprintf(stdout, ">> Characters count: %d \n", matVec.size());

@ -56,15 +56,11 @@ namespace easypr {
plate.setPlatePos(rr);
plateVec.push_back(plate);
}
xmlMap[imageName] = plateVec;
}
return 0;
}
int accuracyTest(const char* test_path) {
std::shared_ptr<easypr::Kv> kv(new easypr::Kv);
kv->load("etc/chinese_mapping");
@ -90,12 +86,10 @@ namespace easypr {
CPlateRecognize pr;
// 设置Debug模式
pr.setDebug(false);
pr.setLifemode(true);
// 设置要处理的一张图片中最多有多少车牌
pr.setMaxPlates(4);
pr.setDetectType(PR_DETECT_COLOR | PR_DETECT_SOBEL);
//pr.setDetectType(PR_DETECT_CMSER);
@ -267,6 +261,14 @@ namespace easypr {
if (diff == 2) {
one_error_count++;
}
vector<CCharacter> charVec = matchPlate->getCopyOfReutCharacters();
CCharacter character = charVec.at(0);
if (1) {
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/chinese" << "/" << i << "_" << t << "_" << character.getCharacterStr() << ".jpg";
imwrite(ss.str(), character.getCharacterMat());
}
}
cout << " chineseError:" << chineseError << endl;
}
@ -282,7 +284,8 @@ namespace easypr {
}
}
/*if (1)
/* REMAIN
if (1)
{
std::stringstream ss(std::stringstream::in | std::stringstream::out);
ss << "resources/image/tmp/plate_" << license << ".jpg";
@ -398,12 +401,14 @@ namespace easypr {
cout << kv->get("seconds") << ":" << seconds << kv->get("sec") << ", ";
cout << kv->get("seconds_average") << ":" << avgsec << kv->get("sec") << endl;
/*cout << kv->get("unrecognized") << ":" << endl;
/* REMAIN
cout << kv->get("unrecognized") << ":" << endl;
for (auto it = not_recognized_files.begin(); it != not_recognized_files.end();
++it) {
cout << *it << endl;
}
cout << endl;*/
cout << endl;
*/
cout << "------------------" << endl;
ofstream myfile("accuracy.txt", ios::app);

Loading…
Cancel
Save