diff --git a/src/core/chars_identify.cpp b/src/core/chars_identify.cpp index b2ffff5..1886d50 100644 --- a/src/core/chars_identify.cpp +++ b/src/core/chars_identify.cpp @@ -9,20 +9,20 @@ namespace easypr{ #define VERTICAL 0 #define NDEBUG -//涓浗杞︾墝 +//中国车牌 const char strCharacters[] = {'0','1','2','3','4','5',\ - '6','7','8','9','A','B', 'C', 'D', 'E','F', 'G', 'H', /* 娌℃湁I */\ - 'J', 'K', 'L', 'M', 'N', /* 娌℃湁O */ 'P', 'Q', 'R', 'S', 'T', \ + '6','7','8','9','A','B', 'C', 'D', 'E','F', 'G', 'H', /* 没有I */\ + 'J', 'K', 'L', 'M', 'N', /* 没有O */ 'P', 'Q', 'R', 'S', 'T', \ 'U','V', 'W', 'X', 'Y', 'Z'}; -const int numCharacter = 34; /* 娌℃湁I鍜0,10涓暟瀛椾笌24涓嫳鏂囧瓧绗︿箣鍜 */ +const int numCharacter = 34; /* 没有I和0,10个数字与24个英文字符之和 */ -//浠ヤ笅閮芥槸鎴戣缁冩椂鐢ㄥ埌鐨勪腑鏂囧瓧绗︽暟鎹紝骞朵笉鍏ㄩ潰锛屾湁浜涚渷浠芥病鏈夎缁冩暟鎹墍浠ユ病鏈夊瓧绗 -const string strChinese[] = {"zh_cuan" /* 宸 */, "zh_e" /* 閯 */, "zh_gan" /* 璧*/, \ - "zh_hei" /* 榛 */, "zh_hu" /* 娌 */, "zh_ji" /* 鍐 */, \ - "zh_jl" /* 鍚 */, "zh_jin" /* 娲 */, "zh_jing" /* 浜 */, "zh_shan" /* 闄 */, \ - "zh_liao" /* 杈 */, "zh_lu" /* 椴 */, "zh_min" /* 闂 */, "zh_ning" /* 瀹 */, \ - "zh_su" /* 鑻 */, "zh_sx" /* 鏅 */, "zh_wan" /* 鐨 */,\ - "zh_yu" /* 璞 */, "zh_yue" /* 绮 */, "zh_zhe" /* 娴 */}; +//以下都是我训练时用到的中文字符数据,并不全面,有些省份没有训练数据所以没有字符 +const string strChinese[] = {"zh_cuan" /* 川 */, "zh_e" /* 鄂 */, "zh_gan" /* 赣*/, \ + "zh_hei" /* 黑 */, "zh_hu" /* 沪 */, "zh_ji" /* 冀 */, \ + "zh_jl" /* 吉 */, "zh_jin" /* 津 */, "zh_jing" /* 京 */, "zh_shan" /* 陕 */, \ + "zh_liao" /* 辽 */, "zh_lu" /* 鲁 */, "zh_min" /* 闽 */, "zh_ning" /* 宁 */, \ + "zh_su" /* 苏 */, "zh_sx" /* 晋 */, "zh_wan" /* 皖 */,\ + "zh_yu" /* 豫 */, "zh_yue" /* 粤 */, "zh_zhe" /* 浙 */}; const int numChinese = 20; const int numAll = 54; /* 34+20=54 */ @@ -36,26 +36,26 @@ CCharsIdentify::CCharsIdentify() if (m_map.empty()) { - m_map.insert(pair("zh_cuan","宸")); - m_map.insert(pair("zh_e","閯")); - m_map.insert(pair("zh_gan","璧")); - m_map.insert(pair("zh_hei","榛")); - m_map.insert(pair("zh_hu","娌")); - m_map.insert(pair("zh_ji","鍐")); - m_map.insert(pair("zh_jl","鍚")); - m_map.insert(pair("zh_jin","娲")); - m_map.insert(pair("zh_jing","浜")); - m_map.insert(pair("zh_shan","闄")); - m_map.insert(pair("zh_liao","杈")); - m_map.insert(pair("zh_lu","椴")); - m_map.insert(pair("zh_min","闂")); - m_map.insert(pair("zh_ning","瀹")); - m_map.insert(pair("zh_su","鑻")); - m_map.insert(pair("zh_sx","鏅")); - m_map.insert(pair("zh_wan","鐨")); - m_map.insert(pair("zh_yu","璞")); - m_map.insert(pair("zh_yue","绮")); - m_map.insert(pair("zh_zhe","娴")); + m_map.insert(pair("zh_cuan","川")); + m_map.insert(pair("zh_e","鄂")); + m_map.insert(pair("zh_gan","赣")); + m_map.insert(pair("zh_hei","黑")); + m_map.insert(pair("zh_hu","沪")); + m_map.insert(pair("zh_ji","冀")); + m_map.insert(pair("zh_jl","吉")); + m_map.insert(pair("zh_jin","津")); + m_map.insert(pair("zh_jing","京")); + m_map.insert(pair("zh_shan","陕")); + m_map.insert(pair("zh_liao","辽")); + m_map.insert(pair("zh_lu","鲁")); + m_map.insert(pair("zh_min","闽")); + m_map.insert(pair("zh_ning","宁")); + m_map.insert(pair("zh_su","苏")); + m_map.insert(pair("zh_sx","晋")); + m_map.insert(pair("zh_wan","皖")); + m_map.insert(pair("zh_yu","豫")); + m_map.insert(pair("zh_yue","粤")); + m_map.insert(pair("zh_zhe","浙")); } } @@ -72,7 +72,7 @@ void CCharsIdentify::LoadModel(string s) ann.load(s.c_str(), "ann"); } -//create the accumulation histograms,img is a binary image, t is 姘村钩鎴栧瀭鐩 +//create the accumulation histograms,img is a binary image, t is 水平或垂直 Mat CCharsIdentify::ProjectedHistogram(Mat img, int t) { int sz=(t)?img.rows:img.cols; @@ -80,7 +80,7 @@ Mat CCharsIdentify::ProjectedHistogram(Mat img, int t) for(int j=0; j(j)=countNonZero(data); //缁熻杩欎竴琛屾垨涓鍒椾腑锛岄潪闆跺厓绱犵殑涓暟锛屽苟淇濆瓨鍒癿hist涓 + mhist.at(j)=countNonZero(data); //统计这一行或一列中,非零元素的个数,并保存到mhist中 } //Normalize histogram @@ -88,12 +88,12 @@ Mat CCharsIdentify::ProjectedHistogram(Mat img, int t) minMaxLoc(mhist, &min, &max); if(max>0) - mhist.convertTo(mhist,-1 , 1.0f/max, 0);//鐢╩hist鐩存柟鍥句腑鐨勬渶澶у硷紝褰掍竴鍖栫洿鏂瑰浘 + mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图 return mhist; } -//! 鑾峰緱瀛楃鐨勭壒寰佸浘 +//! 获得字符的特征图 Mat CCharsIdentify::features(Mat in, int sizeData) { //Histogram features @@ -109,7 +109,7 @@ Mat CCharsIdentify::features(Mat in, int sizeData) Mat out = Mat::zeros(1, numCols, CV_32F); - //Asign values to feature,ANN鐨勬牱鏈壒寰佷负姘村钩銆佸瀭鐩寸洿鏂瑰浘鍜屼綆鍒嗚鲸鐜囧浘鍍忔墍缁勬垚鐨勭煝閲 + //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量 int j=0; for(int i=0; i matVec; string plateIdentify = ""; @@ -33,7 +33,7 @@ int CCharsRecognise::charsRecognise(Mat plate, string& plateLicense) Mat charMat = matVec[j]; bool isChinses = false; - //榛樿棣栦釜瀛楃鍧楁槸涓枃瀛楃 + //默认首个字符块是中文字符 if (j == 0) isChinses = true; diff --git a/src/core/chars_segment.cpp b/src/core/chars_segment.cpp index 202c64d..44ab19a 100644 --- a/src/core/chars_segment.cpp +++ b/src/core/chars_segment.cpp @@ -14,7 +14,7 @@ CCharsSegment::CCharsSegment() m_LiuDingSize = DEFAULT_LIUDING_SIZE; m_theMatWidth = DEFAULT_MAT_WIDTH; - //锛佽溅鐗岄鑹插垽鏂弬鏁 + //!车牌颜色判断参数 m_ColorThreshold = DEFAULT_COLORTHRESHOLD; m_BluePercent = DEFAULT_BLUEPERCEMT; m_WhitePercent = DEFAULT_WHITEPERCEMT; @@ -22,7 +22,7 @@ CCharsSegment::CCharsSegment() m_debug = DEFAULT_DEBUG; } -//! 瀛楃灏哄楠岃瘉 +//! 字符尺寸验证 bool CCharsSegment::verifySizes(Mat r){ //Char sizes 45x90 float aspect=45.0f/90.0f; @@ -46,12 +46,12 @@ bool CCharsSegment::verifySizes(Mat r){ return false; } -//! 瀛楃棰勫鐞 +//! 字符预处理 Mat CCharsSegment::preprocessChar(Mat in){ //Remap image int h=in.rows; int w=in.cols; - int charSize=CHAR_SIZE; //缁熶竴姣忎釜瀛楃鐨勫ぇ灏 + int charSize=CHAR_SIZE; //统一每个字符的大小 Mat transformMat=Mat::eye(2,3,CV_32F); int m=max(w,h); transformMat.at(0,2)=m/2 - w/2; @@ -66,7 +66,7 @@ Mat CCharsSegment::preprocessChar(Mat in){ return out; } -//! 鐩存柟鍥惧潎琛★紝涓哄垽鏂溅鐗岄鑹插仛鍑嗗 +//! 直方图均衡,为判断车牌颜色做准备 Mat CCharsSegment::histeq(Mat in) { Mat out(in.size(), in.type()); @@ -88,8 +88,8 @@ Mat CCharsSegment::histeq(Mat in) } //getPlateType -//鍒ゆ柇杞︾墝鐨勭被鍨嬶紝1涓鸿摑鐗岋紝2涓洪粍鐗岋紝0涓烘湭鐭ワ紝榛樿钃濈墝 -//閫氳繃鍍忕礌涓摑鑹叉墍鍗犳瘮渚嬬殑澶氬皯鏉ュ垽鏂紝澶т簬0.3涓鸿摑鐗岋紝鍚﹀垯涓洪粍鐗 +//判断车牌的类型,1为蓝牌,2为黄牌,0为未知,默认蓝牌 +//通过像素中蓝色所占比例的多少来判断,大于0.3为蓝牌,否则为黄牌 PlateColor CCharsSegment::getPlateType(Mat input) { Mat img; @@ -129,9 +129,9 @@ PlateColor CCharsSegment::getPlateType(Mat input) } //clearLiuDing -//鍘婚櫎杞︾墝涓婃柟鐨勯挳閽 -//璁$畻姣忚鍏冪礌鐨勯樁璺冩暟锛屽鏋滃皬浜嶺璁や负鏄煶涓侊紝灏嗘琛屽叏閮ㄥ~0锛堟秱榛戯級 -//X鐨勬帹鑽愬间负锛屽彲鏍规嵁瀹為檯璋冩暣 +//去除车牌上方的钮钉 +//计算每行元素的阶跃数,如果小于X认为是柳丁,将此行全部填0(涂黑) +//X的推荐值为,可根据实际调整 void CCharsSegment::clearLiuDing(Mat &img) { int *jump = new int[img.rows]; @@ -144,7 +144,7 @@ void CCharsSegment::clearLiuDing(Mat &img) jump[i]++; } if (jump[i] <= m_LiuDingSize) { - // 灏嗘琛屾秱榛 + // 将此行涂黑 for (int k = 0; k < img.rows; ++k) { img.at(i, k) = 0; } @@ -155,13 +155,13 @@ void CCharsSegment::clearLiuDing(Mat &img) jump = nullptr; } -//! 瀛楃鍒嗗壊涓庢帓搴 +//! 字符分割与排序 int CCharsSegment::charsSegment(Mat input, vector& resultVec) { if( !input.data ) { return -3; } - //鍒ゆ柇杞︾墝棰滆壊浠ユ纭threshold鏂规硶 + //判断车牌颜色以此确认threshold方法 PlateColor plateType = getPlateType(input); cvtColor(input, input, CV_RGB2GRAY); @@ -179,7 +179,7 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) imwrite(ss.str(), img_threshold); } - //鍘婚櫎杞︾墝涓婃柟鐨勬煶閽変互鍙婁笅鏂圭殑妯嚎绛夊共鎵 + //去除车牌上方的柳钉以及下方的横线等干扰 clearLiuDing(img_threshold); @@ -205,7 +205,7 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) vector vecRect; //Remove patch that are no inside limits of aspect ratio and area. - //灏嗕笉绗﹀悎鐗瑰畾灏哄鐨勫浘鍧楁帓闄ゅ嚭鍘 + //将不符合特定尺寸的图块排除出去 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); @@ -221,13 +221,13 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) return -3; vector sortedRect(vecRect); - //瀵圭鍚堝昂瀵哥殑鍥惧潡鎸夌収浠庡乏鍒板彸杩涜鎺掑簭 + //对符合尺寸的图块按照从左到右进行排序 std::sort(sortedRect.begin(), sortedRect.end(), [] (const Rect &r1, const Rect &r2) { return r1.x < r2.x; }); int specIndex = 0; - //鑾峰緱鎸囩ず鍩庡競鐨勭壒瀹歊ect,濡傝嫃A鐨"A" + //获得指示城市的特定Rect,如苏A的"A" specIndex = GetSpecificRect(sortedRect); if(m_debug) @@ -241,9 +241,9 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) } } - //鏍规嵁鐗瑰畾Rect鍚戝乏鍙嶆帹鍑轰腑鏂囧瓧绗 - //杩欐牱鍋氱殑涓昏鍘熷洜鏄牴鎹甪indContours鏂规硶寰堥毦鎹曟崏鍒颁腑鏂囧瓧绗︾殑鍑嗙‘Rect锛屽洜姝や粎鑳 - //閫杩囩壒瀹氱畻娉曟潵鎸囧畾 + //根据特定Rect向左反推出中文字符 + //这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 + //退过特定算法来指定 Rect chineseRect; if (specIndex < sortedRect.size()) chineseRect = GetChineseRect(sortedRect[specIndex]); @@ -259,9 +259,9 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) } - //鏂板缓涓涓叏鏂扮殑鎺掑簭Rect - //灏嗕腑鏂囧瓧绗ect绗竴涓姞杩涙潵锛屽洜涓哄畠鑲畾鏄渶宸﹁竟鐨 - //鍏朵綑鐨凴ect鍙寜鐓ч『搴忓幓6涓紝杞︾墝鍙彲鑳芥槸7涓瓧绗︼紒杩欐牱鍙互閬垮厤闃村奖瀵艰嚧鐨勨1鈥濆瓧绗 + //新建一个全新的排序Rect + //将中文字符Rect第一个加进来,因为它肯定是最左边的 + //其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 vector newSortedRect; newSortedRect.push_back(chineseRect); RebuildRect(sortedRect, newSortedRect, specIndex); @@ -290,7 +290,7 @@ int CCharsSegment::charsSegment(Mat input, vector& resultVec) return 0; } -//! 鏍规嵁鐗规畩杞︾墝鏉ユ瀯閫犵寽娴嬩腑鏂囧瓧绗︾殑浣嶇疆鍜屽ぇ灏 +//! 根据特殊车牌来构造猜测中文字符的位置和大小 Rect CCharsSegment::GetChineseRect(const Rect rectSpe) { int height = rectSpe.height; @@ -306,7 +306,7 @@ Rect CCharsSegment::GetChineseRect(const Rect rectSpe) return a; } -//! 鎵惧嚭鎸囩ず鍩庡競鐨勫瓧绗︾殑Rect锛屼緥濡傝嫃A7003X锛屽氨鏄"A"鐨勪綅缃 +//! 找出指示城市的字符的Rect,例如苏A7003X,就是"A"的位置 int CCharsSegment::GetSpecificRect(const vector& vecRect) { vector xpositions; @@ -333,7 +333,7 @@ int CCharsSegment::GetSpecificRect(const vector& vecRect) Rect mr = vecRect[i]; int midx = mr.x + mr.width/2; - //濡傛灉涓涓瓧绗︽湁涓瀹氱殑澶у皬锛屽苟涓斿湪鏁翠釜杞︾墝鐨1/7鍒2/7涔嬮棿锛屽垯鏄垜浠鎵剧殑鐗规畩杞︾墝 + //如果一个字符有一定的大小,并且在整个车牌的1/7到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)) { @@ -344,12 +344,12 @@ int CCharsSegment::GetSpecificRect(const vector& vecRect) return specIndex; } -//! 杩欎釜鍑芥暟鍋氫袱涓簨鎯 -// 1.鎶婄壒娈婂瓧绗ect宸﹁竟鐨勫叏閮≧ect鍘绘帀锛屽悗闈㈠啀閲嶅缓涓枃瀛楃鐨勪綅缃 -// 2.浠庣壒娈婂瓧绗ect寮濮嬶紝渚濇閫夋嫨6涓猂ect锛屽浣欑殑鑸嶅幓銆 +//! 这个函数做两个事情 +// 1.把特殊字符Rect左边的全部Rect去掉,后面再重建中文字符的位置。 +// 2.从特殊字符Rect开始,依次选择6个Rect,多余的舍去。 int CCharsSegment::RebuildRect(const vector& vecRect, vector& outRect, int specIndex) { - //鏈澶у彧鑳芥湁7涓猂ect,鍑忓幓涓枃鐨勫氨鍙湁6涓猂ect + //最大只能有7个Rect,减去中文的就只有6个Rect int count = 6; for (size_t i = specIndex; i < vecRect.size() && count; ++i, --count) { diff --git a/src/core/features.cpp b/src/core/features.cpp index e5a790f..26ec713 100644 --- a/src/core/features.cpp +++ b/src/core/features.cpp @@ -1,6 +1,6 @@ -// 杩欎釜鏂囦欢瀹氫箟浜咵asyPR閲屾墍鏈夌壒寰佺敓鎴愮殑鍑芥暟 -// 鎵灞炲懡鍚嶇┖闂翠负easypr -// 杩欎釜閮ㄥ垎涓殑鐗瑰緛鐢眅asypr鐨勫紑鍙戣呬慨鏀 +// 这个文件定义了EasyPR里所有特征生成的函数 +// 所属命名空间为easypr +// 这个部分中的特征由easypr的开发者修改 #include "../include/prep.h" #include "../include/features.h" @@ -10,7 +10,7 @@ */ namespace easypr { -//! 鐩存柟鍥惧潎琛 +//! 直方图均衡 Mat histeq(Mat in) { Mat out(in.size(), in.type()); @@ -31,7 +31,7 @@ Mat histeq(Mat in) return out; } -// 锛佽幏鍙栧瀭鐩村拰姘村钩鏂瑰悜鐩存柟鍥 +// !获取垂直和水平方向直方图 Mat ProjectedHistogram(Mat img, int t) { int sz=(t)?img.rows:img.cols; @@ -39,7 +39,7 @@ Mat ProjectedHistogram(Mat img, int t) for(int j=0; j(j)=countNonZero(data); //缁熻杩欎竴琛屾垨涓鍒椾腑锛岄潪闆跺厓绱犵殑涓暟锛屽苟淇濆瓨鍒癿hist涓 + mhist.at(j)=countNonZero(data); //统计这一行或一列中,非零元素的个数,并保存到mhist中 } //Normalize histogram @@ -47,13 +47,13 @@ Mat ProjectedHistogram(Mat img, int t) minMaxLoc(mhist, &min, &max); if(max>0) - mhist.convertTo(mhist,-1 , 1.0f/max, 0);//鐢╩hist鐩存柟鍥句腑鐨勬渶澶у硷紝褰掍竴鍖栫洿鏂瑰浘 + mhist.convertTo(mhist,-1 , 1.0f/max, 0);//用mhist直方图中的最大值,归一化直方图 return mhist; } -//! 鑾峰緱杞︾墝鐨勭壒寰佹暟 +//! 获得车牌的特征数 Mat getTheFeatures(Mat in) { const int VERTICAL = 0; @@ -68,7 +68,7 @@ Mat getTheFeatures(Mat in) Mat out = Mat::zeros(1, numCols, CV_32F); - //Asign values to feature,鏍锋湰鐗瑰緛涓烘按骞炽佸瀭鐩寸洿鏂瑰浘 + //Asign values to feature,样本特征为水平、垂直直方图 int j=0; for(int i=0; i& resultVec) { - //鍙兘鏄溅鐗岀殑鍥惧潡闆嗗悎 + //可能是车牌的图块集合 vector matVec; int resultLo = m_plateLocate->plateLocate(src, matVec); diff --git a/src/core/plate_judge.cpp b/src/core/plate_judge.cpp index 30a7ba6..1d27929 100644 --- a/src/core/plate_judge.cpp +++ b/src/core/plate_judge.cpp @@ -27,7 +27,7 @@ void CPlateJudge::LoadModel(string s) svm.load(s.c_str(), "svm"); } -//! 鐩存柟鍥惧潎琛 +//! 直方图均衡 Mat CPlateJudge::histeq(Mat in) { Mat out(in.size(), in.type()); @@ -49,7 +49,7 @@ Mat CPlateJudge::histeq(Mat in) } -//! 瀵瑰崟骞呭浘鍍忚繘琛孲VM鍒ゆ柇 +//! 对单幅图像进行SVM判断 int CPlateJudge::plateJudge(const Mat& inMat,int& result) { if (m_getFeatures == NULL) @@ -58,7 +58,7 @@ int CPlateJudge::plateJudge(const Mat& inMat,int& result) Mat features; m_getFeatures(inMat, features); - //閫氳繃鐩存柟鍥惧潎琛″寲鍚庣殑褰╄壊鍥捐繘琛岄娴 + //通过直方图均衡化后的彩色图进行预测 Mat p = features.reshape(1, 1); p.convertTo(p, CV_32FC1); @@ -69,7 +69,7 @@ int CPlateJudge::plateJudge(const Mat& inMat,int& result) } -//! 瀵瑰骞呭浘鍍忚繘琛孲VM鍒ゆ柇 +//! 对多幅图像进行SVM判断 int CPlateJudge::plateJudge(const vector& inVec, vector& resultVec) { diff --git a/src/core/plate_locate.cpp b/src/core/plate_locate.cpp index 17e1dd3..64fbba6 100644 --- a/src/core/plate_locate.cpp +++ b/src/core/plate_locate.cpp @@ -25,8 +25,8 @@ CPlateLocate::CPlateLocate() m_debug = DEFAULT_DEBUG; } -//! 鐢熸椿妯″紡涓庡伐涓氭ā寮忓垏鎹 -//! 濡傛灉涓虹湡锛屽垯璁剧疆鍚勯」鍙傛暟涓哄畾浣嶇敓娲诲満鏅収鐗囷紙濡傜櫨搴﹀浘鐗囷級鐨勫弬鏁帮紝鍚﹀垯鎭㈠榛樿鍊笺 +//! 生活模式与工业模式切换 +//! 如果为真,则设置各项参数为定位生活场景照片(如百度图片)的参数,否则恢复默认值。 void CPlateLocate::setLifemode(bool param) { if(param == true) @@ -52,12 +52,12 @@ void CPlateLocate::setLifemode(bool param) } -//! 瀵筸inAreaRect鑾峰緱鐨勬渶灏忓鎺ョ煩褰紝鐢ㄧ旱妯瘮杩涜鍒ゆ柇 +//! 对minAreaRect获得的最小外接矩形,用纵横比进行判断 bool CPlateLocate::verifySizes(RotatedRect mr) { float error = m_error; //Spain car plate size: 52x11 aspect 4,7272 - //China car plate size: 440mm*140mm锛宎spect 3.142857 + //China car plate size: 440mm*140mm,aspect 3.142857 float aspect = m_aspect; //Set a min and max area. All other patchs are discarded //int min= 1*aspect*1; // minimum area @@ -85,7 +85,7 @@ bool CPlateLocate::verifySizes(RotatedRect mr) } } -//! 鏄剧ず鏈缁堢敓鎴愮殑杞︾墝鍥惧儚锛屼究浜庡垽鏂槸鍚︽垚鍔熻繘琛屼簡鏃嬭浆銆 +//! 显示最终生成的车牌图像,便于判断是否成功进行了旋转。 Mat CPlateLocate::showResultMat(Mat src, Size rect_size, Point2f center, int index) { Mat img_crop; @@ -114,10 +114,10 @@ Mat CPlateLocate::showResultMat(Mat src, Size rect_size, Point2f center, int ind return resultResized; } -//! 瀹氫綅杞︾墝鍥惧儚 -//! src 鍘熷鍥惧儚 -//! resultVec 涓涓狹at鐨勫悜閲忥紝瀛樺偍鎵鏈夋姄鍙栧埌鐨勫浘鍍 -//! 鎴愬姛杩斿洖0锛屽惁鍒欒繑鍥-1 +//! 定位车牌图像 +//! src 原始图像 +//! resultVec 一个Mat的向量,存储所有抓取到的图像 +//! 成功返回0,否则返回-1 int CPlateLocate::plateLocate(Mat src, vector& resultVec) { Mat src_blur, src_gray; @@ -130,7 +130,7 @@ int CPlateLocate::plateLocate(Mat src, vector& resultVec) if( !src.data ) { return -1; } - //楂樻柉妯$硦銆係ize涓殑鏁板瓧褰卞搷杞︾墝瀹氫綅鐨勬晥鏋溿 + //高斯模糊。Size中的数字影响车牌定位的效果。 GaussianBlur( src, src_blur, Size(m_GaussianBlurSize, m_GaussianBlurSize), 0, 0, BORDER_DEFAULT ); @@ -200,11 +200,11 @@ int CPlateLocate::plateLocate(Mat src, vector& resultVec) imwrite(ss.str(), img_threshold); } - //Find 杞粨 of possibles plates + //Find 轮廓 of possibles plates vector< vector< Point> > contours; findContours(img_threshold, contours, // a vector of contours - CV_RETR_EXTERNAL, // 鎻愬彇澶栭儴杞粨 + CV_RETR_EXTERNAL, // 提取外部轮廓 CV_CHAIN_APPROX_NONE); // all pixels of each contours Mat result; @@ -253,10 +253,10 @@ int CPlateLocate::plateLocate(Mat src, vector& resultVec) { // rotated rectangle drawing // Get rotation matrix - // 鏃嬭浆杩欓儴鍒嗕唬鐮佺‘瀹炲彲浠ュ皢鏌愪簺鍊炬枩鐨勮溅鐗岃皟鏁存锛 - // 浣嗘槸瀹冧篃浼氳灏嗘洿澶氭鐨勮溅鐗屾悶鎴愬炬枩锛佹墍浠ョ患鍚堣冭檻锛岃繕鏄笉浣跨敤杩欐浠g爜銆 - // 2014-08-14,鐢变簬鏂板埌鐨勪竴鎵瑰浘鐗囦腑鍙戠幇鏈夊緢澶氳溅鐗屾槸鍊炬枩鐨勶紝鍥犳鍐冲畾鍐嶆灏濊瘯 - // 杩欐浠g爜銆 + // 旋转这部分代码确实可以将某些倾斜的车牌调整正, + // 但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。 + // 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试 + // 这段代码。 if(m_debug) { Point2f rect_points[4]; @@ -273,7 +273,7 @@ int CPlateLocate::plateLocate(Mat src, vector& resultVec) angle = 90 + angle; swap(rect_size.width, rect_size.height); } - //濡傛灉鎶撳彇鐨勬柟鍧楁棆杞秴杩噈_angle瑙掑害锛屽垯涓嶆槸杞︾墝锛屾斁寮冨鐞 + //如果抓取的方块旋转超过m_angle角度,则不是车牌,放弃处理 if (angle - m_angle < 0 && angle + m_angle > 0) { //Create and rotate image diff --git a/src/core/plate_recognize.cpp b/src/core/plate_recognize.cpp index ab41411..0a614bf 100644 --- a/src/core/plate_recognize.cpp +++ b/src/core/plate_recognize.cpp @@ -12,13 +12,13 @@ CPlateRecognize::CPlateRecognize() //m_charsRecognise = new CCharsRecognise(); } -////! 瑁呰浇SVM妯″瀷 +////! 装载SVM模型 //void CPlateRecognize::LoadSVM(string strSVM) //{ // m_plateDetect->LoadModel(strSVM.c_str()); //} // -////! 瑁呰浇ANN妯″瀷 +////! 装载ANN模型 //void CPlateRecognize::LoadANN(string strANN) //{ // m_charsRecognise->LoadModel(strANN.c_str()); @@ -38,7 +38,7 @@ CPlateRecognize::CPlateRecognize() int CPlateRecognize::plateRecognize(Mat src, vector& licenseVec) { - //杞︾墝鏂瑰潡闆嗗悎 + //车牌方块集合 vector plateVec; int resultPD = plateDetect(src, plateVec); @@ -51,10 +51,10 @@ int CPlateRecognize::plateRecognize(Mat src, vector& licenseVec) { Mat plate = plateVec[j]; - //鑾峰彇杞︾墝棰滆壊 + //获取车牌颜色 string plateType = getPlateType(plate); - //鑾峰彇杞︾墝鍙 + //获取车牌号 string plateIdentify = ""; int resultCR = charsRecognise(plate, plateIdentify); if (resultCR == 0) diff --git a/src/include/chars_identify.h b/src/include/chars_identify.h index d08015c..3657418 100644 --- a/src/include/chars_identify.h +++ b/src/include/chars_identify.h @@ -24,39 +24,39 @@ class CCharsIdentify public: CCharsIdentify(); - //! 瀛楃鍒嗗壊 + //! 字符分割 string charsIdentify(Mat, bool); - //! 瀛楃鍒嗙被 + //! 字符分类 int classify(Mat, bool); - //create the accumulation histograms,img is a binary image, t is 姘村钩鎴栧瀭鐩 + //create the accumulation histograms,img is a binary image, t is 水平或垂直 Mat ProjectedHistogram(Mat img, int t); - //! 鑾峰緱瀛楃鐨勭壒寰佸浘 + //! 获得字符的特征图 Mat features(Mat in, int sizeData); - //! 瑁呰浇ANN妯″瀷 + //! 装载ANN模型 void LoadModel(); - //! 瑁呰浇ANN妯″瀷 + //! 装载ANN模型 void LoadModel(string s); - //! 璁剧疆涓庤鍙栨ā鍨嬭矾寰 + //! 设置与读取模型路径 inline void setModelPath(string path){ m_path = path; } inline string getModelPath() const{ return m_path; } private: - //锛佷娇鐢ㄧ殑ANN妯″瀷 + //!使用的ANN模型 CvANN_MLP ann; - //! 妯″瀷瀛樺偍璺緞 + //! 模型存储路径 string m_path; - //! 鐗瑰緛灏哄 + //! 特征尺寸 int m_predictSize; - //! 鐪佷唤瀵瑰簲map + //! 省份对应map map m_map; }; diff --git a/src/include/chars_recognise.h b/src/include/chars_recognise.h index 6443d93..19bddb3 100644 --- a/src/include/chars_recognise.h +++ b/src/include/chars_recognise.h @@ -27,32 +27,32 @@ class CCharsRecognise public: CCharsRecognise(); - //! 瀛楃鍒嗗壊涓庤瘑鍒 + //! 字符分割与识别 int charsRecognise(Mat, String&); - //! 瑁呰浇ANN妯″瀷 + //! 装载ANN模型 void LoadANN(string s); - //! 鏄惁寮鍚皟璇曟ā寮 + //! 是否开启调试模式 inline void setCRDebug(int param){ m_charsSegment->setDebug(param);} - //! 鑾峰彇璋冭瘯妯″紡鐘舵 + //! 获取调试模式状态 inline int getCRDebug(){ return m_charsSegment->getDebug();} - //! 鑾峰緱杞︾墝棰滆壊 + //! 获得车牌颜色 inline string getPlateType(Mat input) const { - string color = "鏈煡"; + string color = "未知"; int result = m_charsSegment->getPlateType(input); if (PlateColor::Blue == result) - color = "钃濈墝"; + color = "蓝牌"; if (PlateColor::Yellow == result) - color = "榛勭墝"; + color = "黄牌"; return color; } - //! 璁剧疆鍙橀噺 + //! 设置变量 inline void setLiuDingSize(int param){ m_charsSegment->setLiuDingSize(param);} inline void setColorThreshold(int param){ m_charsSegment->setColorThreshold(param);} inline void setBluePercent(float param){ m_charsSegment->setBluePercent(param);} @@ -61,10 +61,10 @@ public: inline float getWhitePercent() const { return m_charsSegment->getWhitePercent();} private: - //锛佸瓧绗﹀垎鍓 + //!字符分割 CCharsSegment* m_charsSegment; - //! 瀛楃璇嗗埆 + //! 字符识别 CCharsIdentify* m_charsIdentify; }; diff --git a/src/include/chars_segment.h b/src/include/chars_segment.h index 4fd896f..77ff7e9 100644 --- a/src/include/chars_segment.h +++ b/src/include/chars_segment.h @@ -30,42 +30,42 @@ class CCharsSegment public: CCharsSegment(); - //! 瀛楃鍒嗗壊 + //! 字符分割 int charsSegment(Mat, vector& ); - //! 瀛楃灏哄楠岃瘉 + //! 字符尺寸验证 bool verifySizes(Mat r); - //! 瀛楃棰勫鐞 + //! 字符预处理 Mat preprocessChar(Mat in); - //! 鐢熸垚鐩存柟鍥 + //! 生成直方图 Mat ProjectedHistogram(Mat img, int t); - //! 鐢熸垚瀛楃鐨勭壒瀹氱壒寰 + //! 生成字符的特定特征 Mat features(Mat in, int sizeData); - //! 鐩存柟鍥惧潎琛★紝涓哄垽鏂溅鐗岄鑹插仛鍑嗗 + //! 直方图均衡,为判断车牌颜色做准备 Mat histeq(Mat in); - //! 鑾峰緱杞︾墝棰滆壊 + //! 获得车牌颜色 PlateColor getPlateType(Mat input); - //! 鍘婚櫎褰卞搷瀛楃璇嗗埆鐨勬煶閽 + //! 去除影响字符识别的柳钉 void clearLiuDing(Mat &img); - //! 鏍规嵁鐗规畩杞︾墝鏉ユ瀯閫犵寽娴嬩腑鏂囧瓧绗︾殑浣嶇疆鍜屽ぇ灏 + //! 根据特殊车牌来构造猜测中文字符的位置和大小 Rect GetChineseRect(const Rect rectSpe); - //! 鎵惧嚭鎸囩ず鍩庡競鐨勫瓧绗︾殑Rect锛屼緥濡傝嫃A7003X锛屽氨鏄疉鐨勪綅缃 + //! 找出指示城市的字符的Rect,例如苏A7003X,就是A的位置 int GetSpecificRect(const vector& vecRect); - //! 杩欎釜鍑芥暟鍋氫袱涓簨鎯 - // 1.鎶婄壒娈婂瓧绗ect宸﹁竟鐨勫叏閮≧ect鍘绘帀锛屽悗闈㈠啀閲嶅缓涓枃瀛楃鐨勪綅缃 - // 2.浠庣壒娈婂瓧绗ect寮濮嬶紝渚濇閫夋嫨6涓猂ect锛屽浣欑殑鑸嶅幓銆 + //! 这个函数做两个事情 + // 1.把特殊字符Rect左边的全部Rect去掉,后面再重建中文字符的位置。 + // 2.从特殊字符Rect开始,依次选择6个Rect,多余的舍去。 int RebuildRect(const vector& vecRect, vector& outRect, int specIndex); - //! 璁剧疆鍙橀噺 + //! 设置变量 inline void setLiuDingSize(int param){ m_LiuDingSize = param;} inline void setColorThreshold(int param){ m_ColorThreshold = param;} @@ -74,38 +74,38 @@ public: inline void setWhitePercent(float param){ m_WhitePercent = param;} inline float getWhitePercent() const { return m_WhitePercent;} - //! 鏄惁寮鍚皟璇曟ā寮忓父閲忥紝榛樿0浠h〃鍏抽棴 + //! 是否开启调试模式常量,默认0代表关闭 static const int DEFAULT_DEBUG = 0; - //! 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; }; diff --git a/src/include/features.h b/src/include/features.h index 4346f8e..976173a 100644 --- a/src/include/features.h +++ b/src/include/features.h @@ -6,31 +6,31 @@ */ namespace easypr { -//! 鐩存柟鍥惧潎琛 +//! 直方图均衡 Mat histeq(Mat in); -// 锛佽幏鍙栧瀭鐩村拰姘村钩鏂瑰悜鐩存柟鍥 +// !获取垂直和水平方向直方图 Mat ProjectedHistogram(Mat img, int t); -//! 鑾峰緱杞︾墝鐨勭壒寰佹暟 +//! 获得车牌的特征数 Mat getTheFeatures(Mat in); -// ! EasyPR鐨刧etFeatures鍥炶皟鍑芥暟 -// 锛佺敤浜庝粠杞︾墝鐨刬mage鐢熸垚svm鐨勮缁冪壒寰乫eatures +// ! EasyPR的getFeatures回调函数 +// !用于从车牌的image生成svm的训练特征features typedef void(*svmCallback)(const Mat& image, Mat& features); -// ! EasyPR鐨刧etFeatures鍥炶皟鍑芥暟 -// 锛佹湰鍑芥暟鏄敓鎴愮洿鏂瑰浘鍧囪 鐗瑰緛鐨勫洖璋冨嚱鏁 +// ! EasyPR的getFeatures回调函数 +// !本函数是生成直方图均衡特征的回调函数 void getHisteqFeatures(const Mat& image, Mat& features); -// ! EasyPR鐨刧etFeatures鍥炶皟鍑芥暟 -// 锛佹湰鍑芥暟鏄幏鍙栧瀭鐩村拰姘村钩鐨勭洿鏂瑰浘鍥惧 +// ! EasyPR的getFeatures回调函数 +// !本函数是获取垂直和水平的直方图图值 void getHistogramFeatures(const Mat& image, Mat& features); -// 锛佹湰鍑芥暟鏄幏鍙朣IFT鐗瑰緛瀛 +// !本函数是获取SIFT特征子 void getSIFTFeatures(const Mat& image, Mat& features); -// 锛佹湰鍑芥暟鏄幏鍙朒OG鐗瑰緛瀛 +// !本函数是获取HOG特征子 void getHOGFeatures(const Mat& image, Mat& features); diff --git a/src/include/plate_detect.h b/src/include/plate_detect.h index 12e68b8..abe497f 100644 --- a/src/include/plate_detect.h +++ b/src/include/plate_detect.h @@ -27,22 +27,22 @@ class CPlateDetect public: CPlateDetect(); - //! 杞︾墝妫娴嬶細杞︾墝瀹氫綅涓庡垽鏂 + //! 车牌检测:车牌定位与判断 int plateDetect(Mat, vector&); - //! 瑁呰浇SVM妯″瀷 + //! 装载SVM模型 void LoadSVM(string s); - //! 鐢熸椿妯″紡涓庡伐涓氭ā寮忓垏鎹 + //! 生活模式与工业模式切换 inline void setPDLifemode(bool param){m_plateLocate->setLifemode(param);} - //! 鏄惁寮鍚皟璇曟ā寮 + //! 是否开启调试模式 inline void setPDDebug(int param){ m_plateLocate->setDebug(param);} - //! 鑾峰彇璋冭瘯妯″紡鐘舵 + //! 获取调试模式状态 inline int getPDDebug(){ return m_plateLocate->getDebug();} - //! 璁剧疆涓庤鍙栧彉閲 + //! 设置与读取变量 inline void setGaussianBlurSize(int param){ m_plateLocate->setGaussianBlurSize(param);} inline int getGaussianBlurSize() const{ return m_plateLocate->getGaussianBlurSize();} @@ -63,10 +63,10 @@ public: inline void setJudgeAngle(int param){m_plateLocate->setJudgeAngle(param);} private: - //锛佽溅鐗屽畾浣 + //!车牌定位 CPlateLocate* m_plateLocate; - //! 杞︾墝鍒ゆ柇 + //! 车牌判断 CPlateJudge* m_plateJudge; }; diff --git a/src/include/plate_judge.h b/src/include/plate_judge.h index 4c50093..388a98a 100644 --- a/src/include/plate_judge.h +++ b/src/include/plate_judge.h @@ -25,34 +25,34 @@ class CPlateJudge public: CPlateJudge(); - //! 杞︾墝鍒ゆ柇 + //! 车牌判断 int plateJudge(const vector&, vector&); - //! 杞︾墝鍒ゆ柇锛堜竴鍓浘鍍忥級 + //! 车牌判断(一副图像) int plateJudge(const Mat& inMat, int& result); - //! 鐩存柟鍥惧潎琛 + //! 直方图均衡 Mat histeq(Mat); - //! 瑁呰浇SVM妯″瀷 + //! 装载SVM模型 void LoadModel(); - //! 瑁呰浇SVM妯″瀷 + //! 装载SVM模型 void LoadModel(string s); - //! 璁剧疆涓庤鍙栨ā鍨嬭矾寰 + //! 设置与读取模型路径 inline void setModelPath(string path){ m_path = path; } inline string getModelPath() const{ return m_path; } private: - //锛佷娇鐢ㄧ殑SVM妯″瀷 + //!使用的SVM模型 CvSVM svm; - // ! EasyPR鐨刧etFeatures鍥炶皟鍑芥暟 - // 锛佺敤浜庝粠杞︾墝鐨刬mage鐢熸垚svm鐨勮缁冪壒寰乫eatures + // ! EasyPR的getFeatures回调函数 + // !用于从车牌的image生成svm的训练特征features svmCallback m_getFeatures; - //! 妯″瀷瀛樺偍璺緞 + //! 模型存储路径 string m_path; }; diff --git a/src/include/plate_locate.h b/src/include/plate_locate.h index 48240ab..61d94bc 100644 --- a/src/include/plate_locate.h +++ b/src/include/plate_locate.h @@ -25,19 +25,19 @@ class CPlateLocate public: CPlateLocate(); - //! 杞︾墝瀹氫綅 + //! 车牌定位 int plateLocate(Mat, vector& ); - //! 杞︾墝鐨勫昂瀵搁獙璇 + //! 车牌的尺寸验证 bool verifySizes(RotatedRect mr); - //! 缁撴灉杞︾墝鏄剧ず + //! 结果车牌显示 Mat showResultMat(Mat src, Size rect_size, Point2f center, int index); - //! 鐢熸椿妯″紡涓庡伐涓氭ā寮忓垏鎹 + //! 生活模式与工业模式切换 void setLifemode(bool param); - //! 璁剧疆涓庤鍙栧彉閲 + //! 设置与读取变量 inline void setGaussianBlurSize(int param){ m_GaussianBlurSize = param;} inline int getGaussianBlurSize() const{ return m_GaussianBlurSize;} @@ -57,13 +57,13 @@ public: inline void setJudgeAngle(int param){ m_angle = param;} - //! 鏄惁寮鍚皟璇曟ā寮 + //! 是否开启调试模式 inline void setDebug(int param){ m_debug = param;} - //! 鑾峰彇璋冭瘯妯″紡鐘舵 + //! 获取调试模式状态 inline int 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; @@ -73,39 +73,39 @@ public: static const int DEFAULT_MORPH_SIZE_WIDTH = 17; static const int DEFAULT_MORPH_SIZE_HEIGHT = 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 = 3; static const int DEFAULT_VERIFY_MAX = 20; - //! 瑙掑害鍒ゆ柇鎵鐢ㄥ父閲 + //! 角度判断所用常量 static const int DEFAULT_ANGLE = 30; - //! 鏄惁寮鍚皟璇曟ā寮忓父閲忥紝榛樿0浠h〃鍏抽棴 + //! 是否开启调试模式常量,默认0代表关闭 static const int DEFAULT_DEBUG = 0; 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开启 int m_debug; }; diff --git a/src/include/plate_recognize.h b/src/include/plate_recognize.h index c799e46..a07a162 100644 --- a/src/include/plate_recognize.h +++ b/src/include/plate_recognize.h @@ -27,39 +27,39 @@ class CPlateRecognize : public CPlateDetect, public CCharsRecognise public: CPlateRecognize(); - //! 杞︾墝妫娴嬩笌瀛楃璇嗗埆 + //! 车牌检测与字符识别 int plateRecognize(Mat, vector&); - //! 鐢熸椿妯″紡涓庡伐涓氭ā寮忓垏鎹 + //! 生活模式与工业模式切换 inline void setLifemode(bool param) { CPlateDetect::setPDLifemode(param); } - //! 鏄惁寮鍚皟璇曟ā寮 + //! 是否开启调试模式 inline void setDebug(int param) { CPlateDetect::setPDDebug(param); CCharsRecognise::setCRDebug(param); } - ////! 杞︾墝瀹氫綅涓庡垽鏂 + ////! 车牌定位与判断 //int plateDetect(Mat, vector&); - ////! 瀛楃鍒嗗壊涓庤瘑鍒 + ////! 字符分割与识别 //int charsRecognise(Mat, String&); - ////! 瑁呰浇SVM + ////! 装载SVM //void LoadSVM(string s); - ////! 瑁呰浇ANN妯″瀷 + ////! 装载ANN模型 //void LoadANN(string s); private: - ////锛佽溅鐗屾娴 + ////!车牌检测 //CPlateDetect* m_plateDetect; - ////! 瀛楃璇嗗埆 + ////! 字符识别 //CCharsRecognise* m_charsRecognise; }; diff --git a/src/include/util.h b/src/include/util.h index 2ad069c..00b1e19 100644 --- a/src/include/util.h +++ b/src/include/util.h @@ -35,11 +35,11 @@ namespace easypr { } -//C++鐨勮幏鍙栨枃浠跺す鍑芥暟 +//C++的获取文件夹函数 void getFiles(std::string path, std::vector& files); -//! levenshtein璺濈锛岀敤浜庤绠椾袱涓溅鐗岀殑璺濈 -//锛丒asyPR涓敤levenshtein璺濈琛¢噺杞︾墝璇嗗埆涓庣湡瀹炶溅鐗岀殑璇樊 +//! levenshtein距离,用于计算两个车牌的距离 +//!EasyPR中用levenshtein距离衡量车牌识别与真实车牌的误差 template unsigned int levenshtein_distance(const T &s1, const T & s2) { const size_t len1 = s1.size(), len2 = s2.size(); @@ -58,32 +58,32 @@ unsigned int levenshtein_distance(const T &s1, const T & s2) { } -/// test.cpp涓柟娉 +/// test.cpp中方法 int testMain(); -/// accuracy_test.cpp涓柟娉 +/// accuracy_test.cpp中方法 int acurayTest(const std::string&); -/// mc_data_prepare.cpp涓柟娉 +/// mc_data_prepare.cpp中方法 void getLearnData(); void Code2Province(const std::string& code, std::string& province); void changeFileName(); void getPlateLicense(const std::string& filepath, std::string& plateLicense); -/// learn_prepare.cpp涓柟娉 +/// learn_prepare.cpp中方法 void label_data(); -/// general_test_prepare.cpp涓柟娉 +/// general_test_prepare.cpp中方法 int general_test(); -// deface.cpp涓柟娉 -// 鍙嶄汉鑴歌瘑鍒嚱鏁 -// 鍦ㄥ彂甯冩暟鎹埌general_test鏁版嵁闆嗛噷锛岃鍏堢敤杩欓噷鐨勬柟娉曞鍥惧儚杩涜澶勭悊 -// EasyPR寮婧愰」鐩潪甯告敞閲嶄繚鎶ゅ浘鐗囦腑椹鹃┒浜虹殑闅愮 +// deface.cpp中方法 +// 反人脸识别函数 +// 在发布数据到general_test数据集里,请先用这里的方法对图像进行处理 +// EasyPR开源项目非常注重保护图片中驾驶人的隐私 int deface(); -/// generate_gdts.cpp涓柟娉 +/// generate_gdts.cpp中方法 int generate_gdts(); #endif diff --git a/src/main.cpp b/src/main.cpp index b07b7ff..982a24e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,18 +20,18 @@ extern const string GENERAL_TEST_PATH = "image/general_test"; extern const string NATIVE_TEST_PATH = "image/native_test"; //////////////////////////////////////////////////////////// -// EasyPR 璁粌鍛戒护琛 +// EasyPR 训练命令行 const string option[] = { - "1. 娴嬭瘯;" , - "2. 鎵归噺娴嬭瘯;" , - "3. SVM璁粌;" , - "4. ANN璁粌(鏈紑鏀);" , - "5. GDTS鐢熸垚;" , - "6. 寮鍙戝洟闃;" , - "7. 鎰熻阿鍚嶅崟;" , - "8. 閫鍑;" , + "1. 测试;" , + "2. 批量测试;" , + "3. SVM训练;" , + "4. ANN训练(未开放);" , + "5. GDTS生成;" , + "6. 开发团队;" , + "7. 感谢名单;" , + "8. 退出;" , }; const int optionCount = 8; @@ -57,7 +57,7 @@ int main(int argc, char *argv[]) cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; - cout << "璇烽夋嫨涓椤规搷浣:"; + cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; @@ -84,28 +84,28 @@ int main(int argc, char *argv[]) generate_gdts(); break; case 6: - // 寮鍙戝洟闃; + // 开发团队; cout << endl; - cout << "鎴戜滑EasyPR鍥㈤槦鐩墠鏈変竴涓5浜哄乏鍙崇殑灏忕粍鍦ㄨ繘琛孍asyPR鍚庣画鐗堟湰鐨勫紑鍙戝伐浣溿" << endl; - cout << "濡傛灉浣犲鏈」鐩劅鍏磋叮锛屽苟涓旀効鎰忎负寮婧愯础鐚竴浠藉姏閲忥紝鎴戜滑寰堟杩庝綘鐨勫姞鍏ャ" << endl; - cout << "鐩墠鎷涜仒鐨勪富瑕佷汉鎵嶆槸锛氳溅鐗屽畾浣嶏紝鍥惧儚璇嗗埆锛屾繁搴﹀涔狅紝缃戠珯寤鸿鐩稿叧鏂归潰鐨勭墰浜恒" << endl; - cout << "濡傛灉浣犺寰楄嚜宸辩鍚堟潯浠讹紝璇峰彂閭欢鍒板湴鍧(easypr_dev@163.com)锛屾湡寰呬綘鐨勫姞鍏ワ紒" << endl; + cout << "我们EasyPR团队目前有一个5人左右的小组在进行EasyPR后续版本的开发工作。" << endl; + cout << "如果你对本项目感兴趣,并且愿意为开源贡献一份力量,我们很欢迎你的加入。" << endl; + cout << "目前招聘的主要人才是:车牌定位,图像识别,深度学习,网站建设相关方面的牛人。" << endl; + cout << "如果你觉得自己符合条件,请发邮件到地址(easypr_dev@163.com),期待你的加入!" << endl; cout << endl; break; case 7: - // 鎰熻阿鍚嶅崟 + // 感谢名单 cout << endl; - cout << "鏈」鐩湪寤鸿杩囩▼涓紝鍙楀埌浜嗗緢澶氫汉鐨勫府鍔╋紝鍏朵腑浠ヤ笅鏄鏈」鐩仛鍑虹獊鍑鸿础鐚殑" << endl; - cout << "(璐$尞鍖呮嫭鏈夌泭寤鸿锛屼唬鐮佽皟浼橈紝鏁版嵁鎻愪緵绛夌瓑,鎺掑悕鎸夋椂闂撮『搴)锛" << endl; - cout << "taotao1233锛屽攼澶т緺锛宩sxyhelu锛屽鏋滄湁涓澶(zhoushiwei)锛屽涔犲鏂楋紝琚佹壙蹇楋紝鍦e煄灏忕煶鍖狅紝goldriver锛孧icooz锛屾ⅵ閲屾椂鍏夛紝Rain Wang锛" << endl; - cout << "杩樻湁寰堝鐨勫悓瀛﹀鏈」鐩篃缁欎簣浜嗛紦鍔变笌鏀寔锛屽湪姝や篃涓骞惰〃绀虹湡璇氱殑璋㈡剰锛" << endl; + cout << "本项目在建设过程中,受到了很多人的帮助,其中以下是对本项目做出突出贡献的" << endl; + cout << "(贡献包括有益建议,代码调优,数据提供等等,排名按时间顺序):" << endl; + cout << "taotao1233,唐大侠,jsxyhelu,如果有一天(zhoushiwei),学习奋斗,袁承志,圣城小石匠,goldriver,Micooz,梦里时光,Rain Wang," << endl; + cout << "还有很多的同学对本项目也给予了鼓励与支持,在此也一并表示真诚的谢意!" << endl; cout << endl; break; case 8: isExit = true; break; default: - cout << "杈撳叆閿欒锛岃閲嶆柊杈撳叆:"; + cout << "输入错误,请重新输入:"; isRepeat = true; break; } @@ -113,18 +113,18 @@ int main(int argc, char *argv[]) } return 0; } -// /EasyPR 璁粌鍛戒护琛 缁撴潫 +// /EasyPR 训练命令行 结束 //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// -// acurayTestMain 鍛戒护琛 +// acurayTestMain 命令行 const string acuray_option[] = { "1. general_test;" , "2. native_test;" , - "3. 杩斿洖;" , + "3. 返回;" , }; const int acuray_optionCount = 3; @@ -144,7 +144,7 @@ int acurayTestMain() cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; - cout << "璇烽夋嫨涓椤规搷浣:"; + cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; @@ -164,7 +164,7 @@ int acurayTestMain() isExit = true; break; default: - cout << "杈撳叆閿欒锛岃閲嶆柊杈撳叆:"; + cout << "输入错误,请重新输入:"; isRepeat = true; break; } @@ -173,22 +173,22 @@ int acurayTestMain() return 0; } -// acurayTestMain 鍛戒护琛 +// acurayTestMain 命令行 //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// -// SVM 璁粌鍛戒护琛 +// SVM 训练命令行 const string svm_option[] = { - "1. 鐢熸垚learndata(璋冩暣浠g爜鍒颁綘鐨勭幆澧冨悗鍐嶇敤);" , - "2. 鏍囩learndata;" , - "3. 杞︾墝妫娴(not divide and train);" , - "4. 杞︾墝妫娴(not train);" , - "5. 杞︾墝妫娴(not divide);" , - "6. 杞︾墝妫娴;" , - "7. 杩斿洖;" , + "1. 生成learndata(调整代码到你的环境后再用);" , + "2. 标签learndata;" , + "3. 车牌检测(not divide and train);" , + "4. 车牌检测(not train);" , + "5. 车牌检测(not divide);" , + "6. 车牌检测;" , + "7. 返回;" , }; const int svm_optionCount = 7; @@ -208,7 +208,7 @@ int svmMain() cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; - cout << "璇烽夋嫨涓椤规搷浣:"; + cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; @@ -240,7 +240,7 @@ int svmMain() isExit = true; break; default: - cout << "杈撳叆閿欒锛岃閲嶆柊杈撳叆:"; + cout << "输入错误,请重新输入:"; isRepeat = true; break; } @@ -249,7 +249,7 @@ int svmMain() return 0; } -// SVM 璁粌鍛戒护琛 +// SVM 训练命令行 //////////////////////////////////////////////////////////// // command line option support @@ -269,29 +269,29 @@ void cmdMain(int argc, char *argv[]) { const char *help[] = { "EasyPR Usage: ", - "--help [ -h ] 鏄剧ず甯姪 ", - "娴嬭瘯妯″潡 ", - "--test_plate_locate [ -tpl ] 杞︾墝瀹氫綅 ", - "--test_plate_judge [ -tpj ] 杞︾墝鍒ゆ柇 ", - "--test_plate_detect [ -tpd ] 杞︾墝妫娴 ", - "--test_chars_segment [ -tcs ] 瀛楃鍒嗛殧 ", - "--test_chars_identify [ -tci ] 瀛楃閴村埆 ", - "--test_chars_recognize [ -tcr ] 瀛楃璇嗗埆 ", - "--test_plate_recognize [ -tpr ] 杞︾墝璇嗗埆 ", - "--test_all [ -ta ] 娴嬭瘯鍏ㄩ儴 ", - "--general_test [ -gt ] 鎵归噺娴嬭瘯-general_test ", - "--native_test [ -nt ] 鎵归噺娴嬭瘯-native_test ", - "SVM璁粌", - "--svm_gen_learndata [ -sgl ] 鐢熸垚Learndata ", - "--svm_tag_learndata [ -stl ] 鏍囩Learndata ", - "--svm_detect [ -sd ] 杞︾墝妫娴嬶紝鍙缃-v鎴-t ", - "--svm_divide [ -v ] 鍚敤鍒嗗壊 ", - "--svm_train [ -t ] 鍚敤璁粌 ", - "ANN璁粌锛堟湭寮鏀撅級 ", - "GDTS鐢熸垚 ", - "--gdts [ -gts ] GDTS鐢熸垚 ", - "--group 寮鍙戝洟闃 ", - "--thanks 鎰熻阿鍚嶅崟 ", + "--help [ -h ] 显示帮助 ", + "测试模块 ", + "--test_plate_locate [ -tpl ] 车牌定位 ", + "--test_plate_judge [ -tpj ] 车牌判断 ", + "--test_plate_detect [ -tpd ] 车牌检测 ", + "--test_chars_segment [ -tcs ] 字符分隔 ", + "--test_chars_identify [ -tci ] 字符鉴别 ", + "--test_chars_recognize [ -tcr ] 字符识别 ", + "--test_plate_recognize [ -tpr ] 车牌识别 ", + "--test_all [ -ta ] 测试全部 ", + "--general_test [ -gt ] 批量测试-general_test ", + "--native_test [ -nt ] 批量测试-native_test ", + "SVM训练", + "--svm_gen_learndata [ -sgl ] 生成Learndata ", + "--svm_tag_learndata [ -stl ] 标签Learndata ", + "--svm_detect [ -sd ] 车牌检测,可设置-v或-t ", + "--svm_divide [ -v ] 启用分割 ", + "--svm_train [ -t ] 启用训练 ", + "ANN训练(未开放) ", + "GDTS生成 ", + "--gdts [ -gts ] GDTS生成 ", + "--group 开发团队 ", + "--thanks 感谢名单 ", " ", "Examples: ", " $ ./EasyPR --test_plate_locate ", @@ -404,23 +404,23 @@ void cmdMain(int argc, char *argv[]) // if (parser.has("group")) { - // 寮鍙戝洟闃; + // 开发团队; cout << endl; - cout << "鎴戜滑EasyPR鍥㈤槦鐩墠鏈変竴涓5浜哄乏鍙崇殑灏忕粍鍦ㄨ繘琛孍asyPR鍚庣画鐗堟湰鐨勫紑鍙戝伐浣溿" << endl; - cout << "濡傛灉浣犲鏈」鐩劅鍏磋叮锛屽苟涓旀効鎰忎负寮婧愯础鐚竴浠藉姏閲忥紝鎴戜滑寰堟杩庝綘鐨勫姞鍏ャ" << endl; - cout << "鐩墠鎷涜仒鐨勪富瑕佷汉鎵嶆槸锛氳溅鐗屽畾浣嶏紝鍥惧儚璇嗗埆锛屾繁搴﹀涔狅紝缃戠珯寤鸿鐩稿叧鏂归潰鐨勭墰浜恒" << endl; - cout << "濡傛灉浣犺寰楄嚜宸辩鍚堟潯浠讹紝璇峰彂閭欢鍒板湴鍧(easypr_dev@163.com)锛屾湡寰呬綘鐨勫姞鍏ワ紒" << endl; + cout << "我们EasyPR团队目前有一个5人左右的小组在进行EasyPR后续版本的开发工作。" << endl; + cout << "如果你对本项目感兴趣,并且愿意为开源贡献一份力量,我们很欢迎你的加入。" << endl; + cout << "目前招聘的主要人才是:车牌定位,图像识别,深度学习,网站建设相关方面的牛人。" << endl; + cout << "如果你觉得自己符合条件,请发邮件到地址(easypr_dev@163.com),期待你的加入!" << endl; cout << endl; break; } if (parser.has("thanks")) { - // 鎰熻阿鍚嶅崟 + // 感谢名单 cout << endl; - cout << "鏈」鐩湪寤鸿杩囩▼涓紝鍙楀埌浜嗗緢澶氫汉鐨勫府鍔╋紝鍏朵腑浠ヤ笅鏄鏈」鐩仛鍑虹獊鍑鸿础鐚殑" << endl; - cout << "(璐$尞鍖呮嫭鏈夌泭寤鸿锛屼唬鐮佽皟浼橈紝鏁版嵁鎻愪緵绛夌瓑,鎺掑悕鎸夋椂闂撮『搴)锛" << endl; - cout << "taotao1233锛屽攼澶т緺锛宩sxyhelu锛屽鏋滄湁涓澶(zhoushiwei)锛屽涔犲鏂楋紝琚佹壙蹇楋紝鍦e煄灏忕煶鍖狅紝goldriver锛孧icooz锛屾ⅵ閲屾椂鍏夛紝Rain Wang锛" << endl; - cout << "杩樻湁寰堝鐨勫悓瀛﹀鏈」鐩篃缁欎簣浜嗛紦鍔变笌鏀寔锛屽湪姝や篃涓骞惰〃绀虹湡璇氱殑璋㈡剰锛" << endl; + cout << "本项目在建设过程中,受到了很多人的帮助,其中以下是对本项目做出突出贡献的" << endl; + cout << "(贡献包括有益建议,代码调优,数据提供等等,排名按时间顺序):" << endl; + cout << "taotao1233,唐大侠,jsxyhelu,如果有一天(zhoushiwei),学习奋斗,袁承志,圣城小石匠,goldriver,Micooz,梦里时光,Rain Wang," << endl; + cout << "还有很多的同学对本项目也给予了鼓励与支持,在此也一并表示真诚的谢意!" << endl; cout << endl; break; } diff --git a/src/test/accuracy_test.cpp b/src/test/accuracy_test.cpp index 60f573d..ae4a9aa 100644 --- a/src/test/accuracy_test.cpp +++ b/src/test/accuracy_test.cpp @@ -1,5 +1,5 @@ -// 閫氱敤姝g‘鐜囨祴璇曟枃浠 -// AcurayTest瀵瑰簲鍒癿ain鎺у埗鍛戒护涓殑閫夐」2 +// 通用正确率测试文件 +// AcurayTest对应到main控制命令中的选项2 #include "../include/plate_recognize.h" #include "../include/util.h" @@ -9,7 +9,7 @@ using namespace easypr; int acurayTest(const string& test_path) { - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(test_path, files); @@ -32,20 +32,20 @@ int acurayTest(const string& test_path) cout << "Begin to test the easypr accuracy!" << endl; - // 鎬荤殑娴嬭瘯鍥剧墖鏁伴噺 + // 总的测试图片数量 int count_all = 0; - // 閿欒鐨勫浘鐗囨暟閲 + // 错误的图片数量 int count_err = 0; - // 鏈瘑鍒殑鍥剧墖鏁伴噺 + // 未识别的图片数量 int count_norecogin = 0; - // 鎬荤殑瀛楃宸窛 + // 总的字符差距 float diff_all = 0; - // 骞冲潎瀛楃宸窛 + // 平均字符差距 float diff_avg = 0; - // 瀹屽叏鍖归厤鐨勮瘑鍒鏁 + // 完全匹配的识别次数 float match_count = 0; - // 瀹屽叏鍖归厤鐨勮瘑鍒鏁版墍鍗犺瘑鍒浘鐗囦腑鐨勬瘮渚 + // 完全匹配的识别次数所占识别图片中的比例 float match_rate = 0; for (int i = 0; i < size; i++) @@ -53,12 +53,12 @@ int acurayTest(const string& test_path) string filepath = files[i].c_str(); cout << "------------------" << endl; - // 鑾峰彇鐪熷疄鐨勮溅鐗 + // 获取真实的车牌 string plateLicense = Utils::getFileName(filepath); - cout << "鍘熺墝:" << plateLicense << endl; + cout << "原牌:" << plateLicense << endl; - // EasyPR寮濮嬪垽鏂溅鐗 + // EasyPR开始判断车牌 Mat src = imread(filepath); vector plateVec; int result = pr.plateRecognize(src, plateVec); @@ -68,20 +68,20 @@ int acurayTest(const string& test_path) if (num == 0) { - cout << ""<< "鏃犺溅鐗" < 1) { - // 澶氳溅鐗屼娇鐢╠iff鏈灏忕殑閭d釜璁板綍 + // 多车牌使用diff最小的那个记录 int mindiff = 10000; for (int j = 0; j < num; j++) { cout << plateVec[j] << " (" << j+1 << ")"< spilt_plate = Utils::splitString(colorplate, ':'); int size = spilt_plate.size(); @@ -93,34 +93,34 @@ int acurayTest(const string& test_path) } } - cout << "宸窛:" << mindiff << "涓瓧绗" << endl; + cout << "差距:" << mindiff << "个字符" << endl; if(mindiff == 0) { - // 瀹屽叏鍖归厤 + // 完全匹配 match_count++; } diff_all = diff_all + mindiff; } else { - // 鍗曡溅鐗屽彧璁$畻涓娆iff + // 单车牌只计算一次diff for (int j = 0; j < num; j++) { cout << plateVec[j] < spilt_plate = Utils::splitString(colorplate, ':'); int size = spilt_plate.size(); if (size == 2) { int diff = levenshtein_distance(plateLicense, spilt_plate[size-1]); - cout << "宸窛:" << diff << "涓瓧绗" << endl; + cout << "差距:" << diff << "个字符" << endl; if(diff == 0) { - // 瀹屽叏鍖归厤 + // 完全匹配 match_count++; } diff_all = diff_all + diff; @@ -131,7 +131,7 @@ int acurayTest(const string& test_path) } else { - cout << "閿欒鐮:" << result << endl; + cout << "错误码:" << result << endl; count_err++; } count_all++; @@ -141,21 +141,21 @@ int acurayTest(const string& test_path) cout << "Easypr accuracy test end!" << endl; cout << "------------------" << endl; cout << endl; - cout << "缁熻鍙傛暟:" << endl; - cout << "鎬诲浘鐗囨暟:" << count_all << "寮, "; - cout << "鏈瘑鍑哄浘鐗:" << count_norecogin << "寮, "; + cout << "统计参数:" << endl; + cout << "总图片数:" << count_all << "张, "; + cout << "未识出图片:" << count_norecogin << "张, "; float count_recogin = count_all - (count_err + count_norecogin); float count_rate = count_recogin / count_all; float count_norate = 1 - count_rate; - cout << "璇嗗嚭鐜:" << count_rate * 100 << "% " << endl; + cout << "识出率:" << count_rate * 100 << "% " << endl; diff_avg = diff_all / count_recogin; match_rate = match_count/ count_recogin * 100; - cout << "骞冲潎瀛楃宸窛:" << diff_avg << "涓, "; - cout << "瀹屽叏鍖归厤鏁:" << match_count << "寮, "; - cout << "瀹屽叏鍖归厤鐜:" << match_rate << "% " << endl; + cout << "平均字符差距:" << diff_avg << "个, "; + cout << "完全匹配数:" << match_count << "张, "; + cout << "完全匹配率:" << match_rate << "% " << endl; cout << endl; cout << "------------------" << endl; diff --git a/src/test/test.cpp b/src/test/test.cpp index a31a833..e135837 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -20,21 +20,21 @@ int test_plate_recognize(); int testMain(); -//鎶婁綘瑕佹祴璇曠殑鍥剧墖鍦板潃鍐欏湪涓嬮潰 +//把你要测试的图片地址写在下面 const string test_img = ""; const string testOption[] = { - "1. test plate_locate(杞︾墝瀹氫綅);" /* 杞︾墝瀹氫綅 */, - "2. test plate_judge(杞︾墝鍒ゆ柇);" /* 杞︾墝鍒ゆ柇 */, - "3. test plate_detect(杞︾墝妫娴);" /* 杞︾墝妫娴嬶紙鍖呭惈杞︾墝瀹氫綅涓庤溅鐗屽垽鏂級 */, - "4. test chars_segment(瀛楃鍒嗛殧);" /* 瀛楃鍒嗛殧 */, - "5. test chars_identify(瀛楃閴村埆);" /* 瀛楃閴村埆 */, - "6. test chars_recognise(瀛楃璇嗗埆);" /* 瀛楃璇嗗埆锛堝寘鍚瓧绗﹀垎闅斾笌瀛楃閴村埆锛 */, - "7. test plate_recognize(杞︾墝璇嗗埆);" /* 杞︾墝璇嗗埆 */, - "8. test all(娴嬭瘯鍏ㄩ儴);" /* 浠ヤ笂鍏ㄩ儴 */, - "9. 杩斿洖;" /* 閫鍑 */, + "1. test plate_locate(车牌定位);" /* 车牌定位 */, + "2. test plate_judge(车牌判断);" /* 车牌判断 */, + "3. test plate_detect(车牌检测);" /* 车牌检测(包含车牌定位与车牌判断) */, + "4. test chars_segment(字符分隔);" /* 字符分隔 */, + "5. test chars_identify(字符鉴别);" /* 字符鉴别 */, + "6. test chars_recognise(字符识别);" /* 字符识别(包含字符分隔与字符鉴别) */, + "7. test plate_recognize(车牌识别);" /* 车牌识别 */, + "8. test all(测试全部);" /* 以上全部 */, + "9. 返回;" /* 退出 */, }; const int testOptionCount = 9; @@ -54,7 +54,7 @@ int testMain() cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; - cout << "璇烽夋嫨涓椤规搷浣:"; + cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; @@ -100,7 +100,7 @@ int testMain() isExit = true; break; default: - cout << "杈撳叆閿欒锛岃閲嶆柊杈撳叆:"; + cout << "输入错误,请重新输入:"; isRepeat = true; break; } @@ -144,10 +144,10 @@ int test_plate_judge() Mat src = imread("image/plate_judge.jpg"); - //鍙兘鏄溅鐗岀殑鍥惧潡闆嗗悎 + //可能是车牌的图块集合 vector matVec; - //缁忚繃SVM鍒ゆ柇鍚庡緱鍒扮殑鍥惧潡闆嗗悎 + //经过SVM判断后得到的图块集合 vector resultVec; CPlateLocate lo; @@ -240,7 +240,7 @@ int test_chars_identify() Mat resultMat = resultVec[j]; bool isChinses = false; - //榛樿棣栦釜瀛楃鍧楁槸涓枃瀛楃 + //默认首个字符块是中文字符 if (j == 0) isChinses = true; @@ -249,7 +249,7 @@ int test_chars_identify() } } - const string plateLicense = "鑻廍771H6"; + const string plateLicense = "苏E771H6"; cout << "plateLicense: " << plateLicense << endl; cout << "plateIdentify: " << plateIdentify << endl; diff --git a/src/train/ann_train.cpp b/src/train/ann_train.cpp index 690f8bc..01d0e76 100644 --- a/src/train/ann_train.cpp +++ b/src/train/ann_train.cpp @@ -1,4 +1,4 @@ -// ann_train.cpp : ann妯″瀷鐨勮缁冩枃浠讹紝涓昏鐢ㄥ湪OCR涓 +// ann_train.cpp : ann模型的训练文件,主要用在OCR中 #include #include @@ -33,21 +33,21 @@ using namespace easypr; CvANN_MLP ann; -//涓浗杞︾墝 +//中国车牌 const char strCharacters[] = {'0','1','2','3','4','5',\ - '6','7','8','9','A','B', 'C', 'D', 'E','F', 'G', 'H', /* 娌℃湁I */\ - 'J', 'K', 'L', 'M', 'N', /* 娌℃湁O */ 'P', 'Q', 'R', 'S', 'T', \ + '6','7','8','9','A','B', 'C', 'D', 'E','F', 'G', 'H', /* 没有I */\ + 'J', 'K', 'L', 'M', 'N', /* 没有O */ 'P', 'Q', 'R', 'S', 'T', \ 'U','V', 'W', 'X', 'Y', 'Z'}; -const int numCharacter = 34; /* 娌℃湁I鍜孫,10涓暟瀛椾笌24涓嫳鏂囧瓧绗︿箣鍜 */ +const int numCharacter = 34; /* 没有I和O,10个数字与24个英文字符之和 */ -//浠ヤ笅閮芥槸鎴戣缁冩椂鐢ㄥ埌鐨勪腑鏂囧瓧绗︽暟鎹紝骞朵笉鍏ㄩ潰锛屾湁浜涚渷浠芥病鏈夎缁冩暟鎹墍浠ユ病鏈夊瓧绗 -//鏈変簺鍚庨潰鍔犳暟瀛2鐨勮〃绀哄湪璁粌鏃跺父鐪嬪埌瀛楃鐨勪竴绉嶅彉褰紝涔熶綔涓鸿缁冩暟鎹瓨鍌 -const string strChinese[] = {"zh_cuan" /* 宸 */, "zh_e" /* 閯 */, "zh_gan" /* 璧*/, \ - "zh_hei" /* 榛 */, "zh_hu" /* 娌 */, "zh_ji" /* 鍐 */, \ - "zh_jl" /* 鍚 */, "zh_jin" /* 娲 */, "zh_jing" /* 浜 */, "zh_shan" /* 闄 */, \ - "zh_liao" /* 杈 */, "zh_lu" /* 椴 */, "zh_min" /* 闂 */, "zh_ning" /* 瀹 */, \ - "zh_su" /* 鑻 */, "zh_sx" /* 鏅 */, "zh_wan" /* 鐨 */,\ - "zh_yu" /* 璞 */, "zh_yue" /* 绮 */, "zh_zhe" /* 娴 */}; +//以下都是我训练时用到的中文字符数据,并不全面,有些省份没有训练数据所以没有字符 +//有些后面加数字2的表示在训练时常看到字符的一种变形,也作为训练数据存储 +const string strChinese[] = {"zh_cuan" /* 川 */, "zh_e" /* 鄂 */, "zh_gan" /* 赣*/, \ + "zh_hei" /* 黑 */, "zh_hu" /* 沪 */, "zh_ji" /* 冀 */, \ + "zh_jl" /* 吉 */, "zh_jin" /* 津 */, "zh_jing" /* 京 */, "zh_shan" /* 陕 */, \ + "zh_liao" /* 辽 */, "zh_lu" /* 鲁 */, "zh_min" /* 闽 */, "zh_ning" /* 宁 */, \ + "zh_su" /* 苏 */, "zh_sx" /* 晋 */, "zh_wan" /* 皖 */,\ + "zh_yu" /* 豫 */, "zh_yue" /* 粤 */, "zh_zhe" /* 浙 */}; const int numChinese = 20; const int numAll = 54; /* 34+20=54 */ @@ -68,7 +68,7 @@ Mat features(Mat in, int sizeData){ int numCols=vhist.cols+hhist.cols+lowData.cols*lowData.cols; Mat out=Mat::zeros(1,numCols,CV_32F); - //Asign values to feature,ANN鐨勬牱鏈壒寰佷负姘村钩銆佸瀭鐩寸洿鏂瑰浘鍜屼綆鍒嗚鲸鐜囧浘鍍忔墍缁勬垚鐨勭煝閲 + //Asign values to feature,ANN的样本特征为水平、垂直直方图和低分辨率图像所组成的矢量 int j=0; for(int i=0; i files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); @@ -23,7 +23,7 @@ void learn2HasPlate(float bound = 0.7) cout << "File not found in " << filePath << endl; return; } - ////闅忔満閫夊彇70%浣滀负璁粌鏁版嵁锛30%浣滀负娴嬭瘯鏁版嵁 + ////随机选取70%作为训练数据,30%作为测试数据 srand(unsigned(time(NULL))); random_shuffle(files.begin(), files.end()); @@ -62,14 +62,14 @@ void learn2NoPlate(float bound = 0.7) const char * filePath = "train/data/plate_detect_svm/learn/NoPlate"; vector files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); if (0 == size) { cout << "File not found in " << filePath << endl; return; } - ////闅忔満閫夊彇70%浣滀负璁粌鏁版嵁锛30%浣滀负娴嬭瘯鏁版嵁 + ////随机选取70%作为训练数据,30%作为测试数据 srand(unsigned(time(NULL))); random_shuffle(files.begin(), files.end()); @@ -109,7 +109,7 @@ void getHasPlateTrain(Mat& trainingImages, vector& trainingLabels, const char * filePath = "train/data/plate_detect_svm/train/HasPlate"; vector files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); @@ -123,7 +123,7 @@ void getHasPlateTrain(Mat& trainingImages, vector& trainingLabels, //cout << files[i].c_str() << endl; Mat img = imread(files[i].c_str()); - //璋冪敤鍥炶皟鍑芥暟鍐冲畾鐗瑰緛 + //调用回调函数决定特征 Mat features; getFeatures(img, features); features = features.reshape(1, 1); @@ -141,7 +141,7 @@ void getNoPlateTrain(Mat& trainingImages, vector& trainingLabels, const char * filePath = "train/data/plate_detect_svm/train/NoPlate"; vector files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); @@ -155,7 +155,7 @@ void getNoPlateTrain(Mat& trainingImages, vector& trainingLabels, //cout << files[i].c_str() << endl; Mat img = imread(files[i].c_str()); - //璋冪敤鍥炶皟鍑芥暟鍐冲畾鐗瑰緛 + //调用回调函数决定特征 Mat features; getFeatures(img, features); features = features.reshape(1, 1); @@ -171,7 +171,7 @@ void getHasPlateTest(vector& testingImages, vector& testingLabels) const char * filePath = "train/data/plate_detect_svm/test/HasPlate"; vector files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); @@ -196,7 +196,7 @@ void getNoPlateTest(vector& testingImages, vector& testingLabels) const char * filePath = "train/data/plate_detect_svm/test/NoPlate"; vector files; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 getFiles(filePath, files ); int size = files.size(); @@ -216,7 +216,7 @@ void getNoPlateTest(vector& testingImages, vector& testingLabels) } -//! 娴嬭瘯SVM鐨勫噯纭巼锛屽洖褰掔巼浠ュ強FScore +//! 测试SVM的准确率,回归率以及FScore void getAccuracy(Mat& testingclasses_preditc, Mat& testingclasses_real) { int channels = testingclasses_preditc.channels(); @@ -318,13 +318,13 @@ int svmTrain(bool dividePrepared = true, bool trainPrepared = true, if (dividePrepared == false) { - //鍒嗗壊learn閲岀殑鏁版嵁鍒皌rain鍜宼est閲 + //分割learn里的数据到train和test里 cout << "Divide learn to train and test" << endl; learn2HasPlate(); learn2NoPlate(); } - //灏嗚缁冩暟鎹姞杞藉叆鍐呭瓨 + //将训练数据加载入内存 if (trainPrepared == false) { cout << "Begin to get train data to memory" << endl; @@ -340,7 +340,7 @@ int svmTrain(bool dividePrepared = true, bool trainPrepared = true, vector testingImages; vector testingLabels_real; - //灏嗘祴璇曟暟鎹姞杞藉叆鍐呭瓨 + //将测试数据加载入内存 cout << "Begin to get test data to memory" << endl; getHasPlateTest(testingImages, testingLabels_real); getNoPlateTest(testingImages, testingLabels_real); @@ -350,8 +350,8 @@ int svmTrain(bool dividePrepared = true, bool trainPrepared = true, { CvSVMParams SVM_params; SVM_params.svm_type = CvSVM::C_SVC; - //SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR; 绾垮瀷锛屼篃灏辨槸鏃犳牳 - SVM_params.kernel_type = CvSVM::RBF; //CvSVM::RBF 寰勫悜鍩哄嚱鏁帮紝涔熷氨鏄珮鏂牳 + //SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR; 线型,也就是无核 + SVM_params.kernel_type = CvSVM::RBF; //CvSVM::RBF 径向基函数,也就是高斯核 SVM_params.degree = 0.1; SVM_params.gamma = 1; SVM_params.coef0 = 0.1; @@ -407,7 +407,7 @@ int svmTrain(bool dividePrepared = true, bool trainPrepared = true, //cout << files[i].c_str() << endl; Mat p = testingImages[i]; - //璋冪敤鍥炶皟鍑芥暟鍐冲畾鐗瑰緛 + //调用回调函数决定特征 Mat features; getFeatures(p, features); diff --git a/src/util/deface.cpp b/src/util/deface.cpp index 6965ef9..6c62c6f 100644 --- a/src/util/deface.cpp +++ b/src/util/deface.cpp @@ -1,9 +1,9 @@ // // main.cpp -// 浜鸿劯椹禌鍏 +// 人脸马赛克 // // Created by zhou shiwei on 15/1/20. -// Copyright (c) 2015骞 zhou shiwei. All rights reserved. +// Copyright (c) 2015年 zhou shiwei. All rights reserved. // // Modified by liu ruoze on 15/1/23 @@ -15,9 +15,9 @@ #include #include -// 鍙嶄汉鑴歌瘑鍒枃浠 -// 鍦ㄥ彂甯冩暟鎹埌general_test鏁版嵁闆嗛噷锛岃鍏堢敤杩欓噷鐨勬柟娉曞鍥惧儚杩涜澶勭悊 -// EasyPR寮婧愰」鐩潪甯告敞閲嶄繚鎶ゅ浘鐗囦腑椹鹃┒浜虹殑闅愮 +// 反人脸识别文件 +// 在发布数据到general_test数据集里,请先用这里的方法对图像进行处理 +// EasyPR开源项目非常注重保护图片中驾驶人的隐私 #include "../include/plate_recognize.h" #include "../include/util.h" @@ -29,9 +29,9 @@ using namespace cv; Mat detectAndMaskFace(Mat& img, CascadeClassifier& cascade, double scale ); -// 鍙嶄汉鑴歌瘑鍒嚱鏁 -// 鍦ㄥ彂甯冩暟鎹埌general_test鏁版嵁闆嗛噷锛岃鍏堢敤杩欓噷鐨勬柟娉曞鍥惧儚杩涜澶勭悊 -// EasyPR寮婧愰」鐩潪甯告敞閲嶄繚鎶ゅ浘鐗囦腑椹鹃┒浜虹殑闅愮 +// 反人脸识别函数 +// 在发布数据到general_test数据集里,请先用这里的方法对图像进行处理 +// EasyPR开源项目非常注重保护图片中驾驶人的隐私 int deface() { CascadeClassifier cascade; diff --git a/src/util/general_test_prepare.cpp b/src/util/general_test_prepare.cpp index 67782bb..f7c07f5 100644 --- a/src/util/general_test_prepare.cpp +++ b/src/util/general_test_prepare.cpp @@ -1,6 +1,6 @@ -// 杈呭姪鐢熸垚general_test涓殑鍥剧墖 -// 閫氱敤鍋氭硶鏄娇鐢╡asypr渚濇璇诲彇鍥剧墖锛屽苟鐢ㄥ叾涓殑涓涓溅鐗岃瘑鍒枃瀛楃粰鍥剧墖閲嶅懡鍚 -// easypr璇嗗埆鐨勬枃瀛椾笉涓瀹氬噯纭紝鍥犳闇瑕佷汉鍐嶆纭涓庢墜宸ヨ皟鏁翠笅 +// 辅助生成general_test中的图片 +// 通用做法是使用easypr依次读取图片,并用其中的一个车牌识别文字给图片重命名 +// easypr识别的文字不一定准确,因此需要人再次确认与手工调整下 #include "../include/plate_recognize.h" #include "../include/util.h" @@ -10,15 +10,15 @@ using namespace easypr; extern const string GENERAL_TEST_PATH; -// TODO 灏嗕笅闈㈢殑璺緞鏀规垚浣犵殑 +// TODO 将下面的路径改成你的 const string src_path = "F:/data/easypr-data/tmp-5"; -// TODO 灏嗕笅闈㈢殑璺緞鏀规垚浣犵殑 +// TODO 将下面的路径改成你的 const string dst_path = "F:/data/easypr-data/tmp-6"; int general_test() { - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(src_path, files); @@ -46,7 +46,7 @@ int general_test() string filepath = files[i].c_str(); cout << "------------------" << endl; - // EasyPR寮濮嬪垽鏂溅鐗 + // EasyPR开始判断车牌 Mat src = imread(filepath); vector plateVec; @@ -57,14 +57,14 @@ int general_test() if (num == 0) { - cout << ""<< "鏃犺溅鐗" < spilt_plate = Utils::splitString(colorplate, ':'); int size = spilt_plate.size(); @@ -78,7 +78,7 @@ int general_test() } else { - cout << "閿欒鐮:" << result << endl; + cout << "错误码:" << result << endl; } } diff --git a/src/util/generate_gdts.cpp b/src/util/generate_gdts.cpp index 71139dc..459af5a 100644 --- a/src/util/generate_gdts.cpp +++ b/src/util/generate_gdts.cpp @@ -1,7 +1,7 @@ -// 鐢熸垚GDTS鐨勬枃浠 -// 鍦ㄦ崘璧犳暟鎹埌GDTS鏁版嵁闆嗛噷锛岃鍏堢敤杩欓噷鐨勬柟娉曞鍥惧儚杩涜棰勫鐞 -// EasyPR寮婧愰」鐩潪甯告敞閲嶄繚鎶ゅ師濮嬪浘鐗囩殑鐗堟潈 -// 鎵鏈夌殑鎹愯禒鏁版嵁閫氳繃GDSL鍗忚杩涜绾﹀畾锛屼繚璇佷娇鐢ㄤ汉浠呯敤浜庨潪鍟嗕笟鎬х洰鐨 +// 生成GDTS的文件 +// 在捐赠数据到GDTS数据集里,请先用这里的方法对图像进行预处理 +// EasyPR开源项目非常注重保护原始图片的版权 +// 所有的捐赠数据通过GDSL协议进行约定,保证使用人仅用于非商业性目的 #include "../include/plate_recognize.h" #include "../include/util.h" @@ -9,15 +9,15 @@ using namespace easypr; -// TODO 灏嗕笅闈㈢殑璺緞鏀规垚浣犵殑鍘熷鍥剧墖璺緞 -// 鍥剧墖涓嶈澶氾紝10-30寮犲氨瓒冲浜嗭紝EasyPR瀵笹DTS鏁版嵁闆嗙殑浣跨敤涓嶄互閲忎负涓昏鎸囨爣 -// 鍙杩欎簺鍥剧墖瓒冲鍙嶆槧浣犳暟鎹泦鐨勪富瑕佺壒寰佸嵆鍙 +// TODO 将下面的路径改成你的原始图片路径 +// 图片不要多,10-30张就足够了,EasyPR对GDTS数据集的使用不以量为主要指标 +// 只要这些图片足够反映你数据集的主要特征即可 const string src_path = "F:/data/easypr-data/tmp-1"; -// TODO 灏嗕笅闈㈢殑璺緞鏀规垚浣犲笇鏈涚敓鎴愭崘璧犵粰GDTS鏁版嵁瀛樻斁鐨勬柊璺緞 +// TODO 将下面的路径改成你希望生成捐赠给GDTS数据存放的新路径 const string dst_path = "F:/data/easypr-data/tmp-2"; -// EasyPR鐨勫浘鍍忛澶勭悊鍑芥暟锛岃繘琛屾ā绯婂寲涓庤鍓寲澶勭悊 +// EasyPR的图像预处理函数,进行模糊化与裁剪化处理 Mat imageProcess(Mat src); Mat detectAndMaskFace(Mat& img, CascadeClassifier& cascade, @@ -25,12 +25,12 @@ Mat detectAndMaskFace(Mat& img, CascadeClassifier& cascade, int generate_gdts() { - // 鑾峰彇浜鸿劯璇嗗埆鏂囦欢 + // 获取人脸识别文件 CascadeClassifier cascade; //string cascadeName="/Users/zhoushiwei/Dropbox/Avatar/SDMLIB/haarcascade_frontalface_alt2.xml"; string cascadeName="model/haarcascade_frontalface_alt_tree.xml"; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(src_path, files); int size = files.size(); @@ -49,16 +49,16 @@ int generate_gdts() cout << "------------------" << endl; cout << filepath << endl; - // EasyPR璇诲彇鍘熷浘鐗 + // EasyPR读取原图片 Mat src = imread(filepath); - // EasyPR寮濮嬪鍥剧墖杩涜妯$硦鍖栦笌瑁佸壀鍖栧鐞 + // EasyPR开始对图片进行模糊化与裁剪化处理 Mat img = imageProcess(src); - // EasyPR寮濮嬪鍥剧墖杩涜浜鸿劯璇嗗埆澶勭悊 + // EasyPR开始对图片进行人脸识别处理 Mat dst = detectAndMaskFace(img, cascade, 1.5); - // 灏嗗浘鐗囧鍑哄埌鏂拌矾寰 + // 将图片导出到新路径 vector spilt_path = Utils::splitString(filepath, '\\'); int spiltsize = spilt_path.size(); @@ -110,7 +110,7 @@ int generate_gdts() -// EasyPR鐨勫浘鍍忛澶勭悊鍑芥暟锛岃繘琛屾ā绯婂寲涓庤鍓寲澶勭悊 +// EasyPR的图像预处理函数,进行模糊化与裁剪化处理 Mat imageProcess(Mat img) { int width = img.size().width; diff --git a/src/util/learn_prepare.cpp b/src/util/learn_prepare.cpp index 60da9d9..f18660e 100644 --- a/src/util/learn_prepare.cpp +++ b/src/util/learn_prepare.cpp @@ -1,29 +1,29 @@ // learn data_prepare : -// 鐢熸垚learn data鐨刢pp锛宭earn data鎸囩殑鏄痶rain data锛寁erify data锛 test data鐨勬诲拰锛岃繖涓悕瀛楁槸鎴戣捣鐨勩 -// learn data搴旇鏄创濂芥爣绛剧殑鏁版嵁锛屽洜姝ゅ嵆渚块氳繃EasyPR鑷姩鍖栧鐞嗕互鍚庯紝涔熻浜轰负鐨勪慨姝d竴涓. +// 生成learn data的cpp,learn data指的是train data,verify data, test data的总和,这个名字是我起的。 +// learn data应该是贴好标签的数据,因此即便通过EasyPR自动化处理以后,也要人为的修正一下. -// 浣犲簲璇ユ寜鐓у涓嬬殑姝ラ鍜岄『搴忕粍缁噇earn data锛 -// 1.鐢‥asyPR瀵筺ot label data杩涜澶勭悊锛岄氳繃EasyPR杈呭姪鏉ュ杞︾墝鍥剧墖杩涜鏍囩锛堟斁鍒颁笉鍚屾枃浠跺す涓嬶級锛 -// 2.鏍囩鍒嗕袱绉嶏紝涓绉嶆槸鏈夎溅鐗岀殑锛屼竴绉嶆槸鏃犺溅鐗岀殑锛 -// 2.EasyPR鐢熸垚鏍囩浠ュ悗锛屼綘杩樺緱闇瑕佽倝鐪肩‘璁や笅锛屽皢琚笉姝g‘鏍囩鐨勬暟鎹浆绉诲埌瀹冧滑璇ュ幓鐨勬枃浠跺す涓嬶紱 -// 3.閫氳繃涓婇潰鐨勬楠わ紝姝g‘鐨勮溅鐗屽浘鐗囧拰闈炶溅鐗屽浘鐗囧凡缁忚鍒嗗埆鏀惧埌涓や釜鏂囦欢涓嬶紝鍋囪鏄痟asplate鍜宯oplate锛 -// 4.灏嗚繖涓や釜鏂囦欢澶规斁鍒癊asyPR鐩綍train/data/plate_detect_svm/learn涓嬮潰 -// 5.杩愯EasyPR锛岄夋嫨鈥滆缁冣濈洰褰曚笅鐨勨滆溅鐗屾娴嬶紙not divide锛夆濓紝杩欎釜绋嬪簭浼氳嚜鍔ㄦ妸learn data鍒嗗潡锛岃缁冿紝娴嬭瘯 +// 你应该按照如下的步骤和顺序组织learn data: +// 1.用EasyPR对not label data进行处理,通过EasyPR辅助来对车牌图片进行标签(放到不同文件夹下); +// 2.标签分两种,一种是有车牌的,一种是无车牌的; +// 2.EasyPR生成标签以后,你还得需要肉眼确认下,将被不正确标签的数据转移到它们该去的文件夹下; +// 3.通过上面的步骤,正确的车牌图片和非车牌图片已经被分别放到两个文件下,假设是hasplate和noplate; +// 4.将这两个文件夹放到EasyPR目录train/data/plate_detect_svm/learn下面 +// 5.运行EasyPR,选择“训练”目录下的“车牌检测(not divide)”,这个程序会自动把learn data分块,训练,测试 #include "../include/plate_recognize.h" #include "../include/util.h" using namespace easypr; -//! 灏嗕互涓嬭矾寰勬敼鎴愪綘鐨勪綅缃 +//! 将以下路径改成你的位置 char * notlabelPath = "F:/data/easypr-data/notlabel"; char * hasPaltePath = "F:/data/easypr-data/learn/hasPlate/"; char * noPlatePath = "F:/data/easypr-data/learn/noPlate/"; -//! 璇诲彇not label data锛岀敓鎴恖earn data +//! 读取not label data,生成learn data void label_data() { - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(notlabelPath, files); diff --git a/src/util/mc_data_prepare.cpp b/src/util/mc_data_prepare.cpp index 005cd17..818afdc 100644 --- a/src/util/mc_data_prepare.cpp +++ b/src/util/mc_data_prepare.cpp @@ -1,7 +1,7 @@ // mc_data_prepare.cpp : -// 鏁版嵁棰勫鐞嗙殑绋嬪簭锛屼富瑕佸仛浠ヤ笅涓や欢浜 -// 1.璇诲彇鍘熺敓鏁版嵁 rawdata锛岃繖鍙兘鏈夋暟鐧句竾寮 -// 2.闅忔満/閫夋嫨鎬у湴閫夊彇閮ㄥ垎鏁版嵁鎴愪负learndata锛岃繖涓牴鎹弬鏁拌缃紝涓鑸缃负1000锛10000锛屾垨鑰1鐧句竾 +// 数据预处理的程序,主要做以下两件事 +// 1.读取原生数据 rawdata,这可能有数百万张 +// 2.随机/选择性地选取部分数据成为learndata,这个根据参数设置,一般设置为1000,10000,或者1百万 #include #include @@ -26,65 +26,65 @@ const int LEARANDATA_COUNT = 5000; void getFiles( string path, vector& files ); -//! 鐪佷唤瀵瑰簲map +//! 省份对应map map mc_map; void Code2Province(const string& code, string& province) { if (mc_map.empty()) { - mc_map.insert(pair("E00","鏈瘑鍒")); - mc_map.insert(pair("A01","浜")); - mc_map.insert(pair("A02","娲")); - mc_map.insert(pair("A03","娌")); - mc_map.insert(pair("A04","娓")); - mc_map.insert(pair("B01","妗")); - mc_map.insert(pair("B02","钂")); - mc_map.insert(pair("B03","瀹")); - mc_map.insert(pair("B04","鏂")); - mc_map.insert(pair("B05","钘")); - mc_map.insert(pair("S01","鐨")); - mc_map.insert(pair("S02","闂")); - mc_map.insert(pair("S03","绮")); - mc_map.insert(pair("S04","鐢")); - mc_map.insert(pair("S05","璐")); - mc_map.insert(pair("S06","閯")); - mc_map.insert(pair("S07","鍐")); - mc_map.insert(pair("S08","榛")); - mc_map.insert(pair("S09","婀")); - mc_map.insert(pair("S10","璞")); - mc_map.insert(pair("S11","鐞")); - mc_map.insert(pair("S12","鍚")); - mc_map.insert(pair("S13","鑻")); - mc_map.insert(pair("S14","璧")); - mc_map.insert(pair("S15","杈")); - mc_map.insert(pair("S16","闈")); - mc_map.insert(pair("S17","宸")); - mc_map.insert(pair("S18","椴")); - mc_map.insert(pair("S19","闄")); - mc_map.insert(pair("S20","鏅")); - mc_map.insert(pair("S21","浜")); - mc_map.insert(pair("S22","娴")); - mc_map.insert(pair("J01","鍐")); - mc_map.insert(pair("J02","娴")); - mc_map.insert(pair("J03","绌")); - mc_map.insert(pair("J04","鍖")); - mc_map.insert(pair("J05","鎴")); - mc_map.insert(pair("J06","骞")); - mc_map.insert(pair("J07","娴")); - mc_map.insert(pair("J08","鍏")); - mc_map.insert(pair("J09","鍗")); - mc_map.insert(pair("J10","娌")); + mc_map.insert(pair("E00","未识别")); + mc_map.insert(pair("A01","京")); + mc_map.insert(pair("A02","津")); + mc_map.insert(pair("A03","沪")); + mc_map.insert(pair("A04","渝")); + mc_map.insert(pair("B01","桂")); + mc_map.insert(pair("B02","蒙")); + mc_map.insert(pair("B03","宁")); + mc_map.insert(pair("B04","新")); + mc_map.insert(pair("B05","藏")); + mc_map.insert(pair("S01","皖")); + mc_map.insert(pair("S02","闽")); + mc_map.insert(pair("S03","粤")); + mc_map.insert(pair("S04","甘")); + mc_map.insert(pair("S05","贵")); + mc_map.insert(pair("S06","鄂")); + mc_map.insert(pair("S07","冀")); + mc_map.insert(pair("S08","黑")); + mc_map.insert(pair("S09","湘")); + mc_map.insert(pair("S10","豫")); + mc_map.insert(pair("S11","琼")); + mc_map.insert(pair("S12","吉")); + mc_map.insert(pair("S13","苏")); + mc_map.insert(pair("S14","赣")); + mc_map.insert(pair("S15","辽")); + mc_map.insert(pair("S16","青")); + mc_map.insert(pair("S17","川")); + mc_map.insert(pair("S18","鲁")); + mc_map.insert(pair("S19","陕")); + mc_map.insert(pair("S20","晋")); + mc_map.insert(pair("S21","云")); + mc_map.insert(pair("S22","浙")); + mc_map.insert(pair("J01","军")); + mc_map.insert(pair("J02","海")); + mc_map.insert(pair("J03","空")); + mc_map.insert(pair("J04","北")); + mc_map.insert(pair("J05","成")); + mc_map.insert(pair("J06","广")); + mc_map.insert(pair("J07","济")); + mc_map.insert(pair("J08","兰")); + mc_map.insert(pair("J09","南")); + mc_map.insert(pair("J10","沈")); } if (mc_map.count(code)) province = mc_map[code]; else - province = "鏃"; + province = "无"; } -//MC锛氬垏鍘讳笂閮ㄤ笌搴曢儴骞叉壈鐨勭粏鑺 +//MC:切去上部与底部干扰的细节 Mat cutBottom(Mat img) { int width = img.size().width; @@ -94,7 +94,7 @@ Mat cutBottom(Mat img) return dst; } -//MC锛歠ilepath绀轰緥锛欶:\data\easypr-data\learndata\20150110132005-210028-S18-H3952K.jpg +//MC:filepath示例:F:\data\easypr-data\learndata\20150110132005-210028-S18-H3952K.jpg bool isNotNight(const string& filepath) { vector spilt_path = Utils::splitString(filepath, '\\'); @@ -115,8 +115,8 @@ bool isNotNight(const string& filepath) datestr = spilt_name[0]; if (datestr != "") { - //"20150110132005", 鏃堕棿鍦ㄧ6锛7涓綅缃 - //灏嗘棭涓10鐐瑰埌涓嬪崍2鐐圭殑鍥剧墖鎴彇鍑烘潵 + //"20150110132005", 时间在第6,7个位置 + //将早上10点到下午2点的图片截取出来 string hourstr = datestr.substr(8, 2); if ( hourstr <= "14" && hourstr >= "10") return true; @@ -128,7 +128,7 @@ bool isNotNight(const string& filepath) return false; } -//! MC锛氬皢rawdata鐨勬枃浠舵崲涓矾寰勫埌learndata閲 +//! MC:将rawdata的文件换个路径到learndata里 bool getNewPath(const string& filepath, string& newfilepath) { string writePath = "F:/data/easypr-data/learndata_dl/"; @@ -146,7 +146,7 @@ bool getNewPath(const string& filepath, string& newfilepath) } -//! MC锛氶氳繃filepath鑾峰彇杞︾墝鍙风爜 +//! MC:通过filepath获取车牌号码 void getPlateLicense(const string& filepath, string& plateLicense) { vector spilt_path = Utils::splitString(filepath, '\\'); @@ -166,10 +166,10 @@ void getPlateLicense(const string& filepath, string& plateLicense) string provinceStr = ""; if (name_size != 0) { - // plateStr鏄渶鍚庝竴涓瓧绗︿覆锛屽寘鎷悗闈㈢殑".jpg" + // plateStr是最后一个字符串,包括后面的".jpg" plateStr = spilt_name[name_size-1]; - // 灏".jpg"鍘绘帀 + // 将".jpg"去掉 vector spilt_plate = Utils::splitString(plateStr, '.'); int plate_size = spilt_plate.size(); @@ -179,7 +179,7 @@ void getPlateLicense(const string& filepath, string& plateLicense) rawplate = spilt_plate[0]; } - // provinceStr鏄掓暟绗簩涓瓧绗︿覆锛屼富瑕佹槸"S13"浠h〃"鑻" + // provinceStr是倒数第二个字符串,主要是"S13"代表"苏" provinceCode = spilt_name[name_size-2]; Code2Province(provinceCode, provinceStr); plateLicense = provinceStr + rawplate; @@ -189,12 +189,12 @@ void getPlateLicense(const string& filepath, string& plateLicense) } -//! MC锛氬皢rawdata鎴彇閮ㄥ垎鏁版嵁鍒發earndata涓 +//! MC:将rawdata截取部分数据到learndata中 void getLearnData() { const char * filePath = "F:/data/easypr-data/rawdata"; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(filePath, files ); @@ -203,11 +203,11 @@ void getLearnData() cout << "File not found in " << filePath << endl; return; } - ////闅忔満鎺掑垪rawdata + ////随机排列rawdata srand(unsigned(time(NULL))); random_shuffle(files.begin(), files.end()); - ////閫夊彇鍓峀EARANDATA_COUNT涓猺awdata鏁版嵁浣滀负learndata + ////选取前LEARANDATA_COUNT个rawdata数据作为learndata int boundry = LEARANDATA_COUNT; int count = 0; cout << "Learndata saved!" << endl; @@ -216,15 +216,15 @@ void getLearnData() cout << files[i].c_str() << endl; string filepath = files[i].c_str(); - //鍙鐞嗙櫧澶╃殑鏁版嵁 + //只处理白天的数据 //if (isNotNight(filepath)!=true) // continue; - //璇诲彇鏁版嵁锛屽苟瀵瑰浘鐗囪繘琛岄澶勭悊 + //读取数据,并对图片进行预处理 Mat img = imread(filepath); img = cutBottom(img); - //瀛樺偍璺緞鏇挎崲涓烘柊鐨 + //存储路径替换为新的 string newfilepath = ""; getNewPath(filepath, newfilepath); @@ -241,13 +241,13 @@ void getLearnData() } } -//! 灏嗘枃浠跺悕绉版浛鎹紝閲嶇偣鏄疢C鐨刢ode鏇挎崲涓簆rovince +//! 将文件名称替换,重点是MC的code替换为province void changeFileName() { //char * filePath = "F:/data/easypr-data/learndata"; char * filePath = "F:/data/PlateLocate/pic1"; - ////鑾峰彇璇ヨ矾寰勪笅鐨勬墍鏈夋枃浠 + ////获取该路径下的所有文件 vector files; getFiles(filePath, files ); @@ -260,7 +260,7 @@ void changeFileName() cout << files[i].c_str() << endl; string filepath = files[i].c_str(); - //璇诲彇鏁版嵁锛屽苟瀵瑰浘鐗囪繘琛岄澶勭悊 + //读取数据,并对图片进行预处理 Mat img = imread(filepath); img = cutBottom(img); diff --git a/src/util/util.cpp b/src/util/util.cpp index dabf7a8..c9163c6 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -109,17 +109,17 @@ Utils::splitString(const string &str, const char delimiter) void getFiles(string path, vector& files) { - //鏂囦欢鍙ユ焺 + //文件句柄 long hFile = 0; - //鏂囦欢淇℃伅 + //文件信息 struct _finddata_t fileinfo; string p; if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1) { do { - //濡傛灉鏄洰褰,杩唬涔 - //濡傛灉涓嶆槸,鍔犲叆鍒楄〃 + //如果是目录,迭代之 + //如果不是,加入列表 if((fileinfo.attrib & _A_SUBDIR)) { if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0)