From 7b4abff14f458905fa9e465c47427a9fdde7a5b9 Mon Sep 17 00:00:00 2001 From: zoeda <2063629468@qq.com> Date: Thu, 18 Jan 2024 00:00:39 +0800 Subject: [PATCH] lcx complete /src/core_func.cpp --- src/src/core/chars_identify.cpp | 930 +++++++------- src/src/core/core_func.cpp | 1997 ++++++++++++++++++++----------- 2 files changed, 1768 insertions(+), 1159 deletions(-) diff --git a/src/src/core/chars_identify.cpp b/src/src/core/chars_identify.cpp index 452978f..306cae1 100644 --- a/src/src/core/chars_identify.cpp +++ b/src/src/core/chars_identify.cpp @@ -7,521 +7,595 @@ using namespace cv; -namespace easypr { +namespace easypr +{ -CharsIdentify* CharsIdentify::instance_ = nullptr; + CharsIdentify *CharsIdentify::instance_ = nullptr; -CharsIdentify* CharsIdentify::instance() { - if (!instance_) { - instance_ = new CharsIdentify; + CharsIdentify *CharsIdentify::instance() + { + if (!instance_) + { + instance_ = new CharsIdentify; + } + return instance_; } - return instance_; -} -// 主要用于加载和管理预训练的神经网络模型,用于字符识别 -CharsIdentify::CharsIdentify() { - LOAD_ANN_MODEL(ann_, kDefaultAnnPath); - LOAD_ANN_MODEL(annChinese_, kChineseAnnPath); - LOAD_ANN_MODEL(annGray_, kGrayAnnPath); + // 涓昏鐢ㄤ簬鍔犺浇鍜岀鐞嗛璁粌鐨勭缁忕綉缁滄ā鍨嬶紝鐢ㄤ簬瀛楃璇嗗埆 + CharsIdentify::CharsIdentify() + { + LOAD_ANN_MODEL(ann_, kDefaultAnnPath); + LOAD_ANN_MODEL(annChinese_, kChineseAnnPath); + LOAD_ANN_MODEL(annGray_, kGrayAnnPath); - kv_ = std::shared_ptr(new Kv); - kv_->load(kChineseMappingPath); + kv_ = std::shared_ptr(new Kv); + kv_->load(kChineseMappingPath); - extractFeature = getGrayPlusProject; -} + extractFeature = getGrayPlusProject; + } -void CharsIdentify::LoadModel(std::string path) { - if (path != std::string(kDefaultAnnPath)) { - if (!ann_->empty()) - ann_->clear(); - LOAD_ANN_MODEL(ann_, path); + void CharsIdentify::LoadModel(std::string path) + { + if (path != std::string(kDefaultAnnPath)) + { + if (!ann_->empty()) + ann_->clear(); + LOAD_ANN_MODEL(ann_, path); + } } -} -void CharsIdentify::LoadChineseModel(std::string path) { - if (path != std::string(kChineseAnnPath)) { - if (!annChinese_->empty()) - annChinese_->clear(); - LOAD_ANN_MODEL(annChinese_, path); + void CharsIdentify::LoadChineseModel(std::string path) + { + if (path != std::string(kChineseAnnPath)) + { + if (!annChinese_->empty()) + annChinese_->clear(); + LOAD_ANN_MODEL(annChinese_, path); + } } -} -void CharsIdentify::LoadGrayChANN(std::string path) { - if (path != std::string(kGrayAnnPath)) { - if (!annGray_->empty()) - annGray_->clear(); - LOAD_ANN_MODEL(annGray_, path); + void CharsIdentify::LoadGrayChANN(std::string path) + { + if (path != std::string(kGrayAnnPath)) + { + if (!annGray_->empty()) + annGray_->clear(); + LOAD_ANN_MODEL(annGray_, path); + } } -} -void CharsIdentify::LoadChineseMapping(std::string path) { - kv_->clear(); - kv_->load(path); -} + void CharsIdentify::LoadChineseMapping(std::string path) + { + kv_->clear(); + kv_->load(path); + } -// 对输入的特征行进行预测,并识别出最可能的字符。 -void CharsIdentify::classify(cv::Mat featureRows, std::vector& out_maxIndexs, - std::vector& out_maxVals, std::vector isChineseVec){ - // 获取特征行的行数。 - int rowNum = featureRows.rows; - // 创建一个新的矩阵output,大小为特征行的行数(rowNum)乘以总的字符数量 - cv::Mat output(rowNum, kCharsTotalNumber, CV_32FC1); - // 使用预先训练好的模型(ann_)对输入的特征行进行预测,结果保存在output矩阵中 - ann_->predict(featureRows, output); - // 循环遍历每一行输出: - // 对于每一行,首先获取该行的预测结果 - for (int output_index = 0; output_index < rowNum; output_index++) { - Mat output_row = output.row(output_index); - int result = 0; - float maxVal = -2.f; - bool isChinses = isChineseVec[output_index]; - // 如果该行不是中文字符(由isChineseVec向量确定), - // 则遍历前kCharactersNumber个预测结果,找出值最大的那个,并记录其索引和值。 - if (!isChinses) { - result = 0; - for (int j = 0; j < kCharactersNumber; j++) { - float val = output_row.at(j); - // std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; + // 瀵硅緭鍏ョ殑鐗瑰緛琛岃繘琛岄娴嬶紝骞惰瘑鍒嚭鏈鍙兘鐨勫瓧绗︺ + void CharsIdentify::classify(cv::Mat featureRows, std::vector &out_maxIndexs, + std::vector &out_maxVals, std::vector isChineseVec) + { + // 鑾峰彇鐗瑰緛琛岀殑琛屾暟銆 + int rowNum = featureRows.rows; + // 鍒涘缓涓涓柊鐨勭煩闃祇utput锛屽ぇ灏忎负鐗瑰緛琛岀殑琛屾暟锛坮owNum锛変箻浠ユ荤殑瀛楃鏁伴噺 + cv::Mat output(rowNum, kCharsTotalNumber, CV_32FC1); + // 浣跨敤棰勫厛璁粌濂界殑妯″瀷锛坅nn_锛夊杈撳叆鐨勭壒寰佽杩涜棰勬祴锛岀粨鏋滀繚瀛樺湪output鐭╅樀涓 + ann_->predict(featureRows, output); + // 寰幆閬嶅巻姣忎竴琛岃緭鍑猴細 + // 瀵逛簬姣忎竴琛岋紝棣栧厛鑾峰彇璇ヨ鐨勯娴嬬粨鏋 + for (int output_index = 0; output_index < rowNum; output_index++) + { + Mat output_row = output.row(output_index); + int result = 0; + float maxVal = -2.f; + bool isChinses = isChineseVec[output_index]; + // 濡傛灉璇ヨ涓嶆槸涓枃瀛楃锛堢敱isChineseVec鍚戦噺纭畾锛夛紝 + // 鍒欓亶鍘嗗墠kCharactersNumber涓娴嬬粨鏋滐紝鎵惧嚭鍊兼渶澶х殑閭d釜锛屽苟璁板綍鍏剁储寮曞拰鍊笺 + if (!isChinses) + { + result = 0; + for (int j = 0; j < kCharactersNumber; j++) + { + float val = output_row.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } + } + } + // 濡傛灉璇ヨ鏄腑鏂囧瓧绗︼紝 + // 鍒欎粠kCharactersNumber寮濮嬮亶鍘嗗悗闈㈢殑棰勬祴缁撴灉锛屾壘鍑哄兼渶澶х殑閭d釜锛屽苟璁板綍鍏剁储寮曞拰鍊笺 + else + { + result = kCharactersNumber; + for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) + { + float val = output_row.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } } } + // 灏嗚褰曠殑鏈澶х储寮曞拰鏈澶у煎垎鍒祴鍊肩粰out_maxIndexs鍜宱ut_maxVals鐨勭浉搴斾綅缃 + out_maxIndexs[output_index] = result; + out_maxVals[output_index] = maxVal; } - // 如果该行是中文字符, - // 则从kCharactersNumber开始遍历后面的预测结果,找出值最大的那个,并记录其索引和值。 - else { - result = kCharactersNumber; - for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) { + } + + // 鎺ュ彈涓涓狢Character绫诲瀷鐨勫悜閲忥紙charVec锛夛紝骞跺姣忎釜瀛楃杩涜鍒嗙被銆 + void CharsIdentify::classify(std::vector &charVec) + { + size_t charVecSize = charVec.size(); + + if (charVecSize == 0) + return; + + // 鍒涘缓涓涓悕涓篺eatureRows鐨凪at瀵硅薄锛屽苟閫氳繃寰幆灏嗘瘡涓瓧绗︾殑鐗瑰緛鎻愬彇鍑烘潵骞舵坊鍔犲埌featureRows涓 + Mat featureRows; + for (size_t index = 0; index < charVecSize; index++) + { + Mat charInput = charVec[index].getCharacterMat(); + Mat feature = charFeatures(charInput, kPredictSize); + featureRows.push_back(feature); + } + + // 鍒涘缓涓涓緭鍑虹煩闃祇utput锛屽苟浣跨敤棰勫厛璁粌濂界殑妯″瀷锛坅nn_锛夊鐗瑰緛杩涜棰勬祴銆 + cv::Mat output(charVecSize, kCharsTotalNumber, CV_32FC1); + ann_->predict(featureRows, output); + + // 閬嶅巻姣忎釜杈撳嚭锛屽浜庢瘡涓緭鍑猴紝棣栧厛鑾峰彇瀵瑰簲鐨勫瓧绗︼紙閫氳繃绱㈠紩锛夛紝 + // 鐒跺悗鑾峰彇璇ュ瓧绗︾殑棰勬祴缁撴灉琛岋紙閫氳繃绱㈠紩锛夈傜劧鍚庯紝鍑芥暟妫鏌ヨ瀛楃鏄惁涓轰腑鏂囧瓧绗︼紝 + // 濡傛灉涓嶆槸锛屽畠灏卞湪寰幆涓壘鍑哄兼渶澶х殑棰勬祴缁撴灉锛屽苟璁板綍鍏剁储寮曞拰鍊笺 + // 鏈鍚庯紝鍑芥暟鏍规嵁杩欎釜鏈澶у煎拰绱㈠紩纭畾棰勬祴鐨勫瓧绗︼紝骞跺皢鍏朵綔涓烘爣绛俱 + for (size_t output_index = 0; output_index < charVecSize; output_index++) + { + CCharacter &character = charVec[output_index]; + Mat output_row = output.row(output_index); + + int result = 0; + float maxVal = -2.f; + std::string label = ""; + + bool isChinses = character.getIsChinese(); + if (!isChinses) + { + result = 0; + for (int j = 0; j < kCharactersNumber; j++) + { + float val = output_row.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } + } + label = std::make_pair(kChars[result], kChars[result]).second; + } + // 濡傛灉瀛楃鏄腑鏂囧瓧绗︼紝鍑芥暟鍒欎粠棰勬祴缁撴灉鐨勫悗闈㈤儴鍒嗗紑濮嬫煡鎵炬渶澶у硷紝骞惰褰曞叾绱㈠紩鍜屽笺 + // 鐒跺悗锛屽嚱鏁版牴鎹繖涓渶澶у煎拰绱㈠紩纭畾棰勬祴鐨勫瓧绗︼紝骞堕氳繃閿煎锛坘v_锛夋煡鎵惧搴旂殑鐪佷唤锛屽皢瀛楃鍜岀渷浠戒綔涓烘爣绛俱 + else + { + result = kCharactersNumber; + for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) + { + float val = output_row.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } + } + const char *key = kChars[result]; + std::string s = key; + std::string province = kv_->get(s); + label = std::make_pair(s, province).second; + } + /*std::cout << "result:" << result << std::endl; + std::cout << "maxVal:" << maxVal << std::endl;*/ + + // 鍑芥暟灏嗛娴嬬殑鏈澶у煎拰鏍囩鍒嗗埆璁剧疆鍒板搴斿瓧绗﹀璞$殑寰楀垎鍜屽瓧绗︿覆灞炴т腑銆 + character.setCharacterScore(maxVal); + character.setCharacterStr(label); + } + } + + // 瀵硅緭鍏ョ殑涓枃瀛楃杩涜鍒嗙被 + void CharsIdentify::classifyChineseGray(std::vector &charVec) + { + size_t charVecSize = charVec.size(); + if (charVecSize == 0) + return; + + Mat featureRows; + // 閫氳繃寰幆鎻愬彇姣忎釜瀛楃鐨勭壒寰侊紝骞跺瓨鍌ㄥ湪featureRows涓 + for (size_t index = 0; index < charVecSize; index++) + { + Mat charInput = charVec[index].getCharacterMat(); + cv::Mat feature; + extractFeature(charInput, feature); + featureRows.push_back(feature); + } + // 鍒涘缓涓涓緭鍑虹煩闃碉紙output锛夛紝鐒跺悗浣跨敤棰勫厛璁粌濂界殑妯″瀷锛坅nnGray_锛夊鐗瑰緛杩涜棰勬祴锛屽苟灏嗙粨鏋滃瓨鍌ㄥ湪output涓 + cv::Mat output(charVecSize, kChineseNumber, CV_32FC1); + annGray_->predict(featureRows, output); + + // 瀵逛簬杈撳嚭鐭╅樀涓殑姣忎竴琛岋紙姣忎釜瀛楃鐨勯娴嬬粨鏋滐級锛 + // 濡傛灉璇ュ瓧绗︽槸涓枃瀛楃锛屽嚱鏁颁細浠庨娴嬬粨鏋滅殑鍚庨潰閮ㄥ垎寮濮嬫煡鎵炬渶澶у硷紝骞惰褰曞叾绱㈠紩鍜屽笺 + for (size_t output_index = 0; output_index < charVecSize; output_index++) + { + CCharacter &character = charVec[output_index]; + Mat output_row = output.row(output_index); + bool isChinese = true; + + float maxVal = -2; + int result = 0; + + for (int j = 0; j < kChineseNumber; j++) + { float val = output_row.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { maxVal = val; result = j; } } + + // no match + if (-1 == result) + { + result = 0; + maxVal = 0; + isChinese = false; + } + + // 鏍规嵁杩欎釜鏈澶у煎拰绱㈠紩纭畾棰勬祴鐨勫瓧绗︺ + // 杩欐槸閫氳繃鏌ユ壘kChars鏁扮粍瀹炵幇鐨勶紝鍏朵腑kChars鍙兘鏄竴涓瀹氫箟鐨勫瓧绗﹂泦銆 + auto index = result + kCharsTotalNumber - kChineseNumber; + const char *key = kChars[index]; + std::string s = key; + std::string province = kv_->get(s); + + /*std::cout << "result:" << result << std::endl; + std::cout << "maxVal:" << maxVal << std::endl;*/ + + // 灏嗛娴嬬殑鏈澶у笺侀娴嬬殑瀛楃浠ュ強瀵瑰簲鐨勭渷浠戒綔涓烘爣绛撅紝 + // 鍒嗗埆璁剧疆鍒板搴斿瓧绗﹀璞$殑寰楀垎銆佸瓧绗︿覆灞炴у拰鏄惁涓轰腑鏂囧瓧绗﹀睘鎬т腑 + character.setCharacterScore(maxVal); + character.setCharacterStr(province); + character.setIsChinese(isChinese); } - // 将记录的最大索引和最大值分别赋值给out_maxIndexs和out_maxVals的相应位置 - out_maxIndexs[output_index] = result; - out_maxVals[output_index] = maxVal; } -} -// 接受一个CCharacter类型的向量(charVec),并对每个字符进行分类。 -void CharsIdentify::classify(std::vector& charVec){ - size_t charVecSize = charVec.size(); - - if (charVecSize == 0) - return; - - // 创建一个名为featureRows的Mat对象,并通过循环将每个字符的特征提取出来并添加到featureRows中。 - Mat featureRows; - for (size_t index = 0; index < charVecSize; index++) { - Mat charInput = charVec[index].getCharacterMat(); - Mat feature = charFeatures(charInput, kPredictSize); - featureRows.push_back(feature); - } + // 浣跨敤OpenCV搴撳拰绁炵粡缃戠粶杩涜涓枃瀛楃璇嗗埆 + void CharsIdentify::classifyChinese(std::vector &charVec) + { + size_t charVecSize = charVec.size(); + + if (charVecSize == 0) + return; + + Mat featureRows; + // 閫氳繃寰幆閬嶅巻姣忎釜瀛楃锛屾彁鍙栧叾鐗瑰緛骞跺皢鍏跺瓨鍌ㄥ湪featureRows涓 + // 杩欓噷锛宑harFeatures鍑芥暟琚敤浜庢彁鍙栨瘡涓瓧绗︾殑鐗规э紝kChineseSize鍙兘鏄竴涓瀹氫箟鐨勭壒鎬уぇ灏忋 + for (size_t index = 0; index < charVecSize; index++) + { + Mat charInput = charVec[index].getCharacterMat(); + Mat feature = charFeatures(charInput, kChineseSize); + featureRows.push_back(feature); + } - // 创建一个输出矩阵output,并使用预先训练好的模型(ann_)对特征进行预测。 - cv::Mat output(charVecSize, kCharsTotalNumber, CV_32FC1); - ann_->predict(featureRows, output); + // 鍒涘缓涓涓緭鍑虹煩闃碉紙output锛夛紝骞朵娇鐢ㄩ鍏堣缁冨ソ鐨勬ā鍨嬶紙annChinese_锛夊鐗瑰緛杩涜棰勬祴銆傞娴嬬粨鏋滃瓨鍌ㄥ湪output涓 + cv::Mat output(charVecSize, kChineseNumber, CV_32FC1); + annChinese_->predict(featureRows, output); - // 遍历每个输出,对于每个输出,首先获取对应的字符(通过索引), - // 然后获取该字符的预测结果行(通过索引)。然后,函数检查该字符是否为中文字符, - // 如果不是,它就在循环中找出值最大的预测结果,并记录其索引和值。 - // 最后,函数根据这个最大值和索引确定预测的字符,并将其作为标签。 - for (size_t output_index = 0; output_index < charVecSize; output_index++) { - CCharacter& character = charVec[output_index]; - Mat output_row = output.row(output_index); + // 閬嶅巻姣忎釜棰勬祴缁撴灉锛屽苟瀵规瘡涓粨鏋滆繘琛屽鐞嗐傚浜庢瘡涓娴嬬粨鏋滐紝鍑芥暟鏌ユ壘鏈澶у煎強鍏剁储寮曘 + // 濡傛灉鏈澶у煎皬浜庢垨绛変簬-1锛屽垯灏嗘渶澶у艰缃负0锛屽苟灏唕esult璁剧疆涓0锛屽悓鏃跺皢isChinese璁剧疆涓篺alse銆 + for (size_t output_index = 0; output_index < charVecSize; output_index++) + { + CCharacter &character = charVec[output_index]; + Mat output_row = output.row(output_index); + bool isChinese = true; - int result = 0; - float maxVal = -2.f; - std::string label = ""; + float maxVal = -2; + int result = 0; - bool isChinses = character.getIsChinese(); - if (!isChinses) { - result = 0; - for (int j = 0; j < kCharactersNumber; j++) { + for (int j = 0; j < kChineseNumber; j++) + { float val = output_row.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { maxVal = val; result = j; } } - label = std::make_pair(kChars[result], kChars[result]).second; + + // no match + if (-1 == result) + { + result = 0; + maxVal = 0; + isChinese = false; + } + + // 璁$畻绱㈠紩鍊硷紝骞朵娇鐢ㄨ绱㈠紩浠巏Chars鏁扮粍涓幏鍙栧搴旂殑瀛楃銆 + // 鍚屾椂锛岄氳繃閿煎锛坘v_锛夋煡鎵句笌璇ュ瓧绗﹀搴旂殑鐪佷唤銆 + auto index = result + kCharsTotalNumber - kChineseNumber; + const char *key = kChars[index]; + std::string s = key; + std::string province = kv_->get(s); + + /*std::cout << "result:" << result << std::endl; + std::cout << "maxVal:" << maxVal << std::endl;*/ + + // 灏嗘渶澶у笺佺渷浠藉拰isChinese浣滀负鏍囩锛屽垎鍒缃埌瀵瑰簲瀛楃瀵硅薄鐨勫緱鍒嗐佸瓧绗︿覆灞炴у拰鏄惁涓轰腑鏂囧瓧绗﹀睘鎬т腑銆 + character.setCharacterScore(maxVal); + character.setCharacterStr(province); + character.setIsChinese(isChinese); } - // 如果字符是中文字符,函数则从预测结果的后面部分开始查找最大值,并记录其索引和值。 - // 然后,函数根据这个最大值和索引确定预测的字符,并通过键值对(kv_)查找对应的省份,将字符和省份作为标签。 - else { + } + + // 瀵硅緭鍏ョ殑鍥惧儚鏁版嵁杩涜鍒嗙被 + int CharsIdentify::classify(cv::Mat f, float &maxVal, bool isChinses, bool isAlphabet) + { + int result = 0; + // 璋冪敤棰勫厛璁粌濂界殑妯″瀷锛坅nn_锛夎繘琛岄娴嬶紝骞跺皢棰勬祴缁撴灉瀛樺偍鍦╫utput鍙橀噺涓 + cv::Mat output(1, kCharsTotalNumber, CV_32FC1); + ann_->predict(f, output); + + // 鏌ユ壘鏈澶у煎強鍏剁储寮曘傚鏋滃浘鍍忔暟鎹笉鏄腑鏂囷紝鍒欎細妫鏌ュ畠鏄惁鏄瓧姣嶃 + // 濡傛灉瀹冩槸瀛楁瘝锛岄偅涔堝嚱鏁板皢鍙煡鎵惧瓧姣嶈寖鍥村唴鐨勫硷紙浠10寮濮嬶紝瀵瑰簲浜'A'锛夈 + // 鍚﹀垯锛屽畠灏嗘煡鎵炬墍鏈夊瓧绗﹁寖鍥村唴鐨勫笺傚鏋滃浘鍍忔暟鎹槸涓枃锛屽垯鍑芥暟灏嗘煡鎵句腑鏂囧瓧绗﹁寖鍥村唴鐨勫 + maxVal = -2.f; + if (!isChinses) + { + if (!isAlphabet) + { + result = 0; + for (int j = 0; j < kCharactersNumber; j++) + { + float val = output.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } + } + } + else + { + result = 0; + // begin with 11th char, which is 'A' + for (int j = 10; j < kCharactersNumber; j++) + { + float val = output.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { + maxVal = val; + result = j; + } + } + } + } + else + { result = kCharactersNumber; - for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) { - float val = output_row.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { + for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) + { + float val = output.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { maxVal = val; result = j; } } - const char* key = kChars[result]; - std::string s = key; - std::string province = kv_->get(s); - label = std::make_pair(s, province).second; } - /*std::cout << "result:" << result << std::endl; - std::cout << "maxVal:" << maxVal << std::endl;*/ - - // 函数将预测的最大值和标签分别设置到对应字符对象的得分和字符串属性中。 - character.setCharacterScore(maxVal); - character.setCharacterStr(label); + // 杩斿洖绱㈠紩鍊紃esult锛岃鍊兼槸棰勬祴鐨勫瓧绗﹀湪棰勫厛瀹氫箟鐨勫瓧绗﹂泦kChars涓殑绱㈠紩銆 + // 鍚屾椂锛屽畠涔熷皢鏈澶у糾axVal鍜屽搴旂殑绱㈠紩result璁剧疆鍒拌緭鍏ョ殑float寮曠敤maxVal涓紝浠ヤ究璋冪敤鑰呭彲浠ヨ闂繖浜涘笺 + // std::cout << "maxVal:" << maxVal << std::endl; + return result; } -} + // 鏍规嵁杈撳叆鐨勫浘鍍忔暟鎹垽鏂畠鏄惁鏄竴涓瓧绗︼紙鐗瑰埆鏄腑鏂囧瓧绗︼級 + bool CharsIdentify::isCharacter(cv::Mat input, std::string &label, float &maxVal, bool isChinese) + { + // 璋冪敤charFeatures鍑芥暟鎻愬彇杈撳叆鍥惧儚鐨勭壒寰侊紝骞跺瓨鍌ㄥ湪feature鍙橀噺涓 + // 鐒跺悗锛屽畠璋冪敤classify鍑芥暟瀵圭壒寰佽繘琛屽垎绫伙紝寰楀埌涓涓储寮曞糹ndex + cv::Mat feature = charFeatures(input, kPredictSize); + auto index = static_cast(classify(feature, maxVal, isChinese)); -// 对输入的中文字符进行分类 -void CharsIdentify::classifyChineseGray(std::vector& charVec){ - size_t charVecSize = charVec.size(); - if (charVecSize == 0) - return; + if (isChinese) + { + // std::cout << "maxVal:" << maxVal << std::endl; + } - Mat featureRows; - // 通过循环提取每个字符的特征,并存储在featureRows中 - for (size_t index = 0; index < charVecSize; index++) { - Mat charInput = charVec[index].getCharacterMat(); - cv::Mat feature; - extractFeature(charInput, feature); - featureRows.push_back(feature); + float chineseMaxThresh = 0.2f; + // 妫鏌ラ娴嬬殑鏈澶у糾axVal鏄惁澶т簬绛変簬0.9锛屾垨鑰呭鏋滆緭鍏ョ殑瀛楃鏄腑鏂囦笖鏈澶у煎ぇ浜庣瓑浜巆hineseMaxThresh锛堣繖涓槇鍊艰璁剧疆涓0.2锛夈 + // 濡傛灉婊¤冻杩欎簺鏉′欢涔嬩竴锛屽嚱鏁板皢妫鏌ョ储寮昳ndex鏄惁灏忎簬kCharactersNumber锛堣繖鍙兘鏄竴涓瀹氫箟鐨勫瓧绗﹂泦澶у皬锛夈 + // 濡傛灉鏄紝鍒欏皢绱㈠紩瀵瑰簲鐨勫瓧绗︿綔涓烘爣绛撅紱鍚﹀垯锛屼娇鐢ㄩ敭鍊煎kv_鏌ユ壘绱㈠紩瀵瑰簲鐨勭渷浠斤紝骞跺皢璇ョ储寮曞搴旂殑瀛楃鍜岀渷浠戒綔涓烘爣绛俱 + // 鏈鍚庯紝鍑芥暟杩斿洖true琛ㄧず杈撳叆鐨勫浘鍍忔槸涓涓瓧绗︼紝鍚﹀垯杩斿洖false + if (maxVal >= 0.9 || (isChinese && maxVal >= chineseMaxThresh)) + { + if (index < kCharactersNumber) + { + label = std::make_pair(kChars[index], kChars[index]).second; + } + else + { + const char *key = kChars[index]; + std::string s = key; + std::string province = kv_->get(s); + label = std::make_pair(s, province).second; + } + return true; + } + else + return false; } - // 创建一个输出矩阵(output),然后使用预先训练好的模型(annGray_)对特征进行预测,并将结果存储在output中 - cv::Mat output(charVecSize, kChineseNumber, CV_32FC1); - annGray_->predict(featureRows, output); - - // 对于输出矩阵中的每一行(每个字符的预测结果), - // 如果该字符是中文字符,函数会从预测结果的后面部分开始查找最大值,并记录其索引和值。 - for (size_t output_index = 0; output_index < charVecSize; output_index++) { - CCharacter& character = charVec[output_index]; - Mat output_row = output.row(output_index); - bool isChinese = true; - + // 鐢ㄤ簬璇嗗埆杈撳叆鐨勫浘鍍忔暟鎹槸鍚︽槸涓涓腑鏂囧瓧绗︺ + std::pair CharsIdentify::identifyChinese(cv::Mat input, float &out, bool &isChinese) + { + // 璋冪敤charFeatures鍑芥暟鎻愬彇杈撳叆鍥惧儚鐨勭壒寰侊紝骞跺瓨鍌ㄥ湪feature鍙橀噺涓 + // 鐒跺悗锛屽畠璋冪敤棰勫厛璁粌濂界殑妯″瀷annChinese_杩涜棰勬祴锛屽苟灏嗛娴嬬粨鏋滃瓨鍌ㄥ湪output鍙橀噺涓 + cv::Mat feature = charFeatures(input, kChineseSize); float maxVal = -2; int result = 0; - for (int j = 0; j < kChineseNumber; j++) { - float val = output_row.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { + cv::Mat output(1, kChineseNumber, CV_32FC1); + annChinese_->predict(feature, output); + + // 閬嶅巻杈撳嚭鏁扮粍锛屾壘鍒版渶澶х殑鍊煎強鍏剁储寮曘 + // 濡傛灉鏈澶у煎ぇ浜0.9锛屽垯灏唅sChinese璁剧疆涓簍rue锛岃〃绀鸿緭鍏ョ殑瀛楃鍙兘鏄腑鏂囥 + for (int j = 0; j < kChineseNumber; j++) + { + float val = output.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { maxVal = val; result = j; } } // no match - if (-1 == result) { + // 濡傛灉绱㈠紩鍊间负-1锛堝嵆娌℃湁鍖归厤鐨勫瓧绗︼級锛 + // 鍒欏皢result璁剧疆涓0锛宮axVal璁剧疆涓0锛屽苟灏唅sChinese璁剧疆涓篺alse锛岃〃绀鸿緭鍏ョ殑瀛楃涓嶆槸涓枃銆 + if (-1 == result) + { result = 0; maxVal = 0; isChinese = false; } - - // 根据这个最大值和索引确定预测的字符。 - // 这是通过查找kChars数组实现的,其中kChars可能是一个预定义的字符集。 + else if (maxVal > 0.9) + { + isChinese = true; + } + // 閫氳繃绱㈠紩鍊艰幏鍙栧瓧绗︾殑鏍囩鍜岀渷浠斤紝骞跺皢鏈澶у间繚瀛樺埌out涓傚嚱鏁拌繑鍥炰竴涓敱瀛楃鏍囩鍜岀渷浠界粍鎴愮殑pair銆 auto index = result + kCharsTotalNumber - kChineseNumber; - const char* key = kChars[index]; + const char *key = kChars[index]; std::string s = key; std::string province = kv_->get(s); + out = maxVal; - /*std::cout << "result:" << result << std::endl; - std::cout << "maxVal:" << maxVal << std::endl;*/ - - // 将预测的最大值、预测的字符以及对应的省份作为标签, - // 分别设置到对应字符对象的得分、字符串属性和是否为中文字符属性中 - character.setCharacterScore(maxVal); - character.setCharacterStr(province); - character.setIsChinese(isChinese); - } -} - -// 使用OpenCV库和神经网络进行中文字符识别 -void CharsIdentify::classifyChinese(std::vector& charVec){ - size_t charVecSize = charVec.size(); - - if (charVecSize == 0) - return; - - Mat featureRows; - // 通过循环遍历每个字符,提取其特征并将其存储在featureRows中。 - // 这里,charFeatures函数被用于提取每个字符的特性,kChineseSize可能是一个预定义的特性大小。 - for (size_t index = 0; index < charVecSize; index++) { - Mat charInput = charVec[index].getCharacterMat(); - Mat feature = charFeatures(charInput, kChineseSize); - featureRows.push_back(feature); + return std::make_pair(s, province); } - - // 创建一个输出矩阵(output),并使用预先训练好的模型(annChinese_)对特征进行预测。预测结果存储在output中。 - cv::Mat output(charVecSize, kChineseNumber, CV_32FC1); - annChinese_->predict(featureRows, output); - - // 遍历每个预测结果,并对每个结果进行处理。对于每个预测结果,函数查找最大值及其索引。 - // 如果最大值小于或等于-1,则将最大值设置为0,并将result设置为0,同时将isChinese设置为false。 - for (size_t output_index = 0; output_index < charVecSize; output_index++) { - CCharacter& character = charVec[output_index]; - Mat output_row = output.row(output_index); - bool isChinese = true; - + // 浠庤緭鍏ョ殑鍥惧儚锛堝彲鑳芥槸涓涓伆搴﹀浘鍍忥級涓瘑鍒嚭鍙兘鐨勪腑鏂囧瓧绗︺ + std::pair CharsIdentify::identifyChineseGray(cv::Mat input, float &out, bool &isChinese) + { + cv::Mat feature; + // 閫氳繃extractFeature鍑芥暟鎻愬彇杈撳叆鍥惧儚鐨勭壒寰侊紝骞跺皢鐗瑰緛淇濆瓨鍦╢eature鍙橀噺涓 + // 鐒跺悗锛屽畠浣跨敤棰勫厛璁粌濂界殑妯″瀷annGray_杩涜棰勬祴锛屽苟灏嗛娴嬬粨鏋滃瓨鍌ㄥ湪output鍙橀噺涓 + extractFeature(input, feature); float maxVal = -2; int result = 0; - - for (int j = 0; j < kChineseNumber; j++) { - float val = output_row.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { + cv::Mat output(1, kChineseNumber, CV_32FC1); + annGray_->predict(feature, output); + // 閬嶅巻杈撳嚭鏁扮粍锛屾壘鍒版渶澶х殑鍊煎強鍏剁储寮曘 + // 濡傛灉鏈澶у煎ぇ浜0.9锛屽垯灏唅sChinese璁剧疆涓簍rue锛岃〃绀鸿緭鍏ョ殑瀛楃鍙兘鏄腑鏂囥 + for (int j = 0; j < kChineseNumber; j++) + { + float val = output.at(j); + // std::cout << "j:" << j << "val:" << val << std::endl; + if (val > maxVal) + { maxVal = val; result = j; } } - // no match - if (-1 == result) { + // 濡傛灉绱㈠紩鍊间负-1锛堝嵆娌℃湁鍖归厤鐨勫瓧绗︼級锛 + // 鍒欏皢result璁剧疆涓0锛宮axVal璁剧疆涓0锛屽苟灏唅sChinese璁剧疆涓篺alse锛岃〃绀鸿緭鍏ョ殑瀛楃涓嶆槸涓枃 + if (-1 == result) + { result = 0; maxVal = 0; isChinese = false; } - - // 计算索引值,并使用该索引从kChars数组中获取对应的字符。 - // 同时,通过键值对(kv_)查找与该字符对应的省份。 + else if (maxVal > 0.9) + { + isChinese = true; + } + // 閫氳繃绱㈠紩鍊艰幏鍙栧瓧绗︾殑鏍囩鍜岀渷浠斤紝骞跺皢鏈澶у间繚瀛樺埌out涓傚嚱鏁拌繑鍥炰竴涓敱瀛楃鏍囩鍜岀渷浠界粍鎴愮殑pair銆 auto index = result + kCharsTotalNumber - kChineseNumber; - const char* key = kChars[index]; + const char *key = kChars[index]; std::string s = key; std::string province = kv_->get(s); - - /*std::cout << "result:" << result << std::endl; - std::cout << "maxVal:" << maxVal << std::endl;*/ - - // 将最大值、省份和isChinese作为标签,分别设置到对应字符对象的得分、字符串属性和是否为中文字符属性中。 - character.setCharacterScore(maxVal); - character.setCharacterStr(province); - character.setIsChinese(isChinese); - } -} - - -// 对输入的图像数据进行分类 -int CharsIdentify::classify(cv::Mat f, float& maxVal, bool isChinses, bool isAlphabet){ - int result = 0; - // 调用预先训练好的模型(ann_)进行预测,并将预测结果存储在output变量中。 - cv::Mat output(1, kCharsTotalNumber, CV_32FC1); - ann_->predict(f, output); - - // 查找最大值及其索引。如果图像数据不是中文,则会检查它是否是字母。 - // 如果它是字母,那么函数将只查找字母范围内的值(从10开始,对应于'A')。 - // 否则,它将查找所有字符范围内的值。如果图像数据是中文,则函数将查找中文字符范围内的值 - maxVal = -2.f; - if (!isChinses) { - if (!isAlphabet) { - result = 0; - for (int j = 0; j < kCharactersNumber; j++) { - float val = output.at(j); - // std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; - } - } - } - else { - result = 0; - // begin with 11th char, which is 'A' - for (int j = 10; j < kCharactersNumber; j++) { - float val = output.at(j); - // std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; - } - } - } - } - else { - result = kCharactersNumber; - for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) { - float val = output.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; - } - } - } - // 返回索引值result,该值是预测的字符在预先定义的字符集kChars中的索引。 - // 同时,它也将最大值maxVal和对应的索引result设置到输入的float引用maxVal中,以便调用者可以访问这些值。 - //std::cout << "maxVal:" << maxVal << std::endl; - return result; -} -// 根据输入的图像数据判断它是否是一个字符(特别是中文字符) -bool CharsIdentify::isCharacter(cv::Mat input, std::string& label, float& maxVal, bool isChinese) { - // 调用charFeatures函数提取输入图像的特征,并存储在feature变量中。 - // 然后,它调用classify函数对特征进行分类,得到一个索引值index - cv::Mat feature = charFeatures(input, kPredictSize); - auto index = static_cast(classify(feature, maxVal, isChinese)); - - if (isChinese) { - //std::cout << "maxVal:" << maxVal << std::endl; + out = maxVal; + return std::make_pair(s, province); } - float chineseMaxThresh = 0.2f; - // 检查预测的最大值maxVal是否大于等于0.9,或者如果输入的字符是中文且最大值大于等于chineseMaxThresh(这个阈值被设置为0.2)。 - // 如果满足这些条件之一,函数将检查索引index是否小于kCharactersNumber(这可能是一个预定义的字符集大小)。 - // 如果是,则将索引对应的字符作为标签;否则,使用键值对kv_查找索引对应的省份,并将该索引对应的字符和省份作为标签。 - // 最后,函数返回true表示输入的图像是一个字符,否则返回false - if (maxVal >= 0.9 || (isChinese && maxVal >= chineseMaxThresh)) { - if (index < kCharactersNumber) { - label = std::make_pair(kChars[index], kChars[index]).second; + // 鐢ㄤ簬璇嗗埆杈撳叆鐨勫浘鍍忔暟鎹槸鍚︽槸涓涓瓧绗︺ + std::pair CharsIdentify::identify(cv::Mat input, bool isChinese, bool isAlphabet) + { + // 杩囪皟鐢╟harFeatures鍑芥暟鎻愬彇杈撳叆鍥惧儚鐨勭壒寰侊紝骞跺瓨鍌ㄥ湪feature鍙橀噺涓 + // 鐒跺悗锛屽畠璋冪敤棰勫厛璁粌濂界殑妯″瀷classify杩涜棰勬祴锛屽苟灏嗛娴嬬粨鏋滃瓨鍌ㄥ湪index鍙橀噺涓 + cv::Mat feature = charFeatures(input, kPredictSize); + float maxVal = -2; + auto index = static_cast(classify(feature, maxVal, isChinese, isAlphabet)); + // 妫鏌ョ储寮曞糹ndex鏄惁灏忎簬瀛楃闆嗗ぇ灏弅CharactersNumber銆傚鏋滄槸锛屽垯杩斿洖鐢辩浉鍚屽瓧绗︾粍鎴愮殑pair锛 + // 鍚﹀垯锛岃幏鍙栫储寮曞搴旂殑瀛楃浣滀负閿紝骞朵娇鐢ㄩ敭鍊煎kv_鏌ユ壘瀵瑰簲鐨勭渷浠姐 + if (index < kCharactersNumber) + { + return std::make_pair(kChars[index], kChars[index]); } - else { - const char* key = kChars[index]; + else + { + const char *key = kChars[index]; std::string s = key; std::string province = kv_->get(s); - label = std::make_pair(s, province).second; - } - return true; - } - else - return false; -} -// 用于识别输入的图像数据是否是一个中文字符。 -std::pair CharsIdentify::identifyChinese(cv::Mat input, float& out, bool& isChinese) { - // 调用charFeatures函数提取输入图像的特征,并存储在feature变量中。 - // 然后,它调用预先训练好的模型annChinese_进行预测,并将预测结果存储在output变量中。 - cv::Mat feature = charFeatures(input, kChineseSize); - float maxVal = -2; - int result = 0; - - cv::Mat output(1, kChineseNumber, CV_32FC1); - annChinese_->predict(feature, output); - - // 遍历输出数组,找到最大的值及其索引。 - // 如果最大值大于0.9,则将isChinese设置为true,表示输入的字符可能是中文。 - for (int j = 0; j < kChineseNumber; j++) { - float val = output.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; + return std::make_pair(s, province); } } - // no match - // 如果索引值为-1(即没有匹配的字符), - // 则将result设置为0,maxVal设置为0,并将isChinese设置为false,表示输入的字符不是中文。 - if (-1 == result) { - result = 0; - maxVal = 0; - isChinese = false; - } - else if (maxVal > 0.9){ - isChinese = true; - } - // 通过索引值获取字符的标签和省份,并将最大值保存到out中。函数返回一个由字符标签和省份组成的pair。 - auto index = result + kCharsTotalNumber - kChineseNumber; - const char* key = kChars[index]; - std::string s = key; - std::string province = kv_->get(s); - out = maxVal; - - return std::make_pair(s, province); -} -// 从输入的图像(可能是一个灰度图像)中识别出可能的中文字符。 -std::pair CharsIdentify::identifyChineseGray(cv::Mat input, float& out, bool& isChinese) { - cv::Mat feature; - // 通过extractFeature函数提取输入图像的特征,并将特征保存在feature变量中。 - // 然后,它使用预先训练好的模型annGray_进行预测,并将预测结果存储在output变量中。 - extractFeature(input, feature); - float maxVal = -2; - int result = 0; - cv::Mat output(1, kChineseNumber, CV_32FC1); - annGray_->predict(feature, output); - // 遍历输出数组,找到最大的值及其索引。 - // 如果最大值大于0.9,则将isChinese设置为true,表示输入的字符可能是中文。 - for (int j = 0; j < kChineseNumber; j++) { - float val = output.at(j); - //std::cout << "j:" << j << "val:" << val << std::endl; - if (val > maxVal) { - maxVal = val; - result = j; + // 鐢ㄤ簬澶勭悊涓缁勮緭鍏ョ殑鍥惧儚鏁版嵁骞惰瘑鍒嚭瀵瑰簲鐨勫瓧绗﹀拰鐪佷唤銆 + // 鍑芥暟鍙傛暟鍖呮嫭杈撳叆鍥惧儚鏁版嵁锛坕nputs锛夛紝杈撳嚭缁撴灉锛坥utputs锛変互鍙婁竴涓竷灏斿煎悜閲忥紙isChineseVec锛 + int CharsIdentify::identify(std::vector inputs, std::vector> &outputs, + std::vector isChineseVec) + { + // featureRows鍒涘缓涓涓┖鐨凪at瀵硅薄銆傚畠灏嗚鐢ㄤ簬瀛樺偍鎵鏈夎緭鍏ュ浘鍍忕殑鐗瑰緛銆 + Mat featureRows; + size_t input_size = inputs.size(); + // 姣忎竴寮犲浘鍍忔彁鍙栫壒寰侊紝骞跺皢杩欎簺鐗瑰緛娣诲姞鍒癴eatureRows涓 + for (size_t i = 0; i < input_size; i++) + { + Mat input = inputs[i]; + cv::Mat feature = charFeatures(input, kPredictSize); + featureRows.push_back(feature); } - } - // no match - // 如果索引值为-1(即没有匹配的字符), - // 则将result设置为0,maxVal设置为0,并将isChinese设置为false,表示输入的字符不是中文 - if (-1 == result) { - result = 0; - maxVal = 0; - isChinese = false; - } else if (maxVal > 0.9){ - isChinese = true; - } - // 通过索引值获取字符的标签和省份,并将最大值保存到out中。函数返回一个由字符标签和省份组成的pair。 - auto index = result + kCharsTotalNumber - kChineseNumber; - const char* key = kChars[index]; - std::string s = key; - std::string province = kv_->get(s); - out = maxVal; - return std::make_pair(s, province); -} - -// 用于识别输入的图像数据是否是一个字符。 -std::pair CharsIdentify::identify(cv::Mat input, bool isChinese, bool isAlphabet) { - // 过调用charFeatures函数提取输入图像的特征,并存储在feature变量中。 - // 然后,它调用预先训练好的模型classify进行预测,并将预测结果存储在index变量中。 - cv::Mat feature = charFeatures(input, kPredictSize); - float maxVal = -2; - auto index = static_cast(classify(feature, maxVal, isChinese, isAlphabet)); - // 检查索引值index是否小于字符集大小kCharactersNumber。如果是,则返回由相同字符组成的pair; - // 否则,获取索引对应的字符作为键,并使用键值对kv_查找对应的省份。 - 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); - } -} -// 用于处理一组输入的图像数据并识别出对应的字符和省份。 -// 函数参数包括输入图像数据(inputs),输出结果(outputs)以及一个布尔值向量(isChineseVec) -int CharsIdentify::identify(std::vector inputs, std::vector>& outputs, - std::vector isChineseVec) { - // featureRows创建一个空的Mat对象。它将被用于存储所有输入图像的特征。 - Mat featureRows; - size_t input_size = inputs.size(); - // 每一张图像提取特征,并将这些特征添加到featureRows中。 - for (size_t i = 0; i < input_size; i++) { - Mat input = inputs[i]; - cv::Mat feature = charFeatures(input, kPredictSize); - featureRows.push_back(feature); - } - - std::vector maxIndexs; - std::vector maxVals; - // 调用classify函数,输入特征矩阵featureRows,并返回最大值的索引(maxIndexs)和最大值(maxVals)。 - // 同时,根据这些最大值,更新isChineseVec中的对应元素。 - classify(featureRows, maxIndexs, maxVals, isChineseVec); - // 遍历所有的输入图像,对于每一张图像,根据其对应的最大值索引,构造一个输出对,并存储在outputs中。 - // 如果索引小于字符集大小kCharactersNumber,则输出对由相同字符组成; - // 否则,获取索引对应的字符作为键,并使用键值对kv_查找对应的省份。 - - for (size_t row_index = 0; row_index < input_size; row_index++) { - int index = maxIndexs[row_index]; - if (index < kCharactersNumber) { - outputs[row_index] = std::make_pair(kChars[index], kChars[index]); - } - else { - const char* key = kChars[index]; - std::string s = key; - std::string province = kv_->get(s); - outputs[row_index] = std::make_pair(s, province); + std::vector maxIndexs; + std::vector maxVals; + // 璋冪敤classify鍑芥暟锛岃緭鍏ョ壒寰佺煩闃礷eatureRows锛屽苟杩斿洖鏈澶у肩殑绱㈠紩锛坢axIndexs锛夊拰鏈澶у硷紙maxVals锛夈 + // 鍚屾椂锛屾牴鎹繖浜涙渶澶у硷紝鏇存柊isChineseVec涓殑瀵瑰簲鍏冪礌銆 + classify(featureRows, maxIndexs, maxVals, isChineseVec); + // 閬嶅巻鎵鏈夌殑杈撳叆鍥惧儚锛屽浜庢瘡涓寮犲浘鍍忥紝鏍规嵁鍏跺搴旂殑鏈澶у肩储寮曪紝鏋勯犱竴涓緭鍑哄锛屽苟瀛樺偍鍦╫utputs涓 + // 濡傛灉绱㈠紩灏忎簬瀛楃闆嗗ぇ灏弅CharactersNumber锛屽垯杈撳嚭瀵圭敱鐩稿悓瀛楃缁勬垚锛 + // 鍚﹀垯锛岃幏鍙栫储寮曞搴旂殑瀛楃浣滀负閿紝骞朵娇鐢ㄩ敭鍊煎kv_鏌ユ壘瀵瑰簲鐨勭渷浠姐 + + for (size_t row_index = 0; row_index < input_size; row_index++) + { + int index = maxIndexs[row_index]; + if (index < kCharactersNumber) + { + outputs[row_index] = std::make_pair(kChars[index], kChars[index]); + } + else + { + const char *key = kChars[index]; + std::string s = key; + std::string province = kv_->get(s); + outputs[row_index] = std::make_pair(s, province); + } } + return 0; } - return 0; -} } diff --git a/src/src/core/core_func.cpp b/src/src/core/core_func.cpp index a4e419e..bb8d143 100644 --- a/src/src/core/core_func.cpp +++ b/src/src/core/core_func.cpp @@ -6,47 +6,56 @@ #include "thirdparty/mser/mser2.hpp" #include -namespace easypr { - Mat colorMatch(const Mat &src, Mat &match, const Color r, - const bool adaptive_minsv) { +namespace easypr +{ + + // 鐢ㄤ簬鍦℉SV棰滆壊绌洪棿涓尮閰嶇壒瀹氱殑棰滆壊銆傚畠鍙互鐢ㄤ簬璇嗗埆鍥惧儚涓殑钃濊壊銆侀粍鑹插拰鐧借壊鍖哄煙 + Mat colorMatch(const Mat &src, Mat &match, const Color r, // 鍑芥暟鎺ュ彈鍥涗釜鍙傛暟锛氫竴涓簮鍥惧儚src锛屼竴涓緭鍑哄浘鍍弇atch锛屼竴涓鑹叉灇涓緍锛屽拰涓涓竷灏斿糰daptive_minsv + const bool adaptive_minsv) + { // if use adaptive_minsv // min value of s and v is adaptive to h + // 瀹氫箟浜嗕竴浜涘父閲忥紝鍖呮嫭鏈澶х殑楗卞拰搴﹀拰浜害鍊糾ax_sv锛屽弬鑰冪殑鏈灏忛ケ鍜屽害鍜屼寒搴﹀糾inref_sv锛屽拰缁濆鐨勬渶灏忛ケ鍜屽害鍜屼寒搴﹀糾inabs_sv const float max_sv = 255; const float minref_sv = 64; - const float minabs_sv = 95; //95; + const float minabs_sv = 95; // 95; - // H range of blue + // 瀹氫箟浜嗚摑鑹层侀粍鑹插拰鐧借壊鍦℉SV棰滆壊绌洪棿涓殑H鍊艰寖鍥 + // H range of blue - const int min_blue = 100; // 100 - const int max_blue = 140; // 140 + const int min_blue = 100; // 100 + const int max_blue = 140; // 140 // H range of yellow - const int min_yellow = 15; // 15 - const int max_yellow = 40; // 40 + const int min_yellow = 15; // 15 + const int max_yellow = 40; // 40 // H range of white - const int min_white = 0; // 15 - const int max_white = 30; // 40 + const int min_white = 0; // 15 + const int max_white = 30; // 40 + // 灏嗘簮鍥惧儚浠嶣GR棰滆壊绌洪棿杞崲鍒癏SV棰滆壊绌洪棿 Mat src_hsv; // convert to HSV space cvtColor(src, src_hsv, CV_BGR2HSV); + // 灏咹SV鍥惧儚鍒嗗壊鎴愪笁涓氶亾锛屽V閫氶亾杩涜鐩存柟鍥惧潎琛″寲锛岀劧鍚庡啀鍚堝苟鍥炲幓 std::vector hsvSplit; split(src_hsv, hsvSplit); equalizeHist(hsvSplit[2], hsvSplit[2]); merge(hsvSplit, src_hsv); // match to find the color - + // 鏍规嵁杈撳叆鐨勯鑹叉灇涓緍锛岃缃瓾鍊肩殑鑼冨洿 int min_h = 0; int max_h = 0; - switch (r) { + switch (r) + { case BLUE: min_h = min_blue; max_h = max_blue; @@ -64,38 +73,54 @@ namespace easypr { break; } + // 璁$畻H鍊肩殑宸煎拰骞冲潎鍊 float diff_h = float((max_h - min_h) / 2); float avg_h = min_h + diff_h; + // 鑾峰彇鍥惧儚鐨勯氶亾鏁般佽鏁板拰鍒楁暟銆傚鏋滃浘鍍忔槸杩炵画鐨勶紝閭d箞灏嗗垪鏁颁箻浠ヨ鏁帮紝琛屾暟璁剧疆涓1 int channels = src_hsv.channels(); int nRows = src_hsv.rows; // consider multi channel image int nCols = src_hsv.cols * channels; - if (src_hsv.isContinuous()) { + if (src_hsv.isContinuous()) + { nCols *= nRows; nRows = 1; } + // 瀵瑰浘鍍忚繘琛岄亶鍘嗭紝瀵规瘡涓儚绱犺繘琛岄鑹插尮閰嶃傚鏋滃儚绱犵殑棰滆壊鍖归厤鎴愬姛锛岄偅涔堝皢鍏惰缃负绾㈣壊锛255锛夛紝鍚﹀垯璁剧疆涓洪粦鑹诧紙0锛 + // 瀹氫箟鍙橀噺鐢ㄤ簬鍚庣画鐨勮绠楀拰寰幆 int i, j; - uchar* p; + uchar *p; float s_all = 0; float v_all = 0; float count = 0; - for (i = 0; i < nRows; ++i) { + + // 鍙岄噸寰幆锛岄亶鍘嗗浘鍍忕殑姣忎釜鍍忕礌銆傚浜庢瘡涓儚绱狅紝鎴戜滑鑾峰彇鍏跺湪HSV棰滆壊绌洪棿涓殑H锛堣壊鐩革級銆丼锛堥ケ鍜屽害锛夊拰V锛堜寒搴︼級鍊 + for (i = 0; i < nRows; ++i) + { p = src_hsv.ptr(i); - for (j = 0; j < nCols; j += 3) { - int H = int(p[j]); // 0-180 - int S = int(p[j + 1]); // 0-255 - int V = int(p[j + 2]); // 0-255 + for (j = 0; j < nCols; j += 3) + { + int H = int(p[j]); // 0-180 + int S = int(p[j + 1]); // 0-255 + int V = int(p[j + 2]); // 0-255 + // 姝ゅ璁$畻浜嗘墍鏈夊儚绱犵殑S鍜孷鍊肩殑鎬诲拰锛屼互鍙婂儚绱犵殑鏁伴噺 s_all += S; v_all += V; count++; + // 浠ヤ笅杩欐浠g爜鏄鑹插尮閰嶇殑鏍稿績閮ㄥ垎銆 + // 棣栧厛锛屾鏌ュ儚绱犵殑H鍊兼槸鍚﹀湪棰勮鐨勮寖鍥村唴銆 + // 濡傛灉鍦ㄨ寖鍥村唴锛岃绠桯鍊间笌骞冲潎H鍊肩殑宸硷紝鐒跺悗鏍规嵁杩欎釜宸煎拰棰勮鐨勯槇鍊兼潵璁$畻鏈灏忕殑S鍜孷鍊笺 + // 濡傛灉鍍忕礌鐨凷鍜孷鍊奸兘鍦ㄨ繖涓寖鍥村唴锛岃涓鸿繖涓儚绱犵殑棰滆壊鍖归厤鎴愬姛銆 bool colorMatched = false; - if (H > min_h && H < max_h) { + if (H > min_h && H < max_h) + { ////濡傛灉鍦ㄨ寖鍥村唴锛岃绠桯鍊间笌骞冲潎H鍊肩殑宸 + float Hdiff = 0; if (H > avg_h) Hdiff = H - avg_h; @@ -105,25 +130,27 @@ namespace easypr { float Hdiff_p = float(Hdiff) / diff_h; float min_sv = 0; - if (true == adaptive_minsv) + if (true == adaptive_minsv) // 鐒跺悗鏍规嵁杩欎釜宸煎拰棰勮鐨勯槇鍊兼潵璁$畻鏈灏忕殑S鍜孷鍊 min_sv = - minref_sv - - minref_sv / 2 * - (1 - - Hdiff_p); // inref_sv - minref_sv / 2 * (1 - Hdiff_p) + minref_sv - + minref_sv / 2 * + (1 - Hdiff_p); // inref_sv - minref_sv / 2 * (1 - Hdiff_p) else - min_sv = minabs_sv; // add + min_sv = minabs_sv; // add - if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv)) + if ((S > min_sv && S < max_sv) && (V > min_sv && V < max_sv)) // 濡傛灉鍍忕礌鐨凷鍜孷鍊奸兘鍦ㄨ繖涓寖鍥村唴锛岃涓鸿繖涓儚绱犵殑棰滆壊鍖归厤鎴愬姛 colorMatched = true; } - if (colorMatched == true) { + // 濡傛灉鍍忕礌鐨勯鑹插尮閰嶆垚鍔燂紝灏嗗叾璁剧疆涓虹孩鑹诧紙鍦℉SV棰滆壊绌洪棿涓紝H=0锛孲=0锛孷=255琛ㄧず绾㈣壊锛夈傚惁鍒欙紝灏嗗叾璁剧疆涓洪粦鑹诧紙H=0锛孲=0锛孷=0锛 + if (colorMatched == true) + { p[j] = 0; p[j + 1] = 0; p[j + 2] = 255; } - else { + else + { p[j] = 0; p[j + 1] = 0; p[j + 2] = 0; @@ -135,7 +162,8 @@ namespace easypr { // cout << "avg_v:" << v_all / count << endl; // get the final binary - + // 灏嗗鐞嗗悗鐨凥SV鍥惧儚鍒嗗壊鎴愪笁涓氶亾锛屽彇鍑篤閫氶亾浣滀负鐏板害鍥惧儚锛屽苟杩斿洖杩欎釜鐏板害鍥惧儚 + // 鍦ㄨ繖涓伆搴﹀浘鍍忎腑锛屽尮閰嶇殑棰滆壊鍖哄煙琚爣璁颁负鐧借壊锛屽叾浣欏尯鍩熶负榛戣壊 Mat src_grey; std::vector hsvSplit_done; split(src_hsv, hsvSplit_done); @@ -143,45 +171,64 @@ namespace easypr { match = src_grey; - return src_grey; + return src_grey; // 鍑芥暟杩斿洖涓涓伆搴﹀浘鍍忥紝鍏朵腑鍖归厤鐨勯鑹插尯鍩熻鏍囪涓虹櫧鑹诧紝鍏朵綑鍖哄煙涓洪粦鑹 } - bool bFindLeftRightBound1(Mat &bound_threshold, int &posLeft, int &posRight) { - + // 璇ュ嚱鏁扮敤浜庡湪鍥惧儚涓壘鍒板乏鍙宠竟鐣屻傚畠閫氳繃璁$畻鍥惧儚涓殑鐧借壊鍍忕礌鏁伴噺鏉ョ‘瀹氳竟鐣 + // 鍑芥暟鎺ュ彈涓涓狹at绫诲瀷鐨勫紩鐢╞ound_threshold锛屼互鍙婁袱涓暣鍨嬪紩鐢╬osLeft鍜宲osRight浣滀负鍙傛暟 + bool bFindLeftRightBound1(Mat &bound_threshold, int &posLeft, int &posRight) + { + // 棣栧厛锛岃绠楀浘鍍忚鏁扮殑20%锛屼綔涓轰竴涓法搴pan float span = bound_threshold.rows * 0.2f; - for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) { + // 鐒跺悗锛屼粠宸﹀埌鍙抽亶鍘嗗浘鍍忥紝姣忔璺宠繃3鍒楋紝璁$畻姣忎釜璺ㄥ害鍐呯殑鐧借壊鍍忕礌鏁伴噺 + for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) + { int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l < i + span; l++) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l < i + span; l++) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.15) { + // 濡傛灉鐧借壊鍍忕礌鐨勬瘮渚嬭秴杩15%锛屽垯璁や负鎵惧埌浜嗗乏杈圭晫 + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.15) + { posLeft = i; break; } } - span = bound_threshold.rows * 0.2f; - + span = bound_threshold.rows * 0.2f; // 鍐嶆璁$畻璺ㄥ害锛岀敤浜庡鎵惧彸杈圭晫 - for (int i = bound_threshold.cols - 1; i > span; i -= 2) { + // 鎺ョ潃锛屼粠鍙冲埌宸﹂亶鍘嗗浘鍍忥紝姣忔璺宠繃2鍒楋紝璁$畻姣忎釜璺ㄥ害鍐呯殑鐧借壊鍍忕礌鏁伴噺 + for (int i = bound_threshold.cols - 1; i > span; i -= 2) + { int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l > i - span; l--) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l > i - span; l--) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.06) { + // 濡傛灉鐧借壊鍍忕礌鐨勬瘮渚嬭秴杩6%锛屽垯璁や负鎵惧埌浜嗗彸杈圭晫 + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.06) + { posRight = i; - if (posRight + 5 < bound_threshold.cols) { + if (posRight + 5 < bound_threshold.cols) + { // 濡傛灉鍙宠竟鐣屽姞5鍚庝粛鍦ㄥ浘鍍忓垪鏁拌寖鍥村唴锛屽垯鍙宠竟鐣屽姞5 posRight = posRight + 5; - } else { + } + else + { // 鍚﹀垯鍙宠竟鐣岃涓哄浘鍍忓垪鏁板噺1 posRight = bound_threshold.cols - 1; } @@ -189,139 +236,190 @@ namespace easypr { } } - if (posLeft < posRight) { + // 鏈鍚庯紝濡傛灉宸﹁竟鐣屽皬浜庡彸杈圭晫锛岃繑鍥炵湡锛屽惁鍒欒繑鍥炲亣 + if (posLeft < posRight) + { return true; } return false; } - bool bFindLeftRightBound(Mat &bound_threshold, int &posLeft, int &posRight) { - + // 璇ュ嚱鏁扮敤浜庡湪鍥惧儚涓壘鍒板乏鍙宠竟鐣屻傚畠閫氳繃璁$畻鍥惧儚涓殑鐧借壊鍍忕礌鏁伴噺鏉ョ‘瀹氳竟鐣 + // 鍑芥暟鎺ュ彈涓変釜鍙傛暟锛氫竴涓狹at绫诲瀷鐨勫紩鐢╞ound_threshold锛屼互鍙婁袱涓暣鍨嬪紩鐢╬osLeft鍜宲osRight + // 鍑芥暟杩斿洖涓涓竷灏斿硷紝琛ㄧず鏄惁鎴愬姛鎵惧埌宸﹀彸杈圭晫 + bool bFindLeftRightBound(Mat &bound_threshold, int &posLeft, int &posRight) + { + // 棣栧厛锛岃绠楀浘鍍忚鏁扮殑20%锛屼綔涓轰竴涓法搴pan float span = bound_threshold.rows * 0.2f; - - for (int i = 0; i < bound_threshold.cols - span - 1; i += 2) { + // 鐒跺悗锛屼粠宸﹀埌鍙抽亶鍘嗗浘鍍忥紝姣忔璺宠繃2鍒楋紝璁$畻姣忎釜璺ㄥ害鍐呯殑鐧借壊鍍忕礌鏁伴噺銆傚鏋滅櫧鑹插儚绱犵殑姣斾緥瓒呰繃36%锛屽垯璁や负鎵惧埌浜嗗乏杈圭晫 + for (int i = 0; i < bound_threshold.cols - span - 1; i += 2) + { int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l < i + span; l++) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l < i + span; l++) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.36) { + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.36) + { posLeft = i; break; } } - span = bound_threshold.rows * 0.2f; - + span = bound_threshold.rows * 0.2f; // 鍐嶆璁$畻璺ㄥ害锛岀敤浜庡鎵惧彸杈圭晫 - for (int i = bound_threshold.cols - 1; i > span; i -= 2) { + // 鎺ョ潃锛屼粠鍙冲埌宸﹂亶鍘嗗浘鍍忥紝姣忔璺宠繃2鍒楋紝璁$畻姣忎釜璺ㄥ害鍐呯殑鐧借壊鍍忕礌鏁伴噺銆傚鏋滅櫧鑹插儚绱犵殑姣斾緥瓒呰繃26%锛屽垯璁や负鎵惧埌浜嗗彸杈圭晫 + for (int i = bound_threshold.cols - 1; i > span; i -= 2) + { int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l > i - span; l--) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l > i - span; l--) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.26) { + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.26) + { posRight = i; break; } } - - if (posLeft < posRight) { + // 鏈鍚庯紝濡傛灉宸﹁竟鐣屽皬浜庡彸杈圭晫锛岃繑鍥炵湡锛屽惁鍒欒繑鍥炲亣 + if (posLeft < posRight) + { return true; } return false; } - bool bFindLeftRightBound2(Mat &bound_threshold, int &posLeft, int &posRight) { - + // 璇ュ嚱鏁版帴鍙椾竴涓狹at绫诲瀷鐨勫紩鐢╞ound_threshold鍜屼袱涓暣鍨嬪紩鐢╬osLeft鍜宲osRight浣滀负鍙傛暟 + // 鍑芥暟杩斿洖涓涓竷灏斿硷紝琛ㄧず鏄惁鎴愬姛鎵惧埌宸﹀彸杈圭晫 + bool bFindLeftRightBound2(Mat &bound_threshold, int &posLeft, int &posRight) + { + // 璁$畻鍥惧儚琛屾暟鐨20%锛屼綔涓轰竴涓法搴pan float span = bound_threshold.rows * 0.2f; - for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) { + for (int i = 0; i < bound_threshold.cols - span - 1; i += 3) + { // 浠庡乏鍒板彸閬嶅巻鍥惧儚锛屾瘡娆¤烦杩3鍒 + // 鍦ㄦ瘡涓法搴﹀唴锛岃绠楃櫧鑹插儚绱犵殑鏁伴噺 int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l < i + span; l++) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l < i + span; l++) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.32) { + // 濡傛灉鐧借壊鍍忕礌鐨勬瘮渚嬭秴杩32%锛屽垯璁や负鎵惧埌浜嗗乏杈圭晫 + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.32) + { posLeft = i; break; } } + // 鍐嶆璁$畻璺ㄥ害锛岀敤浜庡鎵惧彸杈圭晫 span = bound_threshold.rows * 0.2f; - - for (int i = bound_threshold.cols - 1; i > span; i -= 3) { + for (int i = bound_threshold.cols - 1; i > span; i -= 3) + { // 浠庡彸鍒板乏閬嶅巻鍥惧儚锛屾瘡娆¤烦杩3鍒 + // 鍦ㄦ瘡涓法搴﹀唴锛岃绠楃櫧鑹插儚绱犵殑鏁伴噺 int whiteCount = 0; - for (int k = 0; k < bound_threshold.rows; k++) { - for (int l = i; l > i - span; l--) { - if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) { + for (int k = 0; k < bound_threshold.rows; k++) + { + for (int l = i; l > i - span; l--) + { + if (bound_threshold.data[k * bound_threshold.step[0] + l] == 255) + { whiteCount++; } } } - - if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.22) { + // 濡傛灉鐧借壊鍍忕礌鐨勬瘮渚嬭秴杩22%锛屽垯璁や负鎵惧埌浜嗗彸杈圭晫 + if (whiteCount * 1.0 / (span * bound_threshold.rows) > 0.22) + { posRight = i; break; } } - - if (posLeft < posRight) { + // 濡傛灉宸﹁竟鐣屽皬浜庡彸杈圭晫锛岃繑鍥炵湡锛屽惁鍒欒繑鍥炲亣 + if (posLeft < posRight) + { return true; } return false; } - + // 璇ュ嚱鏁版帴鍙椾竴涓狹at绫诲瀷鐨勫紩鐢╯rc锛屼竴涓鑹叉灇涓緍锛屼竴涓竷灏斿糰daptive_minsv锛屽拰涓涓诞鐐规暟寮曠敤percent浣滀负鍙傛暟 + // 鍑芥暟杩斿洖涓涓竷灏斿硷紝琛ㄧず棰滆壊鏄惁鍖归厤 bool plateColorJudge(const Mat &src, const Color r, const bool adaptive_minsv, - float &percent) { - + float &percent) + { + // 瀹氫箟涓涓父閲弔hresh锛岃〃绀洪槇鍊 const float thresh = 0.45f; + // 璋冪敤colorMatch鍑芥暟锛屽皢婧愬浘鍍忚浆鎹负鐏板害鍥惧儚 Mat src_gray; colorMatch(src, src_gray, r, adaptive_minsv); + // 璁$畻鐏板害鍥惧儚涓潪闆跺儚绱犵殑姣斾緥 percent = float(countNonZero(src_gray)) / float(src_gray.rows * src_gray.cols); // cout << "percent:" << percent << endl; + // 濡傛灉闈為浂鍍忕礌鐨勬瘮渚嬪ぇ浜庨槇鍊硷紝杩斿洖鐪燂紝鍚﹀垯杩斿洖鍋 if (percent > thresh) return true; else return false; } - Color getPlateType(const Mat &src, const bool adaptive_minsv) { + // 鐢ㄤ簬鍒ゆ柇鍥惧儚涓殑棰滆壊绫诲瀷锛堣摑鑹层侀粍鑹叉垨鐧借壊锛 + // 鎺ュ彈涓や釜鍙傛暟锛氫竴涓狹at绫诲瀷鐨勫紩鐢╯rc锛堟簮鍥惧儚锛夊拰涓涓竷灏斿糰daptive_minsv + // 鍑芥暟杩斿洖涓涓竷灏斿硷紝琛ㄧず棰滆壊鏄惁鍖归厤銆 + Color getPlateType(const Mat &src, const bool adaptive_minsv) + { + // 瀹氫箟涓や釜鍙橀噺锛歮ax_percent鐢ㄤ簬瀛樺偍鏈澶х殑棰滆壊鍖归厤鐧惧垎姣旓紝max_color鐢ㄤ簬瀛樺偍瀵瑰簲鐨勯鑹层傚垵濮嬪煎垎鍒涓0鍜孶NKNOW float max_percent = 0; Color max_color = UNKNOWN; - + // 瀹氫箟涓変釜鍙橀噺锛岀敤浜庡瓨鍌ㄨ摑鑹层侀粍鑹插拰鐧借壊鐨勫尮閰嶇櫨鍒嗘瘮銆傚垵濮嬪奸兘璁句负0 float blue_percent = 0; float yellow_percent = 0; float white_percent = 0; - - if (plateColorJudge(src, BLUE, adaptive_minsv, blue_percent) == true) { + // 璋冪敤plateColorJudge鍑芥暟锛屽垽鏂簮鍥惧儚涓殑棰滆壊鏄惁涓鸿摑鑹层侀粍鑹叉垨鐧借壊銆傚鏋滄槸锛岃繑鍥炲搴旂殑棰滆壊 + if (plateColorJudge(src, BLUE, adaptive_minsv, blue_percent) == true) + { // cout << "BLUE" << endl; return BLUE; - } else if (plateColorJudge(src, YELLOW, adaptive_minsv, yellow_percent) == - true) { + } + else if (plateColorJudge(src, YELLOW, adaptive_minsv, yellow_percent) == + true) + { // cout << "YELLOW" << endl; return YELLOW; - } else if (plateColorJudge(src, WHITE, adaptive_minsv, white_percent) == - true) { + } + else if (plateColorJudge(src, WHITE, adaptive_minsv, white_percent) == + true) + { // cout << "WHITE" << endl; return WHITE; - } else { - //std::cout << "OTHER" << std::endl; + } + else + { // 濡傛灉婧愬浘鍍忎腑鐨勯鑹叉棦涓嶆槸钃濊壊銆佷篃涓嶆槸榛勮壊銆佷篃涓嶆槸鐧借壊锛岄偅涔堥粯璁よ繑鍥炶摑鑹 + // std::cout << "OTHER" << std::endl; /*max_percent = blue_percent > yellow_percent ? blue_percent : yellow_percent; max_color = blue_percent > yellow_percent ? BLUE : YELLOW; @@ -332,55 +430,79 @@ namespace easypr { } } - void clearLiuDingOnly(Mat &img) { - const int x = 7; - Mat jump = Mat::zeros(1, img.rows, CV_32F); - for (int i = 0; i < img.rows; i++) { - int jumpCount = 0; - int whiteCount = 0; - for (int j = 0; j < img.cols - 1; j++) { - if (img.at(i, j) != img.at(i, j + 1)) jumpCount++; - - if (img.at(i, j) == 255) { + // 璇ュ嚱鏁扮敤浜庢竻闄ゅ浘鍍忎腑鐨勫櫔澹帮紝鎻愰珮鍥惧儚澶勭悊鐨勫噯纭 + // 鎺ュ彈涓涓狹at绫诲瀷鐨勫紩鐢╥mg浣滀负鍙傛暟 + // 璇ュ嚱鏁颁富瑕侀氳繃璁$畻鍥惧儚涓瘡涓琛岀殑璺冲彉娆℃暟锛堝嵆鍍忕礌鍊间粠0鍙樹负255锛屾垨浠255鍙樹负0鐨勬鏁帮級鏉ュ垽鏂槸鍚︿负鍣0锛屽鏋滆烦鍙樻鏁板皬浜庢煇涓槇鍊硷紝閭d箞灏卞皢杩欎竴琛岀殑鎵鏈夊儚绱犲艰涓0锛屽嵆榛戣壊 + void clearLiuDingOnly(Mat &img) + { + const int x = 7; // 璁剧疆闃堝间负7 + Mat jump = Mat::zeros(1, img.rows, CV_32F); // 鍒涘缓涓涓ぇ灏忎负鍥惧儚琛屾暟锛岀被鍨嬩负娴偣鏁扮殑Mat瀵硅薄锛岀敤浜庡瓨鍌ㄦ瘡涓琛岀殑璺冲彉娆℃暟 + for (int i = 0; i < img.rows; i++) + { // 閬嶅巻鍥惧儚鐨勬瘡涓琛 + int jumpCount = 0; // 鍒濆鍖栬烦鍙樻鏁颁负0 + int whiteCount = 0; // 鍒濆鍖栫櫧鑹插儚绱犵殑鏁伴噺涓0 + for (int j = 0; j < img.cols - 1; j++) + { // 閬嶅巻褰撳墠琛岀殑姣忎竴涓儚绱 + if (img.at(i, j) != img.at(i, j + 1)) + jumpCount++; // 濡傛灉褰撳墠鍍忕礌涓庝笅涓涓儚绱犵殑鍊间笉鍚岋紝璺冲彉娆℃暟鍔1 + + if (img.at(i, j) == 255) + { // 濡傛灉褰撳墠鍍忕礌鐨勫间负255锛屽嵆鐧借壊锛岀櫧鑹插儚绱犵殑鏁伴噺鍔1 whiteCount++; } } - - jump.at(i) = (float) jumpCount; + // 灏嗗綋鍓嶈鐨勮烦鍙樻鏁板瓨鍌ㄥ埌jump涓 + jump.at(i) = (float)jumpCount; } - for (int i = 0; i < img.rows; i++) { - if (jump.at(i) <= x) { - for (int j = 0; j < img.cols; j++) { - img.at(i, j) = 0; + for (int i = 0; i < img.rows; i++) + { // 鍐嶆閬嶅巻鍥惧儚鐨勬瘡涓琛 + if (jump.at(i) <= x) + { // 濡傛灉褰撳墠琛岀殑璺冲彉娆℃暟灏忎簬闃堝 + for (int j = 0; j < img.cols; j++) + { // 閬嶅巻褰撳墠琛岀殑姣忎竴涓儚绱 + img.at(i, j) = 0; // 灏嗗儚绱犲艰涓0锛屽嵆榛戣壊 } } } } - bool clearLiuDing(Mat &img) { + // 璇ュ嚱鏁扮敤浜庢竻闄ゅ浘鍍忎腑鐨勫櫔澹 + // 鎺ュ彈涓涓狹at绫诲瀷鐨勫紩鐢╥mg浣滀负鍙傛暟锛岃繑鍥炰竴涓竷灏斿硷紝琛ㄧず鏄惁鎴愬姛娓呴櫎鍣0 + bool clearLiuDing(Mat &img) + { + // 鍒濆鍖栦竴涓诞鐐规暟鍚戦噺fJump锛屼竴涓暣鏁皐hiteCount鐢ㄤ簬璁℃暟鐧借壊鍍忕礌锛屼竴涓父閲弜璁句负7锛屼互鍙婁竴涓叏闆剁殑Mat瀵硅薄jump锛屽ぇ灏忎负1琛岋紝鍒楁暟涓哄浘鍍忕殑琛屾暟 std::vector fJump; int whiteCount = 0; const int x = 7; Mat jump = Mat::zeros(1, img.rows, CV_32F); - for (int i = 0; i < img.rows; i++) { + + // 閬嶅巻鍥惧儚鐨勬瘡涓琛岋紝璁$畻姣忎竴琛岀殑璺冲彉娆℃暟锛堝嵆鍍忕礌鍊间粠0鍙樹负255锛屾垨浠255鍙樹负0鐨勬鏁帮級鍜岀櫧鑹插儚绱犵殑鏁伴噺锛屽皢璺冲彉娆℃暟瀛樺偍鍒癹ump涓 + for (int i = 0; i < img.rows; i++) + { int jumpCount = 0; - for (int j = 0; j < img.cols - 1; j++) { - if (img.at(i, j) != img.at(i, j + 1)) jumpCount++; + for (int j = 0; j < img.cols - 1; j++) + { + if (img.at(i, j) != img.at(i, j + 1)) + jumpCount++; - if (img.at(i, j) == 255) { + if (img.at(i, j) == 255) + { whiteCount++; } } - jump.at(i) = (float) jumpCount; + jump.at(i) = (float)jumpCount; } + // 閬嶅巻jump锛屽皢鍏跺兼坊鍔犲埌fJump涓紝濡傛灉jump鐨勫煎湪16鍒45涔嬮棿锛宨Count鍔1 int iCount = 0; - for (int i = 0; i < img.rows; i++) { + for (int i = 0; i < img.rows; i++) + { fJump.push_back(jump.at(i)); - if (jump.at(i) >= 16 && jump.at(i) <= 45) { + if (jump.at(i) >= 16 && jump.at(i) <= 45) + { // jump condition iCount++; @@ -388,18 +510,25 @@ namespace easypr { } // if not is not plate - if (iCount * 1.0 / img.rows <= 0.40) { + // 濡傛灉iCount鍗犲浘鍍忚鏁扮殑姣斾緥灏忎簬鎴栫瓑浜40%锛屾垨鑰呯櫧鑹插儚绱犲崰鍥惧儚鎬诲儚绱犵殑姣斾緥灏忎簬15%鎴栧ぇ浜50%锛屽垯杩斿洖鍋囷紝琛ㄧず鏈兘鎴愬姛娓呴櫎鍣0 + if (iCount * 1.0 / img.rows <= 0.40) + { return false; } if (whiteCount * 1.0 / (img.rows * img.cols) < 0.15 || - whiteCount * 1.0 / (img.rows * img.cols) > 0.50) { + whiteCount * 1.0 / (img.rows * img.cols) > 0.50) + { return false; } - for (int i = 0; i < img.rows; i++) { - if (jump.at(i) <= x) { - for (int j = 0; j < img.cols; j++) { + // 閬嶅巻鍥惧儚鐨勬瘡涓琛岋紝濡傛灉璇ヨ鐨勮烦鍙樻鏁板皬浜庢垨绛変簬x锛屽垯灏嗚琛岀殑鎵鏈夊儚绱犲艰涓0锛屽嵆榛戣壊銆傛渶鍚庤繑鍥炵湡锛岃〃绀烘垚鍔熸竻闄ゅ櫔澹 + for (int i = 0; i < img.rows; i++) + { + if (jump.at(i) <= x) + { + for (int j = 0; j < img.cols; j++) + { img.at(i, j) = 0; } } @@ -407,296 +536,384 @@ namespace easypr { return true; } - -void clearBorder(const Mat &img, Rect& cropRect) { - int r = img.rows; - int c = img.cols; - Mat boder = Mat::zeros(1, r, CV_8UC1); - const int noJunpCount_thresh = int(0.15f * c); - - // if nojumpcount > - for (int i = 0; i < r; i++) { - int nojumpCount = 0; - int isBorder = 0; - for (int j = 0; j < c - 1; j++) { - if (img.at(i, j) == img.at(i, j + 1)) - nojumpCount++; - if (nojumpCount > noJunpCount_thresh) { - nojumpCount = 0; - isBorder = 1; - break; + // 璇ュ嚱鏁扮敤浜庢竻闄ゅ浘鍍忕殑杈圭晫 + // 鎺ュ彈涓涓狹at绫诲瀷鐨勫浘鍍廼mg鍜屼竴涓猂ect绫诲瀷鐨勮鍓煩褰ropRect浣滀负鍙傛暟 + void clearBorder(const Mat &img, Rect &cropRect) + { + // 鑾峰彇鍥惧儚鐨勮鏁板拰鍒楁暟 + int r = img.rows; + int c = img.cols; + + // 鍒涘缓涓涓叏闆剁殑鍗曢氶亾鐭╅樀boder锛岀敤浜庡瓨鍌ㄦ瘡涓琛屾槸鍚︿负杈圭晫 + Mat boder = Mat::zeros(1, r, CV_8UC1); + + // 璁剧疆涓涓槇鍊硷紝濡傛灉涓琛屼腑鍍忕礌鍊兼病鏈夊彉鍖栫殑娆℃暟瓒呰繃杩欎釜闃堝硷紝閭d箞璁や负杩欎竴琛屾槸杈圭晫 + const int noJunpCount_thresh = int(0.15f * c); + + // if nojumpcount > + // 閬嶅巻鍥惧儚鐨勬瘡涓琛岋紝璁$畻姣忎竴琛屼腑鍍忕礌鍊兼病鏈夊彉鍖栫殑娆℃暟锛屽鏋滆繖涓鏁拌秴杩囬槇鍊硷紝閭d箞璁や负杩欎竴琛屾槸杈圭晫锛屽皢boder瀵瑰簲鐨勪綅缃涓1 + for (int i = 0; i < r; i++) + { + int nojumpCount = 0; + int isBorder = 0; + for (int j = 0; j < c - 1; j++) + { + if (img.at(i, j) == img.at(i, j + 1)) + nojumpCount++; + if (nojumpCount > noJunpCount_thresh) + { + nojumpCount = 0; + isBorder = 1; + break; + } } + boder.at(i) = (char)isBorder; } - boder.at(i) = (char) isBorder; - } - - const int mintop = int(0.1f * r); - const int maxtop = int(0.9f * r); - - int minMatTop = 0; - int maxMatTop = r - 1; - - for (int i = 0; i < mintop; i++) { - if (boder.at(i) == 1) { - minMatTop = i; + // 璁剧疆涓婁笅杈圭晫鐨勬悳绱㈣寖鍥达紝鍙湪鍥惧儚鐨勪腑闂80%鐨勫尯鍩熷唴鎼滅储杈圭晫 + const int mintop = int(0.1f * r); + const int maxtop = int(0.9f * r); + // 鍒濆鍖栬鍓煩褰㈢殑涓婁笅杈圭晫 + int minMatTop = 0; + int maxMatTop = r - 1; + // 浠庝笂鍒颁笅鎼滅储涓婅竟鐣 + for (int i = 0; i < mintop; i++) + { + if (boder.at(i) == 1) + { + minMatTop = i; + } } - } - - for (int i = r - 1; i > maxtop; i--) { - if (boder.at(i) == 1) { - maxMatTop = i; + // 浠庝笅鍒颁笂鎼滅储涓嬭竟鐣 + for (int i = r - 1; i > maxtop; i--) + { + if (boder.at(i) == 1) + { + maxMatTop = i; + } } + // 鏍规嵁鎵惧埌鐨勪笂涓嬭竟鐣屽垱寤鸿鍓煩褰 + cropRect = Rect(0, minMatTop, c, maxMatTop - minMatTop + 1); } - cropRect = Rect(0, minMatTop, c, maxMatTop - minMatTop + 1); - -} - - void clearLiuDing(Mat mask, int &top, int &bottom) { - const int x = 7; - - for (int i = 0; i < mask.rows / 2; i++) { + // 鐢ㄤ簬娓呴櫎鍥惧儚涓殑鍣0鈥斺斺斺斺斿嚱鏁扮殑鐩殑鏄壘鍒板浘鍍忎腑鐨勬湁鏁堝尯鍩燂紝鍗冲幓闄や笂涓嬭竟缂樼殑鍣0 + // 鍑芥暟鎺ュ彈涓涓狹at绫诲瀷鐨勫浘鍍弇ask鍜屼袱涓暣鏁板紩鐢╰op鍜宐ottom浣滀负鍙傛暟 + void clearLiuDing(Mat mask, int &top, int &bottom) + { + const int x = 7; // 瀹氫箟涓涓父閲弜锛屽间负7 + // 閬嶅巻鍥惧儚鐨勪笂鍗婇儴鍒嗭紝璁$畻姣忎竴琛岀殑璺冲彉娆℃暟锛堝嵆鍍忕礌鍊间粠0鍙樹负255锛屾垨浠255鍙樹负0鐨勬鏁帮級鍜岀櫧鑹插儚绱犵殑鏁伴噺 + for (int i = 0; i < mask.rows / 2; i++) + { int whiteCount = 0; int jumpCount = 0; - for (int j = 0; j < mask.cols - 1; j++) { - if (mask.at(i, j) != mask.at(i, j + 1)) jumpCount++; + for (int j = 0; j < mask.cols - 1; j++) + { + if (mask.at(i, j) != mask.at(i, j + 1)) + jumpCount++; - if ((int) mask.at(i, j) == 255) { + if ((int)mask.at(i, j) == 255) + { whiteCount++; } } + // 濡傛灉璺冲彉娆℃暟灏忎簬x涓旂櫧鑹插儚绱犲崰璇ヨ鐨勬瘮渚嬪ぇ浜15%锛屾垨鑰呯櫧鑹插儚绱犵殑鏁伴噺灏忎簬4锛岄偅涔堝皢top璁句负褰撳墠琛屾暟 if ((jumpCount < x && whiteCount * 1.0 / mask.cols > 0.15) || - whiteCount < 4) { + whiteCount < 4) + { top = i; } } + // 灏唗op鍑1锛屽鏋渢op灏忎簬0锛岄偅涔堝皢top璁句负0 top -= 1; - if (top < 0) { + if (top < 0) + { top = 0; } // ok, find top and bottom boudnadry - - for (int i = mask.rows - 1; i >= mask.rows / 2; i--) { + // 閬嶅巻鍥惧儚鐨勪笅鍗婇儴鍒嗭紝璁$畻姣忎竴琛岀殑璺冲彉娆℃暟鍜岀櫧鑹插儚绱犵殑鏁伴噺 + for (int i = mask.rows - 1; i >= mask.rows / 2; i--) + { int jumpCount = 0; int whiteCount = 0; - for (int j = 0; j < mask.cols - 1; j++) { - if (mask.at(i, j) != mask.at(i, j + 1)) jumpCount++; - if (mask.at(i, j) == 255) { + for (int j = 0; j < mask.cols - 1; j++) + { + if (mask.at(i, j) != mask.at(i, j + 1)) + jumpCount++; + if (mask.at(i, j) == 255) + { whiteCount++; } } + // 濡傛灉璺冲彉娆℃暟灏忎簬x涓旂櫧鑹插儚绱犲崰璇ヨ鐨勬瘮渚嬪ぇ浜15%锛屾垨鑰呯櫧鑹插儚绱犵殑鏁伴噺灏忎簬4锛岄偅涔堝皢bottom璁句负褰撳墠琛屾暟 if ((jumpCount < x && whiteCount * 1.0 / mask.cols > 0.15) || - whiteCount < 4) { + whiteCount < 4) + { bottom = i; } } + // 灏哹ottom鍔1锛屽鏋渂ottom澶т簬绛変簬鍥惧儚鐨勮鏁帮紝閭d箞灏哹ottom璁句负鍥惧儚鐨勮鏁板噺1 bottom += 1; - if (bottom >= mask.rows) { + if (bottom >= mask.rows) + { bottom = mask.rows - 1; } - - if (top >= bottom) { + // 濡傛灉top澶т簬绛変簬bottom锛岄偅涔堝皢top璁句负0锛宐ottom璁句负鍥惧儚鐨勮鏁板噺1 + if (top >= bottom) + { top = 0; bottom = mask.rows - 1; } } - int ThresholdOtsu(Mat mat) { - int height = mat.rows; - int width = mat.cols; + int ThresholdOtsu(Mat mat) + { // 瀹氫箟鍑芥暟ThresholdOtsu锛屽弬鏁颁负OpenCV鐨凪at绫诲瀷锛岃繑鍥炲间负int绫诲瀷 + int height = mat.rows; // 鑾峰彇鍥惧儚鐨勮鏁 + int width = mat.cols; // 鑾峰彇鍥惧儚鐨勫垪鏁 // histogram - float histogram[256] = {0}; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { - unsigned char p = (unsigned char) ((mat.data[i * mat.step[0] + j])); - histogram[p]++; + float histogram[256] = {0}; // 鍒濆鍖栦竴涓暱搴︿负256鐨勬诞鐐规暟鏁扮粍锛岀敤浜庡瓨鍌ㄥ浘鍍忕殑鐏板害鐩存柟鍥 + for (int i = 0; i < height; i++) + { // 閬嶅巻鍥惧儚鐨勬瘡涓琛 + for (int j = 0; j < width; j++) + { // 閬嶅巻鍥惧儚鐨勬瘡涓鍒 + unsigned char p = (unsigned char)((mat.data[i * mat.step[0] + j])); // 鑾峰彇褰撳墠鍍忕礌鐨勭伆搴﹀ + histogram[p]++; // 瀵瑰簲鐨勭伆搴︾洿鏂瑰浘鐨勫煎姞1 } } // normalize histogram - int size = height * width; - for (int i = 0; i < 256; i++) { - histogram[i] = histogram[i] / size; + int size = height * width; // 璁$畻鍥惧儚鐨勬诲儚绱犳暟 + for (int i = 0; i < 256; i++) + { // 閬嶅巻鐏板害鐩存柟鍥 + histogram[i] = histogram[i] / size; // 灏嗙伆搴︾洿鏂瑰浘鐨勬瘡涓涓奸櫎浠ユ诲儚绱犳暟锛屽緱鍒版瘡涓伆搴﹀肩殑姒傜巼 } // average pixel value - float avgValue = 0; - for (int i = 0; i < 256; i++) { - avgValue += i * histogram[i]; + float avgValue = 0; // 鍒濆鍖栧钩鍧囩伆搴﹀间负0 + for (int i = 0; i < 256; i++) + { // 閬嶅巻鐏板害鐩存柟鍥 + avgValue += i * histogram[i]; // 璁$畻骞冲潎鐏板害鍊 } - int thresholdV; - float maxVariance = 0; - float w = 0, u = 0; - for (int i = 0; i < 256; i++) { - w += histogram[i]; - u += i * histogram[i]; + int thresholdV; // 鍒濆鍖栭槇鍊 + float maxVariance = 0; // 鍒濆鍖栨渶澶ф柟宸负0 + float w = 0, u = 0; // 鍒濆鍖栦袱涓彉閲弚鍜寀锛屽垎鍒敤浜庡瓨鍌ㄩ槇鍊间互涓嬪拰闃堝间互涓婄殑鍍忕礌鏁扮殑绱Н鍜 + for (int i = 0; i < 256; i++) + { // 閬嶅巻鐏板害鐩存柟鍥 + w += histogram[i]; // 璁$畻闃堝间互涓嬬殑鍍忕礌鏁扮殑绱Н鍜 + u += i * histogram[i]; // 璁$畻闃堝间互涓婄殑鍍忕礌鏁扮殑绱Н鍜 - float t = avgValue * w - u; - float variance = t * t / (w * (1 - w)); - if (variance > maxVariance) { - maxVariance = variance; - thresholdV = i; + float t = avgValue * w - u; // 璁$畻绫婚棿鏂瑰樊 + float variance = t * t / (w * (1 - w)); // 璁$畻绫婚棿鏂瑰樊 + if (variance > maxVariance) + { // 濡傛灉褰撳墠鐨勭被闂存柟宸ぇ浜庢渶澶ф柟宸 + maxVariance = variance; // 鏇存柊鏈澶ф柟宸 + thresholdV = i; // 鏇存柊闃堝 } } - return thresholdV; + return thresholdV; // 杩斿洖闃堝 } - - Mat histeq(Mat in) { - Mat out(in.size(), in.type()); - if (in.channels() == 3) { + // 璇ュ嚱鏁版帴鍙椾竴涓狹at绫诲瀷鐨勮緭鍏ュ浘鍍忥紝骞惰繑鍥炰竴涓粡杩囩洿鏂瑰浘鍧囪 鍖栧鐞嗙殑鍥惧儚 + Mat histeq(Mat in) + { + Mat out(in.size(), in.type()); // 鍒涘缓涓涓笌杈撳叆鍥惧儚鍚屾牱澶у皬鍜岀被鍨嬬殑Mat瀵硅薄out锛岀敤浜庡瓨鍌ㄥ鐞嗗悗鐨勫浘鍍 + if (in.channels() == 3) + { // 鍒ゆ柇杈撳叆鍥惧儚鐨勯氶亾鏁帮紝濡傛灉涓3锛屽嵆涓哄僵鑹插浘鍍忥紝鎵ц浠ヤ笅鎿嶄綔 + // 鍒涘缓涓涓狹at瀵硅薄hsv鍜屼竴涓狹at鍚戦噺hsvSplit锛岀劧鍚庝娇鐢╟vtColor鍑芥暟灏嗚緭鍏ュ浘鍍忎粠BGR鑹插僵绌洪棿杞崲涓篐SV鑹插僵绌洪棿锛岀粨鏋滃瓨鍌ㄥ湪hsv涓 Mat hsv; std::vector hsvSplit; cvtColor(in, hsv, CV_BGR2HSV); + // 浣跨敤split鍑芥暟灏唄sv鍥惧儚鐨勪笁涓氶亾鍒嗙鍒癶svSplit鍚戦噺涓紝鐒跺悗瀵筕閫氶亾锛堜寒搴︼級杩涜鐩存柟鍥惧潎琛″寲 split(hsv, hsvSplit); equalizeHist(hsvSplit[2], hsvSplit[2]); + // 浣跨敤merge鍑芥暟灏嗗鐞嗗悗鐨勪笁涓氶亾鍚堝苟鍥瀐sv锛岀劧鍚庝娇鐢╟vtColor鍑芥暟灏唄sv鍥惧儚浠嶩SV鑹插僵绌洪棿杞崲鍥濨GR鑹插僵绌洪棿锛岀粨鏋滃瓨鍌ㄥ湪out涓 merge(hsvSplit, hsv); cvtColor(hsv, out, CV_HSV2BGR); - } else if (in.channels() == 1) { + } + else if (in.channels() == 1) + { // 濡傛灉杈撳叆鍥惧儚鐨勯氶亾鏁颁负1锛屽嵆涓虹伆搴﹀浘鍍忥紝鎵ц浠ヤ笅鎿嶄綔 + // 鐩存帴瀵硅緭鍏ュ浘鍍忚繘琛岀洿鏂瑰浘鍧囪 鍖栵紝缁撴灉瀛樺偍鍦╫ut涓 equalizeHist(in, out); } - return out; + return out; // 杩斿洖澶勭悊鍚庣殑鍥惧儚 } #define HORIZONTAL 1 #define VERTICAL 0 - Mat CutTheRect(Mat &in, Rect &rect) { - int size = in.cols; // (rect.width>rect.height)?rect.width:rect.height; + // 浠庤緭鍏ュ浘鍍忎腑瑁佸壀鍑轰竴涓寚瀹氱殑鐭╁舰鍖哄煙锛屽苟灏嗗叾鏀剧疆鍦ㄤ竴涓柊鐨勬鏂瑰舰鍥惧儚鐨勪腑蹇冧綅缃 + Mat CutTheRect(Mat &in, Rect &rect) + { // 鎺ュ彈涓涓狹at绫诲瀷鐨勫紩鐢╥n鍜屼竴涓猂ect绫诲瀷鐨勫紩鐢╮ect浣滀负鍙傛暟 + int size = in.cols; // (rect.width>rect.height)?rect.width:rect.height; + // 鍒涘缓浜嗕竴涓柊鐨凪at瀵硅薄dstMat锛屽叾澶у皬涓簊izexsize锛岀被鍨嬩负CV_8UC1锛8浣嶆棤绗﹀彿鍗曢氶亾锛夛紝骞跺皢鍏舵墍鏈夊厓绱犺缃负0 Mat dstMat(size, size, CV_8UC1); dstMat.setTo(Scalar(0, 0, 0)); + // 璁$畻鐭╁舰rect鍦ㄦ柊鍥惧儚涓殑璧峰浣嶇疆(x, y) + int x = (int)floor((float)(size - rect.width) / 2.0f); + int y = (int)floor((float)(size - rect.height) / 2.0f); - int x = (int) floor((float) (size - rect.width) / 2.0f); - int y = (int) floor((float) (size - rect.height) / 2.0f); - - for (int i = 0; i < rect.height; ++i) { + // 閬嶅巻鐭╁舰rect涓殑姣忎竴涓儚绱狅紝骞跺皢鍏跺鍒跺埌鏂板浘鍍廳stMat鐨勭浉搴斾綅缃 + for (int i = 0; i < rect.height; ++i) + { - for (int j = 0; j < rect.width; ++j) { + for (int j = 0; j < rect.width; ++j) + { dstMat.data[dstMat.step[0] * (i + y) + j + x] = in.data[in.step[0] * (i + rect.y) + j + rect.x]; } } - // + // 杩斿洖澶勭悊鍚庣殑鏂板浘鍍廳stMat return dstMat; } - Rect GetCenterRect(Mat &in) { - Rect _rect; - - int top = 0; - int bottom = in.rows - 1; + // 鍏剁洰鐨勬槸鍦ㄨ緭鍏ュ浘鍍忎腑鎵惧埌涓涓煩褰㈠尯鍩燂紝璇ュ尯鍩熷寘鍚簡鍥惧儚涓墍鏈夊儚绱犲煎ぇ浜20鐨勯儴鍒 + // 鍑芥暟鎺ュ彈涓涓 Mat 绫诲瀷鐨勫紩鐢 in 浣滀负鍙傛暟锛岃繑鍥炰竴涓 Rect 绫诲瀷鐨勫璞 + Rect GetCenterRect(Mat &in) + { + Rect _rect; // 瀹氫箟涓涓煩褰㈠璞 - // find the center rect + int top = 0; // 鍒濆鍖栫煩褰㈢殑涓婅竟鐣 + int bottom = in.rows - 1; // 鍒濆鍖栫煩褰㈢殑涓嬭竟鐣屼负鍥惧儚鐨勮鏁板噺1 - for (int i = 0; i < in.rows; ++i) { + // 浠庝笂鍒颁笅閬嶅巻鍥惧儚鐨勬瘡涓琛岋紝鎵惧埌绗竴涓寘鍚儚绱犲煎ぇ浜20鐨勮锛屽皢鍏惰鏁拌涓虹煩褰㈢殑涓婅竟鐣 + for (int i = 0; i < in.rows; ++i) + { bool bFind = false; - for (int j = 0; j < in.cols; ++j) { - if (in.data[i * in.step[0] + j] > 20) { + for (int j = 0; j < in.cols; ++j) + { + if (in.data[i * in.step[0] + j] > 20) + { top = i; bFind = true; break; } } - if (bFind) { + if (bFind) + { break; } - } - for (int i = in.rows - 1; - i >= 0; - --i) { + + // 浠庝笅鍒颁笂閬嶅巻鍥惧儚鐨勬瘡涓琛岋紝鎵惧埌绗竴涓寘鍚儚绱犲煎ぇ浜20鐨勮锛屽皢鍏惰鏁拌涓虹煩褰㈢殑涓嬭竟鐣 + for (int i = in.rows - 1; i >= 0; --i) + { bool bFind = false; - for (int j = 0; j < in.cols; ++j) { - if (in.data[i * in.step[0] + j] > 20) { + for (int j = 0; j < in.cols; ++j) + { + if (in.data[i * in.step[0] + j] > 20) + { bottom = i; bFind = true; break; } } - if (bFind) { + if (bFind) + { break; } - } + int left = 0; // 鍒濆鍖栫煩褰㈢殑宸﹁竟鐣 + int right = in.cols - 1; // 鍒濆鍖栫煩褰㈢殑鍙宠竟鐣屼负鍥惧儚鐨勫垪鏁板噺1 - int left = 0; - int right = in.cols - 1; - for (int j = 0; j < in.cols; ++j) { + // 浠庡乏鍒板彸閬嶅巻鍥惧儚鐨勬瘡涓鍒楋紝鎵惧埌绗竴涓寘鍚儚绱犲煎ぇ浜20鐨勫垪锛屽皢鍏跺垪鏁拌涓虹煩褰㈢殑宸﹁竟鐣 + for (int j = 0; j < in.cols; ++j) + { bool bFind = false; - for (int i = 0; i < in.rows; ++i) { - if (in.data[i * in.step[0] + j] > 20) { + for (int i = 0; i < in.rows; ++i) + { + if (in.data[i * in.step[0] + j] > 20) + { left = j; bFind = true; break; } } - if (bFind) { + if (bFind) + { break; } - } - for (int j = in.cols - 1; - j >= 0; - --j) { + + // 浠庡彸鍒板乏閬嶅巻鍥惧儚鐨勬瘡涓鍒楋紝鎵惧埌绗竴涓寘鍚儚绱犲煎ぇ浜20鐨勫垪锛屽皢鍏跺垪鏁拌涓虹煩褰㈢殑鍙宠竟鐣 + for (int j = in.cols - 1; j >= 0; --j) + { bool bFind = false; - for (int i = 0; i < in.rows; ++i) { - if (in.data[i * in.step[0] + j] > 20) { + for (int i = 0; i < in.rows; ++i) + { + if (in.data[i * in.step[0] + j] > 20) + { right = j; bFind = true; - break; } } - if (bFind) { + if (bFind) + { break; } } + // 璁剧疆鐭╁舰鐨勪綅缃拰澶у皬 _rect.x = left; _rect.y = top; _rect.width = right - left + 1; _rect.height = bottom - top + 1; - return _rect; + return _rect; // 杩斿洖鐭╁舰瀵硅薄 } - float countOfBigValue(Mat &mat, int iValue) { + // 璁$畻鍥惧儚涓ぇ浜庣粰瀹氶槇鍊肩殑鍍忕礌鏁伴噺 + float countOfBigValue(Mat &mat, int iValue) + { float iCount = 0.0; - if (mat.rows > 1) { - for (int i = 0; i < mat.rows; ++i) { - if (mat.data[i * mat.step[0]] > iValue) { + // 濡傛灉鍥惧儚鐨勮鏁板ぇ浜1锛岄亶鍘嗘瘡涓琛 + if (mat.rows > 1) + { + for (int i = 0; i < mat.rows; ++i) + { + // 濡傛灉褰撳墠鍍忕礌鐨勫煎ぇ浜庨槇鍊硷紝璁℃暟鍣ㄥ姞1 + if (mat.data[i * mat.step[0]] > iValue) + { iCount += 1.0; } } return iCount; - - } else { - for (int i = 0; i < mat.cols; ++i) { - if (mat.data[i] > iValue) { + } + else + { + // 濡傛灉鍥惧儚鐨勮鏁颁笉澶т簬1锛岄亶鍘嗘瘡涓鍒 + for (int i = 0; i < mat.cols; ++i) + { + // 濡傛灉褰撳墠鍍忕礌鐨勫煎ぇ浜庨槇鍊硷紝璁℃暟鍣ㄥ姞1 + if (mat.data[i] > iValue) + { iCount += 1.0; } } - return iCount; } } - Mat ProjectedHistogram(Mat img, int t, int threshold) { + // 璁$畻鍥惧儚鐨勬姇褰辩洿鏂瑰浘 + Mat ProjectedHistogram(Mat img, int t, int threshold) + { + // 鏍规嵁鍙傛暟t鐨勫硷紝纭畾鐩存柟鍥剧殑闀垮害 int sz = (t) ? img.rows : img.cols; + // 鍒涘缓涓涓暱搴︿负sz鐨勯浂鐭╅樀锛岀敤浜庡瓨鍌ㄧ洿鏂瑰浘 Mat mhist = Mat::zeros(1, sz, CV_32F); - for (int j = 0; j < sz; j++) { + for (int j = 0; j < sz; j++) + { + // 鏍规嵁鍙傛暟t鐨勫硷紝纭畾鏄鍥惧儚鐨勮杩樻槸鍒楄繘琛屾搷浣 Mat data = (t) ? img.row(j) : img.col(j); - + // 璁$畻澶т簬闃堝肩殑鍍忕礌鏁伴噺锛屽苟瀛樺偍鍦ㄧ洿鏂瑰浘涓 mhist.at(j) = countOfBigValue(data, threshold); } - // Normalize histogram + // 褰掍竴鍖栫洿鏂瑰浘 double min, max; minMaxLoc(mhist, &min, &max); @@ -706,144 +923,170 @@ void clearBorder(const Mat &img, Rect& cropRect) { return mhist; } - Mat showHistogram(const Mat &hist) { + // 鏄剧ず鐩存柟鍥 + Mat showHistogram(const Mat &hist) + { int height = 32; int width = hist.cols; + // 鍒涘缓涓涓珮搴︿负32锛屽搴︿负鐩存柟鍥鹃暱搴︾殑闆剁煩闃碉紝鐢ㄤ簬鏄剧ず鐩存柟鍥 Mat show = Mat::zeros(height, width, CV_8UC1); - for (int i = 0; i < width; i++) { - int len = int((float) height * hist.at(i)); - for (int j = height - 1; j >= 0; j--) { + for (int i = 0; i < width; i++) + { + // 璁$畻鐩存柟鍥剧殑姣忎竴鍒楃殑闀垮害 + int len = int((float)height * hist.at(i)); + for (int j = height - 1; j >= 0; j--) + { + // 灏嗙洿鏂瑰浘鐨勬瘡涓鍒楃殑闀垮害浠ュ儚绱犵殑褰㈠紡鏄剧ず鍑烘潵 if (height - j <= len) - show.at(j, i) = (char) 255; + show.at(j, i) = (char)255; } } return show; } - Mat preprocessChar(Mat in, int char_size) { + // 瀵硅緭鍏ュ浘鍍忚繘琛岄澶勭悊 + // 棰勫鐞嗗寘鎷垱寤轰竴涓崟浣嶇煩闃碉紝璁$畻鍥惧儚鐨勬渶澶х淮搴︼紝瀵硅緭鍏ュ浘鍍忚繘琛屼豢灏勫彉鎹紝鐒跺悗灏嗗彉鎹㈠悗鐨勫浘鍍忕缉鏀惧埌鎸囧畾鐨勫瓧绗﹀ぇ灏 + Mat preprocessChar(Mat in, int char_size) + { // Remap image - int h = in.rows; - int w = in.cols; + 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) = float(m / 2 - w / 2); + Mat transformMat = Mat::eye(2, 3, CV_32F); // 鍒涘缓涓涓2x3鐨勫崟浣嶇煩闃 + int m = max(w, h); // 鑾峰彇鍥惧儚鐨勬渶澶х淮搴 + transformMat.at(0, 2) = float(m / 2 - w / 2); // 璁$畻骞惰缃彉鎹㈢煩闃电殑骞崇Щ閮ㄥ垎 transformMat.at(1, 2) = float(m / 2 - h / 2); - Mat warpImage(m, m, in.type()); + Mat warpImage(m, m, in.type()); // 鍒涘缓涓涓柊鐨勫浘鍍忥紝澶у皬涓簃*m锛岀被鍨嬩笌杈撳叆鍥惧儚鐩稿悓 warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, - BORDER_CONSTANT, Scalar(0)); + BORDER_CONSTANT, Scalar(0)); // 瀵硅緭鍏ュ浘鍍忚繘琛屼豢灏勫彉鎹 Mat out; - cv::resize(warpImage, out, Size(charSize, charSize)); + cv::resize(warpImage, out, Size(charSize, charSize)); // 灏嗗彉鎹㈠悗鐨勫浘鍍忕缉鏀惧埌鎸囧畾鐨勫瓧绗﹀ぇ灏 - return out; + return out; // 杩斿洖澶勭悊鍚庣殑鍥惧儚 } - Rect GetChineseRect(const Rect rectSpe) { - int height = rectSpe.height; - float newwidth = rectSpe.width * 1.10f; - int x = rectSpe.x; - int y = rectSpe.y; + // 鎺ュ彈涓涓壒瀹氱殑鐭╁舰锛岀劧鍚庤绠楁柊鐨勫搴︼紙涓哄師瀹藉害鐨110%锛 + // 鏂扮殑x鍧愭爣锛堜负鍘焫鍧愭爣鍑忓幓鏂板搴︾殑110%锛夛紝濡傛灉鏂扮殑x鍧愭爣灏忎簬0锛屽皢鍏惰缃负0 + // 鐒跺悗鍒涘缓涓涓柊鐨勭煩褰紝骞惰繑鍥 + Rect GetChineseRect(const Rect rectSpe) + { + int height = rectSpe.height; // 鑾峰彇鐗瑰畾鐭╁舰鐨勯珮搴 + float newwidth = rectSpe.width * 1.10f; // 璁$畻鏂扮殑瀹藉害锛屼负鍘熷搴︾殑110% + int x = rectSpe.x; // 鑾峰彇鐗瑰畾鐭╁舰鐨剎鍧愭爣 + int y = rectSpe.y; // 鑾峰彇鐗瑰畾鐭╁舰鐨剏鍧愭爣 - int newx = x - int(newwidth * 1.10f); - newx = newx > 0 ? newx : 0; + int newx = x - int(newwidth * 1.10f); // 璁$畻鏂扮殑x鍧愭爣锛屼负鍘焫鍧愭爣鍑忓幓鏂板搴︾殑110% + newx = newx > 0 ? newx : 0; // 濡傛灉鏂扮殑x鍧愭爣灏忎簬0锛屽皢鍏惰缃负0 - Rect a(newx, y, int(newwidth), height); + Rect a(newx, y, int(newwidth), height); // 鍒涘缓涓涓柊鐨勭煩褰 - return a; + return a; // 杩斿洖鏂扮殑鐭╁舰 } - bool verifyCharSizes(Rect r) { + // 鎺ュ彈涓涓煩褰紝鐒跺悗璁$畻瀛楃鐨勫楂樻瘮锛岀煩褰㈢殑瀹介珮姣斺︹ + bool verifyCharSizes(Rect r) + { // Char sizes 45x90 - float aspect = 45.0f / 90.0f; - float charAspect = (float) r.width / (float) r.height; - float error = 0.35f; - float minHeight = 25.f; - float maxHeight = 50.f; + float aspect = 45.0f / 90.0f; // 璁$畻瀛楃鐨勫楂樻瘮 + float charAspect = (float)r.width / (float)r.height; // 璁$畻鐭╁舰鐨勫楂樻瘮 + float error = 0.35f; // 璁剧疆璇樊鑼冨洿 + float minHeight = 25.f; // 璁剧疆鏈灏忛珮搴 + float maxHeight = 50.f; // 璁剧疆鏈澶ч珮搴 // We have a different aspect ratio for number 1, and it can be ~0.2 - float minAspect = 0.05f; - float maxAspect = aspect + aspect * error; + float minAspect = 0.05f; // 璁剧疆鏈灏忓楂樻瘮 + float maxAspect = aspect + aspect * error; // 璁剧疆鏈澶у楂樻瘮 // bb area - int bbArea = r.width * r.height; + int bbArea = r.width * r.height; // 璁$畻鐭╁舰鐨勯潰绉 if (charAspect > minAspect && charAspect < maxAspect /*&& - r.rows >= minHeight && r.rows < maxHeight*/) - return true; + r.rows >= minHeight && r.rows < maxHeight*/ + ) + return true; // 濡傛灉鐭╁舰鐨勫楂樻瘮鍦ㄦ渶灏忓拰鏈澶у楂樻瘮涔嬮棿锛岃繑鍥瀟rue else - return false; + return false; // 鍚﹀垯杩斿洖false } - - Mat scaleImage(const Mat &image, const Size &maxSize, double &scale_ratio) { + // 璁$畻鍥惧儚瀹藉害鍜屾渶澶у搴︾殑姣斾緥锛屽浘鍍忛珮搴﹀拰鏈澶ч珮搴︾殑姣斾緥锛岃幏鍙栧搴﹀拰楂樺害姣斾緥涓殑鏈澶у + // 璁$畻鏂扮殑瀹藉害鍜屾柊鐨勯珮搴︼紝灏嗗浘鍍忕缉鏀惧埌鏂扮殑澶у皬锛岃缃缉鏀炬瘮渚 + // 濡傛灉鍥惧儚鐨勫ぇ灏忓凡缁忓皬浜庢垨绛変簬鏈澶уぇ灏忥紝鐩存帴杩斿洖鍘熷浘鍍忥紝璁剧疆缂╂斁姣斾緥涓1.0 + Mat scaleImage(const Mat &image, const Size &maxSize, double &scale_ratio) + { Mat ret; - if (image.cols > maxSize.width || image.rows > maxSize.height) { - double widthRatio = image.cols / (double) maxSize.width; - double heightRatio = image.rows / (double) maxSize.height; - double m_real_to_scaled_ratio = max(widthRatio, heightRatio); + if (image.cols > maxSize.width || image.rows > maxSize.height) + { + double widthRatio = image.cols / (double)maxSize.width; // 璁$畻鍥惧儚瀹藉害鍜屾渶澶у搴︾殑姣斾緥 + double heightRatio = image.rows / (double)maxSize.height; // 璁$畻鍥惧儚楂樺害鍜屾渶澶ч珮搴︾殑姣斾緥 + double m_real_to_scaled_ratio = max(widthRatio, heightRatio); // 鑾峰彇瀹藉害鍜岄珮搴︽瘮渚嬩腑鐨勬渶澶у - int newWidth = int(image.cols / m_real_to_scaled_ratio); - int newHeight = int(image.rows / m_real_to_scaled_ratio); + int newWidth = int(image.cols / m_real_to_scaled_ratio); // 璁$畻鏂扮殑瀹藉害 + int newHeight = int(image.rows / m_real_to_scaled_ratio); // 璁$畻鏂扮殑楂樺害 - cv::resize(image, ret, Size(newWidth, newHeight), 0, 0); - scale_ratio = m_real_to_scaled_ratio; - } else { - ret = image; - scale_ratio = 1.0; + cv::resize(image, ret, Size(newWidth, newHeight), 0, 0); // 灏嗗浘鍍忕缉鏀惧埌鏂扮殑澶у皬 + scale_ratio = m_real_to_scaled_ratio; // 璁剧疆缂╂斁姣斾緥 + } + else + { + ret = image; // 濡傛灉鍥惧儚鐨勫ぇ灏忓凡缁忓皬浜庢垨绛変簬鏈澶уぇ灏忥紝鐩存帴杩斿洖鍘熷浘鍍 + scale_ratio = 1.0; // 璁剧疆缂╂斁姣斾緥涓1.0 } - return ret; + return ret; // 杩斿洖澶勭悊鍚庣殑鍥惧儚 } - -// Scale back RotatedRect - RotatedRect scaleBackRRect(const RotatedRect &rr, const float scale_ratio) { - float width = rr.size.width * scale_ratio; - float height = rr.size.height * scale_ratio; - float x = rr.center.x * scale_ratio; - float y = rr.center.y * scale_ratio; - RotatedRect mserRect(Point2f(x, y), Size2f(width, height), rr.angle); - - return mserRect; + // Scale back RotatedRect + // 灏嗕竴涓棆杞煩褰紙RotatedRect锛夋寜鐓х粰瀹氱殑缂╂斁姣斾緥杩涜鍙嶅悜缂╂斁锛岃繑鍥炰竴涓柊鐨勬棆杞煩褰 + RotatedRect scaleBackRRect(const RotatedRect &rr, const float scale_ratio) + { + float width = rr.size.width * scale_ratio; // 鏍规嵁缂╂斁姣斾緥璁$畻鍘熷瀹藉害 + float height = rr.size.height * scale_ratio; // 鏍规嵁缂╂斁姣斾緥璁$畻鍘熷楂樺害 + float x = rr.center.x * scale_ratio; // 鏍规嵁缂╂斁姣斾緥璁$畻鍘熷涓績鐐圭殑x鍧愭爣 + float y = rr.center.y * scale_ratio; // 鏍规嵁缂╂斁姣斾緥璁$畻鍘熷涓績鐐圭殑y鍧愭爣 + RotatedRect mserRect(Point2f(x, y), Size2f(width, height), rr.angle); // 鍒涘缓涓涓柊鐨凴otatedRect瀵硅薄锛屼娇鐢ㄥ師濮嬬殑灏哄鍜岃搴 + + return mserRect; // 杩斿洖鏂扮殑RotatedRect瀵硅薄 } - bool verifyPlateSize(Rect mr) { - float error = 0.6f; + // 楠岃瘉涓涓煩褰㈡槸鍚︾鍚堥璁剧殑杞︾墝灏哄锛屽寘鎷潰绉拰瀹介珮姣旓紝濡傛灉绗﹀悎鍒欒繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse + bool verifyPlateSize(Rect mr) + { + float error = 0.6f; // 璁剧疆璇樊鑼冨洿 // Spain car plate size: 52x11 aspect 4,7272 // China car plate size: 440mm*140mm锛宎spect 3.142857 // Real car plate size: 136 * 32, aspect 4 - float aspect = 3.75; + float aspect = 3.75; // 璁剧疆鏈熸湜鐨勫楂樻瘮 // Set a min and max area. All other patchs are discarded - // int min= 1*aspect*1; // minimum area - // int max= 2000*aspect*2000; // maximum area - int min = 34 * 8 * 1; // minimum area - int max = 34 * 8 * 200; // maximum area + int min = 34 * 8 * 1; // 璁剧疆鏈灏忛潰绉 + int max = 34 * 8 * 200; // 璁剧疆鏈澶ч潰绉 // Get only patchs that match to a respect ratio. - float rmin = aspect - aspect * error; - float rmax = aspect + aspect * error; + float rmin = aspect - aspect * error; // 璁$畻鏈灏忓楂樻瘮 + float rmax = aspect + aspect * error; // 璁$畻鏈澶у楂樻瘮 - float area = float(mr.height * mr.width); - float r = (float) mr.width / (float) mr.height; - if (r < 1) r = (float) mr.height / (float) mr.width; - - // cout << "area:" << area << endl; - // cout << "r:" << r << endl; + float area = float(mr.height * mr.width); // 璁$畻鐭╁舰鐨勯潰绉 + float r = (float)mr.width / (float)mr.height; // 璁$畻鐭╁舰鐨勫楂樻瘮 + if (r < 1) + r = (float)mr.height / (float)mr.width; // 濡傛灉瀹介珮姣斿皬浜1锛屽彇鍏跺掓暟 + // 鍒ゆ柇鐭╁舰鐨勯潰绉拰瀹介珮姣旀槸鍚﹀湪鎸囧畾鐨勮寖鍥村唴 if ((area < min || area > max) || (r < rmin || r > rmax)) - return false; + return false; // 濡傛灉涓嶅湪鎸囧畾鑼冨洿鍐咃紝杩斿洖false else - return true; + return true; // 濡傛灉鍦ㄦ寚瀹氳寖鍥村唴锛岃繑鍥瀟rue } - bool verifyRotatedPlateSizes(RotatedRect mr, bool showDebug) { - float error = 0.65f; + // 鎺ユ敹涓涓猂otatedRect瀵硅薄鍜屼竴涓竷灏斿約howDebug浣滀负鍙傛暟 + // 璇ュ嚱鏁扮殑涓昏鐩殑鏄獙璇佷竴涓棆杞煩褰紙閫氬父鏄溅鐗岋級鏄惁绗﹀悎棰勮鐨勫昂瀵稿拰姣斾緥 + bool verifyRotatedPlateSizes(RotatedRect mr, bool showDebug) + { + float error = 0.65f; // 瀹氫箟璇樊鍊糴rror锛屼互鍙婅溅鐗岀殑瀹介珮姣攁spect // Spain car plate size: 52x11 aspect 4,7272 // China car plate size: 440mm*140mm锛宎spect 3.142857 @@ -853,107 +1096,139 @@ void clearBorder(const Mat &img, Rect& cropRect) { // Set a min and max area. All other patchs are discarded // int min= 1*aspect*1; // minimum area // int max= 2000*aspect*2000; // maximum area - //int min = 34 * 8 * 1; // minimum area - //int max = 34 * 8 * 200; // maximum area + // int min = 34 * 8 * 1; // minimum area + // int max = 34 * 8 * 200; // maximum area // Get only patchs that match to a respect ratio. + // 璁$畻鏈灏忓拰鏈澶х殑闈㈢Нmin鍜宮ax锛屼互鍙婃渶灏忓拰鏈澶х殑瀹介珮姣攁spect_min鍜宎spect_max float aspect_min = aspect - aspect * error; float aspect_max = aspect + aspect * error; float width_max = 600.f; float width_min = 30.f; - float min = float(width_min * width_min / aspect_max); // minimum area - float max = float(width_max * width_max / aspect_min); // maximum area - + float min = float(width_min * width_min / aspect_max); // minimum area + float max = float(width_max * width_max / aspect_min); // maximum area + // 鑾峰彇鏃嬭浆鐭╁舰鐨勫搴︺侀珮搴︺侀潰绉佸楂樻瘮鍜岃搴 float width = mr.size.width; float height = mr.size.height; float area = width * height; float ratio = width / height; float angle = mr.angle; - if (ratio < 1) { + if (ratio < 1) + { // 濡傛灉瀹介珮姣斿皬浜1锛屼氦鎹㈠搴﹀拰楂樺害鐨勫硷紝骞惰皟鏁磋搴 swap(width, height); ratio = width / height; angle = 90.f + angle; - //std::cout << "angle:" << angle << std::endl; + // std::cout << "angle:" << angle << std::endl; } - + // 瀹氫箟瑙掑害鐨勬渶灏忓拰鏈澶у float angle_min = -60.f; float angle_max = 60.f; - //std::cout << "aspect_min:" << aspect_min << std::endl; - //std::cout << "aspect_max:" << aspect_max << std::endl; - - if (area < min || area > max) { - if (0 && showDebug) { + // std::cout << "aspect_min:" << aspect_min << std::endl; + // std::cout << "aspect_max:" << aspect_max << std::endl; + // 閫氳繃涓绯诲垪鐨勬潯浠跺垽鏂紝楠岃瘉鏃嬭浆鐭╁舰鏄惁绗﹀悎棰勮鐨勫昂瀵稿拰姣斾緥 + // 濡傛灉涓嶇鍚堬紝杩斿洖false锛涘鏋滅鍚堬紝杩斿洖true + if (area < min || area > max) + { + if (0 && showDebug) + { std::cout << "area < min || area > max: " << area << std::endl; } return false; - } else if (ratio < aspect_min || ratio > aspect_max) { - if (0 && showDebug) { + } + else if (ratio < aspect_min || ratio > aspect_max) + { + if (0 && showDebug) + { std::cout << "ratio < aspect_min || ratio > aspect_max: " << ratio << std::endl; } return false; - } else if (angle < angle_min || angle > angle_max) { - if (0 && showDebug) { + } + else if (angle < angle_min || angle > angle_max) + { + if (0 && showDebug) + { std::cout << "angle < angle_min || angle > angle_max: " << angle << std::endl; } return false; - } else if (width < width_min || width > width_max) { - if (0 && showDebug) { + } + else if (width < width_min || width > width_max) + { + if (0 && showDebug) + { std::cout << "width < width_min || width > width_max: " << width << std::endl; } return false; - } else { + } + else + { return true; } return true; } -//! non-maximum suppression - void NMStoCharacter(std::vector &inVec, double overlap) { - - std::sort(inVec.begin(), inVec.end()); - + //! non-maximum suppression + // 璇ュ嚱鏁板疄鐜颁簡闈炴瀬澶у兼姂鍒讹紙Non-Maximum Suppression锛孨MS锛夌殑鍔熻兘 + // 闈炴瀬澶у兼姂鍒舵槸涓绉嶅父鐢ㄤ簬鐩爣妫娴嬩腑鐨勬妧鏈紝鐢ㄤ簬娑堥櫎澶氫綑鐨勯噸鍙犲尯鍩 + // 璇ュ嚱鏁颁富瑕佸垹闄nVec涓噸鍙犲害杩囬珮鐨凜Character瀵硅薄锛屽彧淇濈暀鏈鍏蜂唬琛ㄦх殑瀵硅薄 + void NMStoCharacter(std::vector &inVec, double overlap) + { + // 鍑芥暟鎺ュ彈涓や釜鍙傛暟锛屼竴涓槸CCharacter瀵硅薄鐨勫悜閲廼nVec锛屽彟涓涓槸閲嶅彔闃堝紀verlap + std::sort(inVec.begin(), inVec.end()); // 瀵筰nVec杩涜鎺掑簭 + // 閬嶅巻inVec涓殑姣忎竴涓狢Character瀵硅薄 std::vector::iterator it = inVec.begin(); - for (; it != inVec.end(); ++it) { - CCharacter charSrc = *it; - //std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl; + for (; it != inVec.end(); ++it) + { + CCharacter charSrc = *it; // 瀵逛簬姣忎竴涓狢Character瀵硅薄锛岃幏鍙栧叾浣嶇疆淇℃伅rectSrc + // std::cout << "plateScore:" << plateSrc.getPlateScore() << std::endl; Rect rectSrc = charSrc.getCharacterPos(); - + // 閬嶅巻褰撳墠CCharacter瀵硅薄涔嬪悗鐨勬墍鏈塁Character瀵硅薄 std::vector::iterator itc = it + 1; - for (; itc != inVec.end();) { - CCharacter charComp = *itc; + for (; itc != inVec.end();) + { + CCharacter charComp = *itc; // 瀵逛簬姣忎竴涓悗缁殑CCharacter瀵硅薄锛屼篃鑾峰彇鍏朵綅缃俊鎭痳ectComp Rect rectComp = charComp.getCharacterPos(); - //Rect rectInter = rectSrc & rectComp; - //Rect rectUnion = rectSrc | rectComp; - //double r = double(rectInter.area()) / double(rectUnion.area()); - + // Rect rectInter = rectSrc & rectComp; + // Rect rectUnion = rectSrc | rectComp; + // double r = double(rectInter.area()) / double(rectUnion.area()); + // 璁$畻褰撳墠CCharacter瀵硅薄涓庡悗缁瑿Character瀵硅薄鐨勪氦骞舵瘮锛圛ntersection over Union锛孖oU锛 float iou = computeIOU(rectSrc, rectComp); - - if (iou > overlap) { + // 濡傛灉IoU澶т簬璁惧畾鐨勯槇鍊紀verlap锛屽垯鍒犻櫎鍚庣画鐨凜Character瀵硅薄銆傚惁鍒欙紝缁х画妫鏌ヤ笅涓涓狢Character瀵硅薄 + if (iou > overlap) + { itc = inVec.erase(itc); - } else { + } + else + { ++itc; } } } } -// judge weather two CCharacter are nearly the same; - bool compareCharRect(const CCharacter &character1, const CCharacter &character2) { + // judge weather two CCharacter are nearly the same; + // 鐢ㄤ簬姣旇緝涓や釜CCharacter瀵硅薄锛堝彲鑳戒唬琛ㄤ竴绉嶅瓧绗﹀疄浣擄級鏄惁鐩镐技銆 + // 杩欎釜鍑芥暟涓昏渚濇嵁杩欎袱涓瓧绗﹀湪鍥惧儚涓殑浣嶇疆锛堢敱CCharacter::getCharacterPos()杩斿洖鐨凴ect瀵硅薄琛ㄧず锛夋潵纭畾瀹冧滑鏄惁鐩镐技 + bool compareCharRect(const CCharacter &character1, const CCharacter &character2) + { + // 缁撴瀯浣揜ect閫氬父鐢ㄤ簬琛ㄧず鐭╁舰鍖哄煙锛屽叾涓璽l()鍜宐r()鍒嗗埆杩斿洖鐭╁舰鍖哄煙鐨勫乏涓婅鍜屽彸涓嬭鐨勫潗鏍囷紝 + // 楂樺害锛坔eight锛夊拰瀹藉害锛坵idth锛夊垯琛ㄧず鐭╁舰鐨勯珮搴﹀拰瀹藉害 Rect rect1 = character1.getCharacterPos(); Rect rect2 = character2.getCharacterPos(); // the character in plate are similar height + // 鍒ゆ柇涓や釜瀛楃鐨勯珮搴︽槸鍚︾浉浼笺傝绠楀畠浠殑楂樺害宸殑缁濆鍊煎苟鐢ㄦ渶灏忕殑楂樺害褰掍竴鍖栵紝 + // 濡傛灉姣斿艰秴杩0.25锛屽垯鍑芥暟杩斿洖false琛ㄧず涓や釜瀛楃涓嶇浉浼 float width_1 = float(rect1.width); float height_1 = float(rect1.height); @@ -967,6 +1242,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { return false; // the character in plate are similar in the y-axis + // 鍒ゆ柇涓や釜瀛楃鍦▂杞翠笂鐨勪綅缃槸鍚︾浉浼笺傝绠楀畠浠乏涓婅鐨剏鍧愭爣宸殑缁濆鍊煎苟鐢ㄦ渶灏忕殑楂樺害褰掍竴鍖栵紝 + // 濡傛灉姣斿艰秴杩0.5锛屽垯鍑芥暟杩斿洖false琛ㄧず涓や釜瀛楃涓嶇浉浼 float y_1 = float(rect1.tl().y); float y_2 = float(rect2.tl().y); @@ -977,6 +1254,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { return false; // the character center in plate are not to near in the x-axis + // 妫鏌ヤ袱涓瓧绗﹀湪x杞翠笂鐨勪腑蹇冧綅缃槸鍚﹁繃杩戙傝绠楀畠浠腑蹇冪殑x鍧愭爣宸殑缁濆鍊煎苟鐢ㄦ渶灏忕殑楂樺害褰掍竴鍖栵紝 + // 濡傛灉姣斿煎皬浜0.25锛屽垯鍑芥暟杩斿洖false琛ㄧず涓や釜瀛楃涓嶇浉浼 float x_1 = float(rect1.tl().x + rect1.width / 2); float x_2 = float(rect2.tl().x + rect2.width / 2); @@ -987,6 +1266,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { return false; // the character in plate are near in the x-axis but not very near + // 妫鏌ヤ袱涓瓧绗﹀湪x杞翠笂鐨勪綅缃槸鍚﹁繃杩溿傝绠楀畠浠湪x杞翠笂璺濈鐨勫樊骞剁敤鏈灏忕殑楂樺害褰掍竴鍖栵紝 + // 濡傛灉姣斿艰秴杩1.0锛屽垯鍑芥暟杩斿洖false琛ㄧず涓や釜瀛楃涓嶇浉浼 float x_margin_left = float(min(rect1.br().x, rect2.br().x)); float x_margin_right = float(max(rect1.tl().x, rect2.tl().x)); @@ -996,29 +1277,43 @@ void clearBorder(const Mat &img, Rect& cropRect) { if (x_margin_diff_ratio > 1.0) return false; + // 濡傛灉鎵鏈夌殑妫鏌ラ兘閫氳繃锛屽嚱鏁板垯杩斿洖true琛ㄧず杩欎袱涓瓧绗︾浉浼 return true; } -//! merge chars to group, using the similarity + //! merge chars to group, using the similarity + // 瀹氫箟浜嗕竴涓悕涓 mergeCharToGroup 鐨勫嚱鏁帮紝 + // 璇ュ嚱鏁扮殑鍔熻兘鏄皢鐩镐技鐨勫瓧绗﹀悎骞朵负涓缁 void mergeCharToGroup(std::vector vecRect, - std::vector> &charGroupVec) { - + std::vector> &charGroupVec) + { + // 瀹氫箟涓涓 labels 鍚戦噺鏉ュ瓨鍌ㄦ瘡涓瓧绗︾殑绫诲埆鏍囩 std::vector labels; + // 浣跨敤 partition 鍑芥暟璁$畻瀛楃鐨勫垎鍖猴紝姣忎釜鍒嗗尯琛ㄧず涓缁勭浉浼肩殑瀛楃銆 + // partition 鍑芥暟鐨勭涓変釜鍙傛暟鏄竴涓嚱鏁版寚閽堬紝 + // 鐢ㄤ簬姣旇緝涓や釜 CCharacter 瀹炰緥鏄惁鐩镐技锛坈ompareCharRect锛夈 int numbers = 0; + if (vecRect.size() > 0) numbers = partition(vecRect, labels, &compareCharRect); - for (size_t j = 0; j < size_t(numbers); j++) { + // 閬嶅巻鎵鏈夌殑绫诲埆锛堝垎鍖猴級 + for (size_t j = 0; j < size_t(numbers); j++) + { + // 瀵逛簬姣忎竴涓被鍒紝鍒涘缓涓涓柊鐨 charGroup 鍚戦噺鏉ヨ〃绀轰竴涓瓧绗︾粍 std::vector charGroup; - for (size_t t = 0; t < vecRect.size(); t++) { + // 閬嶅巻鎵鏈夌殑瀛楃锛屽鏋滃瓧绗︾殑绫诲埆鏍囩鏄綋鍓嶇殑绫诲埆锛屽垯灏嗚瀛楃鍔犲叆瀛楃缁 + for (size_t t = 0; t < vecRect.size(); t++) + { int label = labels[t]; if (label == j) charGroup.push_back(vecRect[t]); } - + // 妫鏌ユ瘡涓瓧绗︾粍鐨勫ぇ灏(charGroup.size())锛屽鏋滃ぇ灏忓皬浜 2锛堝嵆鍙寘鍚竴涓瓧绗︼級锛 + // 鍒欎笉浼氬皢瀹冨姞鍏ュ埌杈撳嚭鐨勫瓧绗︾粍涓 (charGroupVec)銆備細缁х画璁$畻寰幆涓嬩竴涓瓧绗 if (charGroup.size() < 2) continue; @@ -1026,22 +1321,31 @@ void clearBorder(const Mat &img, Rect& cropRect) { } } - + // 鍦ㄨ緭鍏ョ殑鍥惧儚 (image) 涓婄粯鍒朵竴涓棆杞殑鐭╁舰 (rrect)銆 void rotatedRectangle(InputOutputArray image, RotatedRect rrect, const Scalar &color, int thickness, int lineType, - int shift) { + int shift) + { + // 瀹氫箟浜嗕竴涓 Point2f 绫诲瀷鐨勬暟缁勶紝鐢ㄤ簬瀛樺偍鐭╁舰鐨勫洓涓《鐐 Point2f rect_points[4]; rrect.points(rect_points); - for (int j = 0; j < 4; j++) { + // 寰幆閬嶅巻鐭╁舰鐨勫洓涓《鐐癸紝浣跨敤 OpenCV 涓殑 line 鍑芥暟灏嗘瘡涓瀵圭浉閭荤殑椤剁偣杩炴帴璧锋潵缁樺埗杈广 + // (j + 1) % 4 纭繚浜嗚繛鎺ョ殑鏄綋鍓嶇偣鍜屼笅涓涓偣 + for (int j = 0; j < 4; j++) + { cv::line(image, rect_points[j], rect_points[(j + 1) % 4], color, thickness, lineType, shift); } } - + // 鐢ㄤ簬澶勭悊涓浜涚瀛愬瓧绗︼紙鍙兘灏辨槸杞︾墝鐨勫瓧绗︼級銆 + // 瀹冩帴鏀朵竴绯诲垪鐨勮緭鍏ュ弬鏁帮紝澶ч儴鍒嗙敤浜庢帶鍒跺拰甯姪杩涜瀛楃鎼滅储鐨勬潯浠躲 + // 浠庢暣浣撲笂鐪嬶紝杩欐槸涓涓瓫閫夊苟鎺掑簭瀛楃锛堟寜缁欏畾鏉′欢锛夌殑杩囩▼ void searchWeakSeed(const std::vector &charVec, std::vector &mserCharacter, double thresh1, double thresh2, const Vec4f &line, Point &boundaryPoint, const Rect &maxrect, Rect &plateResult, Mat result, - CharSearchDirection searchDirection) { + CharSearchDirection searchDirection) + { + // 璁剧疆涓浜涘垵濮嬪硷紝鍖呮嫭鐩寸嚎鐨勬枩鐜 k锛岀嚎涓婃煇鐐 (x_1, y_1)锛岀┖鐨勬悳绱㈢粨鏋滃悜閲 searchWeakSeedVec float k = line[1] / line[0]; float x_1 = line[2]; float y_1 = line[3]; @@ -1049,30 +1353,40 @@ void clearBorder(const Mat &img, Rect& cropRect) { std::vector searchWeakSeedVec; searchWeakSeedVec.reserve(8); - for (auto weakSeed : charVec) { + // 閬嶅巻浼犲叆鐨 charVec 涓殑姣忎竴涓瀛愬瓧绗 weakSeed锛屽苟瀵瑰叾鍐呭杩涜涓浜涙鏌 for (auto weakSeed : charVec) + { Rect weakRect = weakSeed.getCharacterPos(); - //cv::rectangle(result, weakRect, Scalar(255, 0, 255)); + // cv::rectangle(result, weakRect, Scalar(255, 0, 255)); Point weakCenter(weakRect.tl().x + weakRect.width / 2, weakRect.tl().y + weakRect.height / 2); - float x_2 = (float) weakCenter.x; + float x_2 = (float)weakCenter.x; - if (searchDirection == CharSearchDirection::LEFT) { - if (weakCenter.x + weakRect.width / 2 > boundaryPoint.x) { + // 濡傛灉绉嶅瓙鍦ㄦ悳绱㈡柟鍚戯紙searchDirection锛夊搴旂殑杈圭晫锛坆oundaryPoint.x锛夌殑鍙︿竴渚у垯鐩存帴璺宠繃 + if (searchDirection == CharSearchDirection::LEFT) + { + if (weakCenter.x + weakRect.width / 2 > boundaryPoint.x) + { continue; } - } else if (searchDirection == CharSearchDirection::RIGHT) { - if (weakCenter.x - weakRect.width / 2 < boundaryPoint.x) { + } + else if (searchDirection == CharSearchDirection::RIGHT) + { + if (weakCenter.x - weakRect.width / 2 < boundaryPoint.x) + { continue; } } float y_2l = k * (x_2 - x_1) + y_1; - float y_2 = (float) weakCenter.y; + float y_2 = (float)weakCenter.y; float y_diff_ratio = abs(y_2l - y_2) / maxrect.height; - if (y_diff_ratio < thresh1) { + // 璁$畻weakCenter鐐瑰埌鐩寸嚎 (k, x_1, y_1) 鐨勫瀭鐩磋窛绂绘瘮鐜 y_diff_ratio銆 + // 濡傛灉姣旂巼灏忎簬闃堝 thresh1锛屽垯璁$畻 weakRect锛堝氨鏄瀛愬瓧绗︾殑杈圭晫鐭╁舰锛夌殑楂樺拰瀹戒笌 maxrect 鐭╁舰鐨勯珮鍜屽涔嬮棿鐨勫樊鍒瘮鐜囷紝 + if (y_diff_ratio < thresh1) + { float width_1 = float(maxrect.width); float height_1 = float(maxrect.height); @@ -1085,26 +1399,36 @@ void clearBorder(const Mat &img, Rect& cropRect) { float width_diff = abs(width_1 - width_2); double width_diff_ratio = width_diff / maxrect.width; - if (height_diff_ratio < thresh1 && width_diff_ratio < 0.5) { - //std::cout << "h" << height_diff_ratio << std::endl; - //std::cout << "w" << width_diff_ratio << std::endl; + // 濡傛灉涓や釜姣旂巼閮藉皬浜庣浉搴旈槇鍊硷紙thresh1 鍜 0.5锛夛紝鍒欏皢 weakSeed 娣诲姞鍒 searchWeakSeedVec + if (height_diff_ratio < thresh1 && width_diff_ratio < 0.5) + { + // std::cout << "h" << height_diff_ratio << std::endl; + // std::cout << "w" << width_diff_ratio << std::endl; searchWeakSeedVec.push_back(weakSeed); - } else { - + } + else + { } } } // form right to left to split - if (searchWeakSeedVec.size() != 0) { - if (searchDirection == CharSearchDirection::LEFT) { + // 鏍规嵁鎼滅储鏂瑰悜锛屽皢 searchWeakSeedVec 涓殑绉嶅瓙瀛楃鎸 x 鍊硷紙getCharacterPos().tl().x锛夎繘琛屾帓搴 + if (searchWeakSeedVec.size() != 0) + { + if (searchDirection == CharSearchDirection::LEFT) + { std::sort(searchWeakSeedVec.begin(), searchWeakSeedVec.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCharacterPos().tl().x > r2.getCharacterPos().tl().x; }); - } else if (searchDirection == CharSearchDirection::RIGHT) { + } + else if (searchDirection == CharSearchDirection::RIGHT) + { std::sort(searchWeakSeedVec.begin(), searchWeakSeedVec.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCharacterPos().tl().x < r2.getCharacterPos().tl().x; }); } @@ -1114,19 +1438,26 @@ void clearBorder(const Mat &img, Rect& cropRect) { Point firstWeakCenter(firstWeakRect.tl().x + firstWeakRect.width / 2, firstWeakRect.tl().y + firstWeakRect.height / 2); - float ratio = (float) abs(firstWeakCenter.x - boundaryPoint.x) / (float) maxrect.height; - if (ratio > thresh2) { - if (0) { + // 浠庢帓搴忓悗鐨勫悜閲 searchWeakSeedVec 涓彇鍑虹涓涓瀛愬瓧绗︼紙鍗虫渶宸︽垨鏈鍙充竴涓級銆 + // 濡傛灉鍏朵腑蹇冨埌杈圭晫鐐 boundaryPoint 鐨勬按骞宠窛绂诲崰鎬婚珮搴︾殑姣斾緥 ratio 澶т簬 thresh2锛屽垯杩斿洖 + float ratio = (float)abs(firstWeakCenter.x - boundaryPoint.x) / (float)maxrect.height; + if (ratio > thresh2) + { + if (0) + { std::cout << "search seed ratio:" << ratio << std::endl; } return; } + // 鍚﹀垯灏嗗叾娣诲姞鍒 mserCharacter锛屽苟灏嗚绉嶅瓙鐨 Rect 鍚堝苟鍒 plateResult锛屽苟鏇存柊 boundaryPoint mserCharacter.push_back(firstWeakSeed); plateResult |= firstWeakRect; boundaryPoint = firstWeakCenter; - for (size_t weakSeedIndex = 0; weakSeedIndex + 1 < searchWeakSeedVec.size(); weakSeedIndex++) { + // 閬嶅巻 searchWeakSeedVec 涓殑鍏朵粬绉嶅瓙瀛楃锛屽苟妫鏌ュ叾涓庡墠涓涓瓧绗︿箣闂寸殑 x 璺濈姣斾緥 x_margin_diff_ratio銆 + for (size_t weakSeedIndex = 0; weakSeedIndex + 1 < searchWeakSeedVec.size(); weakSeedIndex++) + { CCharacter weakSeed = searchWeakSeedVec[weakSeedIndex]; CCharacter weakSeedCompare = searchWeakSeedVec[weakSeedIndex + 1]; @@ -1150,21 +1481,31 @@ void clearBorder(const Mat &img, Rect& cropRect) { float x_margin_diff = abs(x_margin_left - x_margin_right); double x_margin_diff_ratio = x_margin_diff / min(height_1, height_2); - if (x_margin_diff_ratio > thresh2) { - if (0) { + // 濡傛灉 x_margin_diff_ratio 灏忎簬 thresh2锛屽垯灏嗗叾娣诲姞鍒 mserCharacter 骞舵洿鏂 boundaryPoint 鍜 plateResult + if (x_margin_diff_ratio > thresh2) + { + if (0) + { std::cout << "search seed x_margin_diff_ratio:" << x_margin_diff_ratio << std::endl; } break; - } else { + } + else + { //::rectangle(result, weakRect, Scalar(255, 0, 0), 1); mserCharacter.push_back(weakSeedCompare); plateResult |= weakRect; - if (searchDirection == CharSearchDirection::LEFT) { - if (weakCenter.x < boundaryPoint.x) { + if (searchDirection == CharSearchDirection::LEFT) + { + if (weakCenter.x < boundaryPoint.x) + { boundaryPoint = weakCenter; } - } else if (searchDirection == CharSearchDirection::RIGHT) { - if (weakCenter.x > boundaryPoint.x) { + } + else if (searchDirection == CharSearchDirection::RIGHT) + { + if (weakCenter.x > boundaryPoint.x) + { boundaryPoint = weakCenter; } } @@ -1173,10 +1514,13 @@ void clearBorder(const Mat &img, Rect& cropRect) { } } + // 鍦ㄤ竴寮犺溅鐗屽浘鍍忎笂娌跨壒瀹氱殑鐩寸嚎鎼滅储瀛楃銆傜粰瀹氫竴鏉$嚎锛堥氳繃鍏舵瀬鍧愭爣瀹氫箟锛夈佷竴涓悳绱㈡柟鍚戙佷互鍙婁竴浜涢厤缃弬鏁 void slideWindowSearch(const Mat &image, std::vector &slideCharacter, const Vec4f &line, Point &fromPoint, const Vec2i &dist, double ostu_level, float ratioWindow, float threshIsCharacter, const Rect &maxrect, Rect &plateResult, - CharSearchDirection searchDirection, bool isChinese, Mat &result) { + CharSearchDirection searchDirection, bool isChinese, Mat &result) + { + // 鏍规嵁缁欏畾鐨勭洿绾挎柟绋嬭绠楁悳绱㈢獥鍙g殑涓績浣嶇疆 float k = line[1] / line[0]; float x_1 = line[2]; float y_1 = line[3]; @@ -1184,22 +1528,33 @@ void clearBorder(const Mat &img, Rect& cropRect) { int slideLength = int(ratioWindow * maxrect.width); int slideStep = 1; int fromX = 0; - if (searchDirection == CharSearchDirection::LEFT) { + // 鏍规嵁鎼滅储鏂瑰悜锛堝乏/鍙筹級璁剧疆鎼滅储鐨勮捣濮 x 鍧愭爣 + if (searchDirection == CharSearchDirection::LEFT) + { fromX = fromPoint.x - dist[0]; - } else if (searchDirection == CharSearchDirection::RIGHT) { + } + else if (searchDirection == CharSearchDirection::RIGHT) + { fromX = fromPoint.x + dist[0]; } + // 鍦ㄦ寚瀹氱殑婊戝姩绐楀彛鑼冨洿鍐咃紝浠ユ闀 slideStep 瀵瑰浘鍍忚繘琛岄亶鍘 std::vector charCandidateVec; - for (int slideX = -slideLength; slideX < slideLength; slideX += slideStep) { + for (int slideX = -slideLength; slideX < slideLength; slideX += slideStep) + { + + // 璁$畻鍖哄煙鐨勪腑蹇冪偣 float x_slide = 0; - if (searchDirection == CharSearchDirection::LEFT) { + if (searchDirection == CharSearchDirection::LEFT) + { x_slide = float(fromX - slideX); - } else if (searchDirection == CharSearchDirection::RIGHT) { + } + else if (searchDirection == CharSearchDirection::RIGHT) + { x_slide = float(fromX + slideX); } - + // 鍦 result 鍥惧儚涓婄粯鍒跺渾鐐癸紝鍙敤浜庡彲瑙嗗寲 float y_slide = k * (x_slide - x_1) + y_1; Point2f p_slide(x_slide, y_slide); cv::circle(result, p_slide, 2, Scalar(255, 255, 255), 1); @@ -1207,26 +1562,30 @@ void clearBorder(const Mat &img, Rect& cropRect) { int chineseWidth = int(maxrect.width * 1.05); int chineseHeight = int(maxrect.height * 1.05); + // 鐢熸垚鍊欓夊尯鍩熺殑鐭╁舰妗 rect Rect rect(Point2f(x_slide - chineseWidth / 2, y_slide - chineseHeight / 2), Size(chineseWidth, chineseHeight)); + // 瀵圭煩褰㈡鍐呯殑鍥惧儚鍖哄煙杩涜妫鏌ヤ互纭繚娌℃湁瓒婄晫 if (rect.tl().x < 0 || rect.tl().y < 0 || rect.br().x >= image.cols || rect.br().y >= image.rows) continue; Mat region = image(rect); Mat binary_region; - + // 瀵圭煩褰㈡鍐呯殑鍥惧儚鍖哄煙杩涜浜屽煎寲澶勭悊 cv::threshold(region, binary_region, ostu_level, 255, CV_THRESH_BINARY); - //double ostu_level = threshold(region, binary_region, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); - //std::cout << "ostu_level:" << ostu_level << std::endl;*/ + // double ostu_level = threshold(region, binary_region, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); + // std::cout << "ostu_level:" << ostu_level << std::endl;*/ + // 瀵瑰瓧绗﹀浘鍍忚繘琛岄澶勭悊锛岃幏鍙栧噯澶囩敤浜庡垎绫荤殑鍥惧儚 Mat charInput = preprocessChar(binary_region, 20); - if (0) { + if (0) + { imshow("charInput", charInput); waitKey(0); destroyWindow("charInput"); } - + // 寤虹珛鎶婃娴嬪埌鐨勫欓夊尯鍩熷瓨鍌ㄤ负 CCharacter 瀵硅薄锛屽苟娣诲姞鍒板欓夊悜閲 charCandidateVec 涓 CCharacter charCandidate; charCandidate.setCharacterPos(rect); charCandidate.setCharacterMat(charInput); @@ -1234,26 +1593,38 @@ void clearBorder(const Mat &img, Rect& cropRect) { charCandidateVec.push_back(charCandidate); } - if (isChinese) { + // 鏍规嵁 isChinese 鏍囪瀵 charCandidateVec 涓殑鎵鏈夊欓夐」杩涜鍒嗙被 + if (isChinese) + { CharsIdentify::instance()->classifyChinese(charCandidateVec); - } else { + } + else + { CharsIdentify::instance()->classify(charCandidateVec); } + // 搴旂敤闈炴瀬澶ф姂鍒讹紝杩囨护閲嶅彔鐨勫欓夊尯鍩 double overlapThresh = 0.1; NMStoCharacter(charCandidateVec, overlapThresh); - for (auto character : charCandidateVec) { + // 閬嶅巻绛涢夊悗鐨勫欓夊瓧绗﹂泦锛屽浜庨偅浜涢珮浜庣壒瀹氬垎鏁伴槇鍊间笖涓嶆槸瀛楃 "1" 鐨勫尯鍩 + for (auto character : charCandidateVec) + { + // 鏇存柊 plateResult 骞舵坊鍔犲埌 slideCharacter 缁撴灉瀹瑰櫒涓 Rect rect = character.getCharacterPos(); + // 鏇存柊鎼滅储鐨勮捣濮嬬偣 fromPoint Point center(rect.tl().x + rect.width / 2, rect.tl().y + rect.height / 2); - if (character.getCharacterScore() > threshIsCharacter && character.getCharacterStr() != "1") { - //cv::rectangle(result, rect, Scalar(255, 255, 255), 1); + // 璇嗗埆缁撴灉瀛樹簬 slideCharacter 鍜 plateResult 鍙傛暟涓紝鍏朵腑 plateResult 涓鸿瘑鍒嚭鐨勮溅鐗屽尯鍩 + if (character.getCharacterScore() > threshIsCharacter && character.getCharacterStr() != "1") + { + // cv::rectangle(result, rect, Scalar(255, 255, 255), 1); plateResult |= rect; slideCharacter.push_back(character); fromPoint = center; - if (0) { + if (0) + { std::cout << "label:" << character.getCharacterStr(); std::cout << "__score:" << character.getCharacterScore() << std::endl; } @@ -1261,9 +1632,9 @@ void clearBorder(const Mat &img, Rect& cropRect) { } } - bool judegMDOratio2(const Mat &image, const Rect &rect, std::vector &contour, Mat &result, const float thresh, - bool useExtendHeight) { + bool useExtendHeight) + { Mat mser = image(rect); Mat mser_mat; @@ -1279,8 +1650,9 @@ void clearBorder(const Mat &img, Rect& cropRect) { float MserDiffOstuRatio = float(countdiff) / float(rect.area()); - if (MserDiffOstuRatio > thresh) { - //std::cout << "MserDiffOstuRatio:" << MserDiffOstuRatio << std::endl; + if (MserDiffOstuRatio > thresh) + { + // std::cout << "MserDiffOstuRatio:" << MserDiffOstuRatio << std::endl; /*imshow("tmpMat", mser_mat); waitKey(0); imshow("tmpMat", thresh_mat); @@ -1293,7 +1665,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { return true; } - Rect interRect(const Rect &a, const Rect &b) { + Rect interRect(const Rect &a, const Rect &b) + { Rect c; int x1 = a.x > b.x ? a.x : b.x; int y1 = a.y > b.y ? a.y : b.y; @@ -1306,7 +1679,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { return c; } - Rect mergeRect(const Rect &a, const Rect &b) { + Rect mergeRect(const Rect &a, const Rect &b) + { Rect c; int x1 = a.x < b.x ? a.x : b.x; int y1 = a.y < b.y ? a.y : b.y; @@ -1318,7 +1692,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { } bool computeIOU(const RotatedRect &rrect1, const RotatedRect &rrect2, const int width, const int height, - const float thresh, float &result) { + const float thresh, float &result) + { Rect_ safe_rect1; calcSafeRect(rrect1, width, height, safe_rect1); @@ -1328,18 +1703,20 @@ void clearBorder(const Mat &img, Rect& cropRect) { Rect inter = interRect(safe_rect1, safe_rect2); Rect urect = mergeRect(safe_rect1, safe_rect2); - float iou = (float) inter.area() / (float) urect.area(); + float iou = (float)inter.area() / (float)urect.area(); result = iou; - if (iou > thresh) { + if (iou > thresh) + { return true; } return false; } - float computeIOU(const RotatedRect &rrect1, const RotatedRect &rrect2, const int width, const int height) { + float computeIOU(const RotatedRect &rrect1, const RotatedRect &rrect2, const int width, const int height) + { Rect_ safe_rect1; calcSafeRect(rrect1, width, height, safe_rect1); @@ -1349,51 +1726,56 @@ void clearBorder(const Mat &img, Rect& cropRect) { Rect inter = interRect(safe_rect1, safe_rect2); Rect urect = mergeRect(safe_rect1, safe_rect2); - float iou = (float) inter.area() / (float) urect.area(); - //std::cout << "iou" << iou << std::endl; + float iou = (float)inter.area() / (float)urect.area(); + // std::cout << "iou" << iou << std::endl; return iou; } - bool computeIOU(const Rect &rect1, const Rect &rect2, const float thresh, float &result) { + bool computeIOU(const Rect &rect1, const Rect &rect2, const float thresh, float &result) + { Rect inter = interRect(rect1, rect2); Rect urect = mergeRect(rect1, rect2); - float iou = (float) inter.area() / (float) urect.area(); + float iou = (float)inter.area() / (float)urect.area(); result = iou; - if (iou > thresh) { + if (iou > thresh) + { return true; } return false; } - float computeIOU(const Rect &rect1, const Rect &rect2) { + float computeIOU(const Rect &rect1, const Rect &rect2) + { Rect inter = interRect(rect1, rect2); Rect urect = mergeRect(rect1, rect2); - float iou = (float) inter.area() / (float) urect.area(); + float iou = (float)inter.area() / (float)urect.area(); return iou; } - -// the slope are nealy the same along the line -// if one slope is much different others, it should be outliers -// this function to remove it + // the slope are nealy the same along the line + // if one slope is much different others, it should be outliers + // this function to remove it void removeRightOutliers(std::vector &charGroup, std::vector &out_charGroup, double thresh1, - double thresh2, Mat result) { + double thresh2, Mat result) + { std::sort(charGroup.begin(), charGroup.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCenterPoint().x < r2.getCenterPoint().x; }); std::vector slopeVec; float slope_last = 0; - for (size_t charGroup_i = 0; charGroup_i + 1 < charGroup.size(); charGroup_i++) { + for (size_t charGroup_i = 0; charGroup_i + 1 < charGroup.size(); charGroup_i++) + { // line_between_two_points Vec4f line_btp; CCharacter leftChar = charGroup.at(charGroup_i); @@ -1405,52 +1787,64 @@ void clearBorder(const Mat &img, Rect& cropRect) { float slope = line_btp[1] / line_btp[0]; slopeVec.push_back(slope); - if (0) { + if (0) + { cv::line(result, leftChar.getCenterPoint(), rightChar.getCenterPoint(), Scalar(0, 0, 255)); } } int uniformity_count = 0; int outlier_index = -1; - for (size_t slopeVec_i = 0; slopeVec_i + 1 < slopeVec.size(); slopeVec_i++) { + for (size_t slopeVec_i = 0; slopeVec_i + 1 < slopeVec.size(); slopeVec_i++) + { float slope_1 = slopeVec.at(slopeVec_i); float slope_2 = slopeVec.at(slopeVec_i + 1); float slope_diff = abs(slope_1 - slope_2); - if (0) { + if (0) + { std::cout << "slope_diff:" << slope_diff << std::endl; } - if (slope_diff <= thresh1) { + if (slope_diff <= thresh1) + { uniformity_count++; } - if (0) { + if (0) + { std::cout << "slope_1:" << slope_1 << std::endl; std::cout << "slope_2:" << slope_2 << std::endl; } - if (1/*(slope_1 <= 0 && slope_2 >= 0) || (slope_1 >= 0 && slope_2 <= 0)*/) { - if (uniformity_count >= 2 && slope_diff >= thresh2) { + if (1 /*(slope_1 <= 0 && slope_2 >= 0) || (slope_1 >= 0 && slope_2 <= 0)*/) + { + if (uniformity_count >= 2 && slope_diff >= thresh2) + { outlier_index = slopeVec_i + 2; break; } } } - if (0) { + if (0) + { std::cout << "uniformity_count:" << uniformity_count << std::endl; std::cout << "outlier_index:" << outlier_index << std::endl; } - for (int charGroup_i = 0; charGroup_i < (int) charGroup.size(); charGroup_i++) { - if (charGroup_i != outlier_index) { + for (int charGroup_i = 0; charGroup_i < (int)charGroup.size(); charGroup_i++) + { + if (charGroup_i != outlier_index) + { CCharacter theChar = charGroup.at(charGroup_i); out_charGroup.push_back(theChar); } } - if (0) { + if (0) + { std::cout << "end:" << std::endl; } } - Rect getSafeRect(Point2f center, float width, float height, Mat image) { + Rect getSafeRect(Point2f center, float width, float height, Mat image) + { int rows = image.rows; int cols = image.cols; @@ -1465,35 +1859,40 @@ void clearBorder(const Mat &img, Rect& cropRect) { x_tl = x_tl > 0.f ? x_tl : 0.f; y_tl = y_tl > 0.f ? y_tl : 0.f; - x_br = x_br < (float) image.cols ? x_br : (float) image.cols; - y_br = y_br < (float) image.rows ? y_br : (float) image.rows; + x_br = x_br < (float)image.cols ? x_br : (float)image.cols; + y_br = y_br < (float)image.rows ? y_br : (float)image.rows; - Rect rect(Point((int) x_tl, int(y_tl)), Point((int) x_br, int(y_br))); + Rect rect(Point((int)x_tl, int(y_tl)), Point((int)x_br, int(y_br))); return rect; } -// based on the assumptions: distance beween two nearby characters in plate are the same. -// add not found rect and combine two small and near rect. + // based on the assumptions: distance beween two nearby characters in plate are the same. + // add not found rect and combine two small and near rect. void reFoundAndCombineRect(std::vector &mserCharacter, float min_thresh, float max_thresh, - Vec2i dist, Rect maxrect, Mat result) { - if (mserCharacter.size() == 0) { + Vec2i dist, Rect maxrect, Mat result) + { + if (mserCharacter.size() == 0) + { return; } std::sort(mserCharacter.begin(), mserCharacter.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCenterPoint().x < r2.getCenterPoint().x; }); int comparDist = dist[0] * dist[0] + dist[1] * dist[1]; - if (0) { + if (0) + { std::cout << "comparDist:" << comparDist << std::endl; } std::vector reCharacters; size_t mserCharacter_i = 0; - for (; mserCharacter_i + 1 < mserCharacter.size(); mserCharacter_i++) { + for (; mserCharacter_i + 1 < mserCharacter.size(); mserCharacter_i++) + { CCharacter leftChar = mserCharacter.at(mserCharacter_i); CCharacter rightChar = mserCharacter.at(mserCharacter_i + 1); @@ -1506,35 +1905,40 @@ void clearBorder(const Mat &img, Rect& cropRect) { // distance between two centers int distance2 = x_diff * x_diff + y_diff * y_diff; - if (0) { + if (0) + { std::cout << "distance2:" << distance2 << std::endl; } - float ratio = (float) distance2 / (float) comparDist; - if (ratio > max_thresh) { - float x_add = (float) (leftCenter.x + rightCenter.x) / 2.f; - float y_add = (float) (leftCenter.y + rightCenter.y) / 2.f; + float ratio = (float)distance2 / (float)comparDist; + if (ratio > max_thresh) + { + float x_add = (float)(leftCenter.x + rightCenter.x) / 2.f; + float y_add = (float)(leftCenter.y + rightCenter.y) / 2.f; - float width = (float) maxrect.width; - float height = (float) maxrect.height; + float width = (float)maxrect.width; + float height = (float)maxrect.height; float x_tl = (x_add - width / 2.f); float y_tl = (y_add - height / 2.f); - //Rect rect_add((int)x_tl, (int)y_tl, (int)width, (int)height); + // Rect rect_add((int)x_tl, (int)y_tl, (int)width, (int)height); Rect rect_add = getSafeRect(Point2f(x_add, y_add), width, height, result); reCharacters.push_back(leftChar); CCharacter charAdd; - charAdd.setCenterPoint(Point((int) x_add, (int) y_add)); + charAdd.setCenterPoint(Point((int)x_add, (int)y_add)); charAdd.setCharacterPos(rect_add); reCharacters.push_back(charAdd); - if (1) { + if (1) + { cv::rectangle(result, rect_add, Scalar(0, 128, 255)); } - } else if (ratio < min_thresh) { + } + else if (ratio < min_thresh) + { Rect rect_union = leftChar.getCharacterPos() | rightChar.getCharacterPos(); /*float x_add = (float)(leftCenter.x + rightCenter.x) / 2.f; float y_add = (float)(leftCenter.y + rightCenter.y) / 2.f;*/ @@ -1545,28 +1949,33 @@ void clearBorder(const Mat &img, Rect& cropRect) { charAdd.setCenterPoint(Point(x_add, y_add)); charAdd.setCharacterPos(rect_union); reCharacters.push_back(charAdd); - if (1) { + if (1) + { cv::rectangle(result, rect_union, Scalar(0, 128, 255)); } mserCharacter_i++; - } else { + } + else + { reCharacters.push_back(leftChar); } } - if (mserCharacter_i + 1 == mserCharacter.size()) { + if (mserCharacter_i + 1 == mserCharacter.size()) + { reCharacters.push_back(mserCharacter.at(mserCharacter_i)); } mserCharacter = reCharacters; } - - void removeOutliers(std::vector &charGroup, double thresh, Mat result) { + void removeOutliers(std::vector &charGroup, double thresh, Mat result) + { std::vector points; Vec4f line; - for (auto character : charGroup) { + for (auto character : charGroup) + { points.push_back(character.getCenterPoint()); } @@ -1582,34 +1991,40 @@ void clearBorder(const Mat &img, Rect& cropRect) { float b = -1; float c = y_1 - k * x_1; float sumdistance = 0; - for (auto character : charGroup) { + for (auto character : charGroup) + { Point center = character.getCenterPoint(); float distance = (a * center.x + b * center.y + c) / std::sqrt(a * a + b * b); std::cout << "distance:" << distance << std::endl; sumdistance += distance; } - float avgdistance = sumdistance / (float) charGroup.size(); + float avgdistance = sumdistance / (float)charGroup.size(); std::vector::iterator it = charGroup.begin(); - for (; it != charGroup.end();) { + for (; it != charGroup.end();) + { Point center = it->getCenterPoint(); float distance = a * center.x + b * center.y + c; float ratio = distance / avgdistance; std::cout << "ratio:" << ratio << std::endl; - if (ratio > (float) thresh) { + if (ratio > (float)thresh) + { it = charGroup.erase(it); - } else { + } + else + { ++it; } } } -//! use verify size to first generate char candidates + //! use verify size to first generate char candidates void mserCharMatch(const Mat &src, std::vector &match, std::vector &out_plateVec_blue, std::vector &out_plateVec_yellow, bool usePlateMser, std::vector &out_plateRRect_blue, std::vector &out_plateRRect_yellow, int img_index, - bool showDebug) { + bool showDebug) + { Mat image = src; std::vector>> all_contours; @@ -1629,7 +2044,7 @@ void clearBorder(const Mat &img, Rect& cropRect) { const int imageArea = image.rows * image.cols; const int delta = 1; - //const int delta = CParams::instance()->getParam2i();; + // const int delta = CParams::instance()->getParam2i();; const int minArea = 30; const double maxAreaRatio = 0.05; @@ -1642,7 +2057,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { // color_index = 1 : mser+, detect dark characters, which is in yellow plate. #pragma omp parallel for - for (int color_index = 0; color_index < 2; color_index++) { + for (int color_index = 0; color_index < 2; color_index++) + { Color the_color = flags.at(color_index); std::vector charVec; @@ -1662,23 +2078,29 @@ void clearBorder(const Mat &img, Rect& cropRect) { const int char_max_count = 7; // verify char size and output to rects; - for (size_t index = 0; index < size; index++) { + for (size_t index = 0; index < size; index++) + { Rect rect = all_boxes.at(color_index)[index]; std::vector &contour = all_contours.at(color_index)[index]; // sometimes a plate could be a mser rect, so we could // also use mser algorithm to find plate - if (usePlateMser) { + if (usePlateMser) + { RotatedRect rrect = minAreaRect(Mat(contour)); - if (verifyRotatedPlateSizes(rrect)) { - //rotatedRectangle(result, rrect, Scalar(255, 0, 0), 2); - if (the_color == BLUE) out_plateRRect_blue.push_back(rrect); - if (the_color == YELLOW) out_plateRRect_yellow.push_back(rrect); + if (verifyRotatedPlateSizes(rrect)) + { + // rotatedRectangle(result, rrect, Scalar(255, 0, 0), 2); + if (the_color == BLUE) + out_plateRRect_blue.push_back(rrect); + if (the_color == YELLOW) + out_plateRRect_yellow.push_back(rrect); } } // find character - if (verifyCharSizes(rect)) { + if (verifyCharSizes(rect)) + { Mat mserMat = adaptive_image_from_points(contour, rect, Size(char_size, char_size)); Mat charInput = preprocessChar(mserMat, char_size); Rect charRect = rect; @@ -1687,11 +2109,12 @@ void clearBorder(const Mat &img, Rect& cropRect) { Mat tmpMat; double ostu_level = cv::threshold(image(charRect), tmpMat, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); - //cv::circle(result, center, 3, Scalar(0, 0, 255), 2); + // cv::circle(result, center, 3, Scalar(0, 0, 255), 2); // use judegMDOratio2 function to // remove the small lines in character like "zh-cuan" - if (judegMDOratio2(image, rect, contour, result)) { + if (judegMDOratio2(image, rect, contour, result)) + { CCharacter charCandidate; charCandidate.setCharacterPos(charRect); charCandidate.setCharacterMat(charInput); @@ -1713,7 +2136,7 @@ void clearBorder(const Mat &img, Rect& cropRect) { // use nms to remove the character are not likely to be true. double overlapThresh = 0.6; - //double overlapThresh = CParams::instance()->getParam1f(); + // double overlapThresh = CParams::instance()->getParam1f(); NMStoCharacter(charVec, overlapThresh); charVec.shrink_to_fit(); @@ -1724,19 +2147,25 @@ void clearBorder(const Mat &img, Rect& cropRect) { std::vector littleSeedVec; littleSeedVec.reserve(64); - //size_t charCan_size = charVec.size(); - for (auto charCandidate : charVec) { - //CCharacter& charCandidate = charVec[char_index]; + // size_t charCan_size = charVec.size(); + for (auto charCandidate : charVec) + { + // CCharacter& charCandidate = charVec[char_index]; Rect rect = charCandidate.getCharacterPos(); double score = charCandidate.getCharacterScore(); - if (charCandidate.getIsStrong()) { + if (charCandidate.getIsStrong()) + { strongSeedVec.push_back(charCandidate); - } else if (charCandidate.getIsWeak()) { + } + else if (charCandidate.getIsWeak()) + { weakSeedVec.push_back(charCandidate); - //cv::rectangle(result, rect, Scalar(255, 0, 255)); - } else if (charCandidate.getIsLittle()) { + // cv::rectangle(result, rect, Scalar(255, 0, 255)); + } + else if (charCandidate.getIsLittle()) + { littleSeedVec.push_back(charCandidate); - //cv::rectangle(result, rect, Scalar(255, 0, 255)); + // cv::rectangle(result, rect, Scalar(255, 0, 255)); } } @@ -1758,7 +2187,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { // to fit a line which is the middle line of the plate. std::vector plateVec; plateVec.reserve(16); - for (auto charGroup : charGroupVec) { + for (auto charGroup : charGroupVec) + { Rect plateResult = charGroup[0].getCharacterPos(); std::vector points; points.reserve(32); @@ -1781,9 +2211,10 @@ void clearBorder(const Mat &img, Rect& cropRect) { roCharGroup.reserve(32); removeRightOutliers(charGroup, roCharGroup, 0.2, 0.5, result); - //roCharGroup = charGroup; + // roCharGroup = charGroup; - for (auto character : roCharGroup) { + for (auto character : roCharGroup) + { Rect charRect = character.getCharacterPos(); cv::rectangle(result, charRect, Scalar(0, 255, 0), 1); plateResult |= charRect; @@ -1791,40 +2222,46 @@ void clearBorder(const Mat &img, Rect& cropRect) { Point center(charRect.tl().x + charRect.width / 2, charRect.tl().y + charRect.height / 2); points.push_back(center); mserCharVec.push_back(character); - //cv::circle(result, center, 3, Scalar(0, 255, 0), 2); + // cv::circle(result, center, 3, Scalar(0, 255, 0), 2); ostu_level_sum += character.getOstuLevel(); - if (charRect.area() > maxarea) { + if (charRect.area() > maxarea) + { maxrect = charRect; maxarea = charRect.area(); } - if (center.x < leftPoint.x) { + if (center.x < leftPoint.x) + { leftPoint = center; } - if (center.x > rightPoint.x) { + if (center.x > rightPoint.x) + { rightPoint = center; } } - double ostu_level_avg = ostu_level_sum / (double) roCharGroup.size(); - if (1 && showDebug) { + double ostu_level_avg = ostu_level_sum / (double)roCharGroup.size(); + if (1 && showDebug) + { std::cout << "ostu_level_avg:" << ostu_level_avg << std::endl; } - float ratio_maxrect = (float) maxrect.width / (float) maxrect.height; + float ratio_maxrect = (float)maxrect.width / (float)maxrect.height; - if (points.size() >= 2 && ratio_maxrect >= 0.3) { + if (points.size() >= 2 && ratio_maxrect >= 0.3) + { fitLine(Mat(points), line, CV_DIST_L2, 0, 0.01, 0.01); float k = line[1] / line[0]; - //float angle = atan(k) * 180 / (float)CV_PI; - //std::cout << "k:" << k << std::endl; - //std::cout << "angle:" << angle << std::endl; - //std::cout << "cos:" << 0.3 * cos(k) << std::endl; - //std::cout << "ratio_maxrect:" << ratio_maxrect << std::endl; + // float angle = atan(k) * 180 / (float)CV_PI; + // std::cout << "k:" << k << std::endl; + // std::cout << "angle:" << angle << std::endl; + // std::cout << "cos:" << 0.3 * cos(k) << std::endl; + // std::cout << "ratio_maxrect:" << ratio_maxrect << std::endl; std::sort(mserCharVec.begin(), mserCharVec.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCharacterPos().tl().x < r2.getCharacterPos().tl().x; }); @@ -1843,7 +2280,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { // two near characters in the plate, use dist we can // judege how to computer the max search range, and choose the // best location of the sliding window in the next steps. - for (size_t mser_i = 0; mser_i + 1 < mserCharVec.size(); mser_i++) { + for (size_t mser_i = 0; mser_i + 1 < mserCharVec.size(); mser_i++) + { Rect charRect = mserCharVec.at(mser_i).getCharacterPos(); Point center(charRect.tl().x + charRect.width / 2, charRect.tl().y + charRect.height / 2); @@ -1855,27 +2293,28 @@ void clearBorder(const Mat &img, Rect& cropRect) { Vec2i distVec(charRectCompare.x - charRect.x, charRectCompare.y - charRect.y); distVecVec.push_back(distVec); - //if (dist < mindist) { - // mindist = dist; - // mindistVec = distVec; - //} + // if (dist < mindist) { + // mindist = dist; + // mindistVec = distVec; + // } } std::sort(distVecVec.begin(), distVecVec.end(), - [](const Vec2i &r1, const Vec2i &r2) { + [](const Vec2i &r1, const Vec2i &r2) + { return r1[0] < r2[0]; }); avgdistVec = distVecVec.at(int((distVecVec.size() - 1) / 2.f)); - //float step = 10.f * (float)maxrect.width; - //float step = (float)mindistVec[0]; - float step = (float) avgdistVec[0]; + // float step = 10.f * (float)maxrect.width; + // float step = (float)mindistVec[0]; + float step = (float)avgdistVec[0]; - //cv::line(result, Point2f(line[2] - step, line[3] - k*step), Point2f(line[2] + step, k*step + line[3]), Scalar(255, 255, 255)); + // cv::line(result, Point2f(line[2] - step, line[3] - k*step), Point2f(line[2] + step, k*step + line[3]), Scalar(255, 255, 255)); cv::line(result, Point2f(midCenter.x - step, midCenter.y - k * step), Point2f(midCenter.x + step, k * step + midCenter.y), Scalar(255, 255, 255)); - //cv::circle(result, leftPoint, 3, Scalar(0, 0, 255), 2); + // cv::circle(result, leftPoint, 3, Scalar(0, 0, 255), 2); CPlate plate; plate.setPlateLeftPoint(leftPoint); @@ -1897,7 +2336,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { // because we use strong seed to build the middle lines of the plate, // we can simply use this to consider weak seeds only lie in the // near place of the middle line - for (auto plate : plateVec) { + for (auto plate : plateVec) + { Vec4f line = plate.getPlateLine(); Point leftPoint = plate.getPlateLeftPoint(); Point rightPoint = plate.getPlateRightPoint(); @@ -1929,16 +2369,20 @@ void clearBorder(const Mat &img, Rect& cropRect) { // draw weak seed and little seed from line; // search for mser rect - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "search for mser rect:" << std::endl; } - if (0 && showDebug) { + if (0 && showDebug) + { std::stringstream ss(std::stringstream::in | std::stringstream::out); - ss << "resources/image/tmp/" << img_index << "_1_" << "searcgMserRect.jpg"; + ss << "resources/image/tmp/" << img_index << "_1_" + << "searcgMserRect.jpg"; imwrite(ss.str(), result); } - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "mserCharacter:" << mserCharacter.size() << std::endl; } @@ -1947,25 +2391,30 @@ void clearBorder(const Mat &img, Rect& cropRect) { // the same line as the strong seed. The judge condition contains the distance // between strong seed and weak seed , and the rect simily of each other to improve // the roubustnedd of the seed growing algorithm. - if (mserCharacter.size() < char_max_count) { + if (mserCharacter.size() < char_max_count) + { double thresh1 = 0.15; double thresh2 = 2.0; searchWeakSeed(searchCandidate, searchRightWeakSeed, thresh1, thresh2, line, rightPoint, maxrect, plateResult, result, CharSearchDirection::RIGHT); - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "searchRightWeakSeed:" << searchRightWeakSeed.size() << std::endl; } - for (auto seed : searchRightWeakSeed) { + for (auto seed : searchRightWeakSeed) + { cv::rectangle(result, seed.getCharacterPos(), Scalar(255, 0, 0), 1); mserCharacter.push_back(seed); } searchWeakSeed(searchCandidate, searchLeftWeakSeed, thresh1, thresh2, line, leftPoint, maxrect, plateResult, result, CharSearchDirection::LEFT); - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "searchLeftWeakSeed:" << searchLeftWeakSeed.size() << std::endl; } - for (auto seed : searchLeftWeakSeed) { + for (auto seed : searchLeftWeakSeed) + { cv::rectangle(result, seed.getCharacterPos(), Scalar(255, 0, 0), 1); mserCharacter.push_back(seed); } @@ -1986,8 +2435,10 @@ void clearBorder(const Mat &img, Rect& cropRect) { // sometimes there are still some characters could not be captured by mser algorithm, // such as blur, low light ,and some chinese characters like zh-cuan. // to handle this ,we use a simple slide window method to find them. - if (mserCharacter.size() < char_max_count) { - if (1 && showDebug) { + if (mserCharacter.size() < char_max_count) + { + if (1 && showDebug) + { std::cout << "search chinese:" << std::endl; std::cout << "judege the left is chinese:" << std::endl; } @@ -1998,29 +2449,33 @@ void clearBorder(const Mat &img, Rect& cropRect) { // the first thing is to judge the left charcater is // or not the chinese. bool leftIsChinese = false; - if (1) { + if (1) + { std::sort(mserCharacter.begin(), mserCharacter.end(), - [](const CCharacter &r1, const CCharacter &r2) { + [](const CCharacter &r1, const CCharacter &r2) + { return r1.getCharacterPos().tl().x < r2.getCharacterPos().tl().x; }); CCharacter leftChar = mserCharacter[0]; - //Rect theRect = adaptive_charrect_from_rect(leftChar.getCharacterPos(), image.cols, image.rows); + // Rect theRect = adaptive_charrect_from_rect(leftChar.getCharacterPos(), image.cols, image.rows); Rect theRect = leftChar.getCharacterPos(); - //cv::rectangle(result, theRect, Scalar(255, 0, 0), 1); + // cv::rectangle(result, theRect, Scalar(255, 0, 0), 1); Mat region = image(theRect); Mat binary_region; ostu_level = cv::threshold(region, binary_region, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "left : ostu_level:" << ostu_level << std::endl; } - //plate.setOstuLevel(ostu_level); + // plate.setOstuLevel(ostu_level); Mat charInput = preprocessChar(binary_region, char_size); - if (0 /*&& showDebug*/) { + if (0 /*&& showDebug*/) + { imshow("charInput", charInput); waitKey(0); destroyWindow("charInput"); @@ -2029,9 +2484,10 @@ void clearBorder(const Mat &img, Rect& cropRect) { std::string label = ""; float maxVal = -2.f; leftIsChinese = CharsIdentify::instance()->isCharacter(charInput, label, maxVal, true); - //auto character = CharsIdentify::instance()->identifyChinese(charInput, maxVal, leftIsChinese); - //label = character.second; - if (0 /* && showDebug*/) { + // auto character = CharsIdentify::instance()->identifyChinese(charInput, maxVal, leftIsChinese); + // label = character.second; + if (0 /* && showDebug*/) + { std::cout << "isChinese:" << leftIsChinese << std::endl; std::cout << "chinese:" << label; std::cout << "__score:" << maxVal << std::endl; @@ -2042,16 +2498,19 @@ void clearBorder(const Mat &img, Rect& cropRect) { // this means we meed to slide a window to find the missed mser rect. // search for sliding window float ratioWindow = 0.4f; - //float ratioWindow = CParams::instance()->getParam3f(); + // float ratioWindow = CParams::instance()->getParam3f(); float threshIsCharacter = 0.8f; - //float threshIsCharacter = CParams::instance()->getParam3f(); - if (!leftIsChinese) { + // float threshIsCharacter = CParams::instance()->getParam3f(); + if (!leftIsChinese) + { slideWindowSearch(image, slideLeftWindow, line, leftPoint, dist, ostu_level, ratioWindow, threshIsCharacter, maxrect, plateResult, CharSearchDirection::LEFT, true, result); - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "slideLeftWindow:" << slideLeftWindow.size() << std::endl; } - for (auto window : slideLeftWindow) { + for (auto window : slideLeftWindow) + { cv::rectangle(result, window.getCharacterPos(), Scalar(0, 0, 255), 1); mserCharacter.push_back(window); } @@ -2060,54 +2519,63 @@ void clearBorder(const Mat &img, Rect& cropRect) { // if we still have less than max count characters, // we need to slide a window to right to search for the missed mser rect. - if (mserCharacter.size() < char_max_count) { + if (mserCharacter.size() < char_max_count) + { // change ostu_level float ratioWindow = 0.4f; - //float ratioWindow = CParams::instance()->getParam3f(); + // float ratioWindow = CParams::instance()->getParam3f(); float threshIsCharacter = 0.8f; - //float threshIsCharacter = CParams::instance()->getParam3f(); + // float threshIsCharacter = CParams::instance()->getParam3f(); slideWindowSearch(image, slideRightWindow, line, rightPoint, dist, plate.getOstuLevel(), ratioWindow, threshIsCharacter, maxrect, plateResult, CharSearchDirection::RIGHT, false, result); - if (1 && showDebug) { + if (1 && showDebug) + { std::cout << "slideRightWindow:" << slideRightWindow.size() << std::endl; } - for (auto window : slideRightWindow) { + for (auto window : slideRightWindow) + { cv::rectangle(result, window.getCharacterPos(), Scalar(0, 0, 255), 1); mserCharacter.push_back(window); } } // computer the plate angle - float angle = atan(k) * 180 / (float) CV_PI; - if (1 && showDebug) { + float angle = atan(k) * 180 / (float)CV_PI; + if (1 && showDebug) + { std::cout << "k:" << k << std::endl; std::cout << "angle:" << angle << std::endl; } // the plateResult rect need to be enlarge to contains all the plate, // not only the character area. - float widthEnlargeRatio = 1.15f; //1.15f; - float heightEnlargeRatio = 1.25f; //1.25f; + float widthEnlargeRatio = 1.15f; // 1.15f; + float heightEnlargeRatio = 1.25f; // 1.25f; RotatedRect platePos( - Point2f((float) plateResult.x + plateResult.width / 2.f, (float) plateResult.y + plateResult.height / 2.f), + Point2f((float)plateResult.x + plateResult.width / 2.f, (float)plateResult.y + plateResult.height / 2.f), Size2f(plateResult.width * widthEnlargeRatio, maxrect.height * heightEnlargeRatio), angle); // justify the size is likely to be a plate size. - if (verifyRotatedPlateSizes(platePos)) { + if (verifyRotatedPlateSizes(platePos)) + { rotatedRectangle(result, platePos, Scalar(0, 0, 255), 1); plate.setPlatePos(platePos); plate.setPlateColor(the_color); plate.setPlateLocateType(CMSER); - if (the_color == BLUE) out_plateVec_blue.push_back(plate); - if (the_color == YELLOW) out_plateVec_yellow.push_back(plate); + if (the_color == BLUE) + out_plateVec_blue.push_back(plate); + if (the_color == YELLOW) + out_plateVec_yellow.push_back(plate); } // use deskew to rotate the image, so we need the binary image. - if (1) { - for (auto mserChar : mserCharacter) { + if (1) + { + for (auto mserChar : mserCharacter) + { Rect rect = mserChar.getCharacterPos(); match.at(color_index)(rect) = 255; } @@ -2115,109 +2583,142 @@ void clearBorder(const Mat &img, Rect& cropRect) { } } - if (0 /*&& showDebug*/) { + if (0 /*&& showDebug*/) + { imshow("result", result); waitKey(0); destroyWindow("result"); } - if (0) { + if (0) + { imshow("match", match.at(color_index)); waitKey(0); destroyWindow("match"); } - if (1) { + if (1) + { std::stringstream ss(std::stringstream::in | std::stringstream::out); ss << "resources/image/tmp/plateDetect/plate_" << img_index << "_" << the_color << ".jpg"; imwrite(ss.str(), result); } } - - } -// this spatial_ostu algorithm are robust to -// the plate which has the same light shine, which is that -// the light in the left of the plate is strong than the right. - void spatial_ostu(InputArray _src, int grid_x, int grid_y, Color type) { + // this spatial_ostu algorithm are robust to + // the plate which has the same light shine, which is that + // the light in the left of the plate is strong than the right. + void spatial_ostu(InputArray _src, int grid_x, int grid_y, Color type) + { Mat src = _src.getMat(); int width = src.cols / grid_x; int height = src.rows / grid_y; // iterate through grid - for (int i = 0; i < grid_y; i++) { - for (int j = 0; j < grid_x; j++) { + for (int i = 0; i < grid_y; i++) + { + for (int j = 0; j < grid_x; j++) + { Mat src_cell = Mat(src, Range(i * height, (i + 1) * height), Range(j * width, (j + 1) * width)); - if (type == BLUE) { + if (type == BLUE) + { cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); - } else if (type == YELLOW) { + } + else if (type == YELLOW) + { cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); - } else if (type == WHITE) { + } + else if (type == WHITE) + { cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); - } else { + } + else + { cv::threshold(src_cell, src_cell, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); } } } } - - bool mat_valid_position(const Mat &mat, int row, int col) { + bool mat_valid_position(const Mat &mat, int row, int col) + { return row >= 0 && col >= 0 && row < mat.rows && col < mat.cols; } - - template - static void mat_set_invoke(Mat &mat, int row, int col, const Scalar &value) { - if (1 == mat.channels()) { - mat.at(row, col) = (T) value.val[0]; - } else if (3 == mat.channels()) { + template + static void mat_set_invoke(Mat &mat, int row, int col, const Scalar &value) + { + if (1 == mat.channels()) + { + mat.at(row, col) = (T)value.val[0]; + } + else if (3 == mat.channels()) + { T *ptr_src = mat.ptr(row, col); - *ptr_src++ = (T) value.val[0]; - *ptr_src++ = (T) value.val[1]; - *ptr_src = (T) value.val[2]; - } else if (4 == mat.channels()) { + *ptr_src++ = (T)value.val[0]; + *ptr_src++ = (T)value.val[1]; + *ptr_src = (T)value.val[2]; + } + else if (4 == mat.channels()) + { T *ptr_src = mat.ptr(row, col); - *ptr_src++ = (T) value.val[0]; - *ptr_src++ = (T) value.val[1]; - *ptr_src++ = (T) value.val[2]; - *ptr_src = (T) value.val[3]; + *ptr_src++ = (T)value.val[0]; + *ptr_src++ = (T)value.val[1]; + *ptr_src++ = (T)value.val[2]; + *ptr_src = (T)value.val[3]; } } - void setPoint(Mat &mat, int row, int col, const Scalar &value) { - if (CV_8U == mat.depth()) { + void setPoint(Mat &mat, int row, int col, const Scalar &value) + { + if (CV_8U == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_8S == mat.depth()) { + } + else if (CV_8S == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_16U == mat.depth()) { + } + else if (CV_16U == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_16S == mat.depth()) { + } + else if (CV_16S == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_32S == mat.depth()) { + } + else if (CV_32S == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_32F == mat.depth()) { + } + else if (CV_32F == mat.depth()) + { mat_set_invoke(mat, row, col, value); - } else if (CV_64F == mat.depth()) { + } + else if (CV_64F == mat.depth()) + { mat_set_invoke(mat, row, col, value); } } - Rect adaptive_charrect_from_rect(const Rect &rect, int maxwidth, int maxheight, bool useExtendHeight) { + Rect adaptive_charrect_from_rect(const Rect &rect, int maxwidth, int maxheight, bool useExtendHeight) + { int expendWidth = 0; int extendHeight = 0; - if (rect.height > 3 * rect.width) { + if (rect.height > 3 * rect.width) + { expendWidth = int((int(rect.height * 0.5f) - rect.width) * 0.5f); - if (useExtendHeight) { + if (useExtendHeight) + { extendHeight = int(rect.height * 0.3f); } } - //Rect resultRect(rect.tl().x - expendWidth, rect.tl().y, - // rect.width + expendWidth * 2, rect.height); + // Rect resultRect(rect.tl().x - expendWidth, rect.tl().y, + // rect.width + expendWidth * 2, rect.height); int tlx = rect.tl().x - expendWidth > 0 ? rect.tl().x - expendWidth : 0; int tly = rect.tl().y - extendHeight > 0 ? rect.tl().y - extendHeight : 0; @@ -2229,26 +2730,31 @@ void clearBorder(const Mat &img, Rect& cropRect) { return resultRect; } - Mat adaptive_image_from_points(const std::vector &points, const Rect &rect, const Size &size, const Scalar &backgroundColor /* = ml_color_white */, - const Scalar &forgroundColor /* = ml_color_black */, bool gray /* = true */) { + const Scalar &forgroundColor /* = ml_color_black */, bool gray /* = true */) + { int expendHeight = 0; int expendWidth = 0; - if (rect.width > rect.height) { + if (rect.width > rect.height) + { expendHeight = (rect.width - rect.height) / 2; - } else if (rect.height > rect.width) { + } + else if (rect.height > rect.width) + { expendWidth = (rect.height - rect.width) / 2; } Mat image(rect.height + expendHeight * 2, rect.width + expendWidth * 2, gray ? CV_8UC1 : CV_8UC3, backgroundColor); - for (int i = 0; i < (int) points.size(); ++i) { + for (int i = 0; i < (int)points.size(); ++i) + { Point point = points[i]; Point currentPt(point.x - rect.tl().x + expendWidth, point.y - rect.tl().y + expendHeight); - if (mat_valid_position(image, currentPt.y, currentPt.x)) { + if (mat_valid_position(image, currentPt.y, currentPt.x)) + { setPoint(image, currentPt.y, currentPt.x, forgroundColor); } } @@ -2259,27 +2765,29 @@ void clearBorder(const Mat &img, Rect& cropRect) { return result; } -// calc safe Rect -// if not exit, return false + // calc safe Rect + // if not exit, return false bool calcSafeRect(const RotatedRect &roi_rect, const Mat &src, - Rect_ &safeBoundRect) { + Rect_ &safeBoundRect) + { Rect_ boudRect = roi_rect.boundingRect(); float tl_x = boudRect.x > 0 ? boudRect.x : 0; float tl_y = boudRect.y > 0 ? boudRect.y : 0; float br_x = boudRect.x + boudRect.width < src.cols - ? boudRect.x + boudRect.width - 1 - : src.cols - 1; + ? boudRect.x + boudRect.width - 1 + : src.cols - 1; float br_y = boudRect.y + boudRect.height < src.rows - ? boudRect.y + boudRect.height - 1 - : src.rows - 1; + ? boudRect.y + boudRect.height - 1 + : src.rows - 1; float roi_width = br_x - tl_x; float roi_height = br_y - tl_y; - if (roi_width <= 0 || roi_height <= 0) return false; + if (roi_width <= 0 || roi_height <= 0) + return false; // a new rect not out the range of mat @@ -2289,23 +2797,25 @@ void clearBorder(const Mat &img, Rect& cropRect) { } bool calcSafeRect(const RotatedRect &roi_rect, const int width, const int height, - Rect_ &safeBoundRect) { + Rect_ &safeBoundRect) + { Rect_ boudRect = roi_rect.boundingRect(); float tl_x = boudRect.x > 0 ? boudRect.x : 0; float tl_y = boudRect.y > 0 ? boudRect.y : 0; float br_x = boudRect.x + boudRect.width < width - ? boudRect.x + boudRect.width - 1 - : width - 1; + ? boudRect.x + boudRect.width - 1 + : width - 1; float br_y = boudRect.y + boudRect.height < height - ? boudRect.y + boudRect.height - 1 - : height - 1; + ? boudRect.y + boudRect.height - 1 + : height - 1; float roi_width = br_x - tl_x; float roi_height = br_y - tl_y; - if (roi_width <= 0 || roi_height <= 0) return false; + if (roi_width <= 0 || roi_height <= 0) + return false; // a new rect not out the range of mat @@ -2314,10 +2824,10 @@ void clearBorder(const Mat &img, Rect& cropRect) { return true; } - - Mat uniformResize(const Mat &result, float &scale) { + Mat uniformResize(const Mat &result, float &scale) + { const int RESULTWIDTH = kShowWindowWidth; // 640 930 - const int RESULTHEIGHT = kShowWindowHeight; // 540 710 + const int RESULTHEIGHT = kShowWindowHeight; // 540 710 Mat img_window; img_window.create(RESULTHEIGHT, RESULTWIDTH, CV_8UC3); @@ -2326,30 +2836,38 @@ void clearBorder(const Mat &img, Rect& cropRect) { int nCols = result.cols; Mat result_resize; - if (nCols <= img_window.cols && nRows <= img_window.rows) { + if (nCols <= img_window.cols && nRows <= img_window.rows) + { result_resize = result; - } else if (nCols > img_window.cols && nRows <= img_window.rows) { + } + else if (nCols > img_window.cols && nRows <= img_window.rows) + { scale = float(img_window.cols) / float(nCols); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - - } else if (nCols <= img_window.cols && nRows > img_window.rows) { + } + else if (nCols <= img_window.cols && nRows > img_window.rows) + { scale = float(img_window.rows) / float(nRows); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - - } else if (nCols > img_window.cols && nRows > img_window.rows) { + } + else if (nCols > img_window.cols && nRows > img_window.rows) + { float scale1 = float(img_window.cols) / float(nCols); float scale2 = float(img_window.rows) / float(nRows); scale = scale1 < scale2 ? scale1 : scale2; resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - } else { + } + else + { result_resize = result; } return result_resize; } - Mat uniformResizePlates (const Mat &result, float &scale) { + Mat uniformResizePlates(const Mat &result, float &scale) + { const int RESULTWIDTH = kPlateResizeWidth; // 640 930 - const int RESULTHEIGHT = kPlateResizeHeight; // 540 710 + const int RESULTHEIGHT = kPlateResizeHeight; // 540 710 Mat img_window; img_window.create(RESULTHEIGHT, RESULTWIDTH, CV_8UC3); @@ -2358,46 +2876,51 @@ void clearBorder(const Mat &img, Rect& cropRect) { int nCols = result.cols; Mat result_resize; - if (nCols <= img_window.cols && nRows <= img_window.rows) { + if (nCols <= img_window.cols && nRows <= img_window.rows) + { result_resize = result; } - else if (nCols > img_window.cols && nRows <= img_window.rows) { + else if (nCols > img_window.cols && nRows <= img_window.rows) + { scale = float(img_window.cols) / float(nCols); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - } - else if (nCols <= img_window.cols && nRows > img_window.rows) { + else if (nCols <= img_window.cols && nRows > img_window.rows) + { scale = float(img_window.rows) / float(nRows); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - } - else if (nCols > img_window.cols && nRows > img_window.rows) { + else if (nCols > img_window.cols && nRows > img_window.rows) + { float scale1 = float(img_window.cols) / float(nCols); float scale2 = float(img_window.rows) / float(nRows); scale = scale1 < scale2 ? scale1 : scale2; resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); } - else { + else + { result_resize = result; } return result_resize; } - - - void showDectectResults(const Mat &img, const vector &plateVec, size_t num) { + void showDectectResults(const Mat &img, const vector &plateVec, size_t num) + { int index = 0; - if (1) { + if (1) + { Mat result; img.copyTo(result); - for (size_t j = 0; j < plateVec.size(); j++) { + for (size_t j = 0; j < plateVec.size(); j++) + { // add plates to left corner - const CPlate& item = plateVec.at(j); + const CPlate &item = plateVec.at(j); Mat plateMat = item.getPlateMat(); int height = 36; int width = 136; - if (height * index + height < result.rows) { + if (height * index + height < result.rows) + { Mat imageRoi = result(Rect(0, 0 + height * index, width, height)); addWeighted(imageRoi, 0, plateMat, 1, 0, imageRoi); } @@ -2411,9 +2934,12 @@ void clearBorder(const Mat &img, Rect& cropRect) { Point2f rect_points[4]; minRect.points(rect_points); Scalar lineColor = Scalar(255, 255, 255); - if (item.getPlateLocateType() == SOBEL) lineColor = Scalar(255, 0, 0); - if (item.getPlateLocateType() == COLOR) lineColor = Scalar(0, 255, 0); - if (item.getPlateLocateType() == CMSER) lineColor = Scalar(0, 0, 255); + if (item.getPlateLocateType() == SOBEL) + lineColor = Scalar(255, 0, 0); + if (item.getPlateLocateType() == COLOR) + lineColor = Scalar(0, 255, 0); + if (item.getPlateLocateType() == CMSER) + lineColor = Scalar(0, 0, 255); for (int j = 0; j < 4; j++) line(result, rect_points[j], rect_points[(j + 1) % 4], lineColor, 2, 8); @@ -2422,11 +2948,12 @@ void clearBorder(const Mat &img, Rect& cropRect) { } } - Mat showResult(const Mat &result, int img_index) { + Mat showResult(const Mat &result, int img_index) + { namedWindow("EasyPR", CV_WINDOW_AUTOSIZE); const int RESULTWIDTH = kShowWindowWidth; // 640 930 - const int RESULTHEIGHT = kShowWindowHeight; // 540 710 + const int RESULTHEIGHT = kShowWindowHeight; // 540 710 Mat img_window; img_window.create(RESULTHEIGHT, RESULTWIDTH, CV_8UC3); @@ -2435,24 +2962,29 @@ void clearBorder(const Mat &img, Rect& cropRect) { int nCols = result.cols; Mat result_resize; - if (nCols <= img_window.cols && nRows <= img_window.rows) { + if (nCols <= img_window.cols && nRows <= img_window.rows) + { result_resize = result; - - } else if (nCols > img_window.cols && nRows <= img_window.rows) { + } + else if (nCols > img_window.cols && nRows <= img_window.rows) + { float scale = float(img_window.cols) / float(nCols); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - - } else if (nCols <= img_window.cols && nRows > img_window.rows) { + } + else if (nCols <= img_window.cols && nRows > img_window.rows) + { float scale = float(img_window.rows) / float(nRows); resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); - - } else if (nCols > img_window.cols && nRows > img_window.rows) { + } + else if (nCols > img_window.cols && nRows > img_window.rows) + { float scale1 = float(img_window.cols) / float(nCols); float scale2 = float(img_window.rows) / float(nRows); float scale = scale1 < scale2 ? scale1 : scale2; resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA); } - else { + else + { result_resize = result; } @@ -2461,13 +2993,15 @@ void clearBorder(const Mat &img, Rect& cropRect) { result_resize.cols, result_resize.rows)); addWeighted(imageRoi, 0, result_resize, 1, 0, imageRoi); - if (1) { + if (1) + { imshow("EasyPR", img_window); waitKey(0); destroyWindow("EasyPR"); } - if (1) { + if (1) + { std::stringstream ss(std::stringstream::in | std::stringstream::out); ss << "resources/image/tmp/Result/plate_" << img_index << ".jpg"; imwrite(ss.str(), img_window); @@ -2476,9 +3010,10 @@ void clearBorder(const Mat &img, Rect& cropRect) { return img_window; } - Rect rectEnlarge(const Rect &src, const int mat_width, const int mat_height) { - float w = (float) src.width; - float h = (float) src.height; + Rect rectEnlarge(const Rect &src, const int mat_width, const int mat_height) + { + float w = (float)src.width; + float h = (float)src.height; // enlarge the rect, // width to 120% // height to 105% @@ -2486,8 +3021,8 @@ void clearBorder(const Mat &img, Rect& cropRect) { float new_h = h * 1.05f; Rect_ boudRect; - boudRect.x = (float) src.x - w * 0.1f; - boudRect.y = (float) src.y - h * 0.025f; + boudRect.x = (float)src.x - w * 0.1f; + boudRect.y = (float)src.y - h * 0.025f; boudRect.width = new_w; boudRect.height = new_h; @@ -2495,11 +3030,11 @@ void clearBorder(const Mat &img, Rect& cropRect) { float tl_y = boudRect.y > 0 ? boudRect.y : 0; float br_x = boudRect.x + boudRect.width - 1 <= mat_width - 1 - ? boudRect.x + boudRect.width - 1 - : mat_width - 1; + ? boudRect.x + boudRect.width - 1 + : mat_width - 1; float br_y = boudRect.y + boudRect.height - 1 < mat_height - 1 - ? boudRect.y + boudRect.height - 1 - : mat_height - 1; + ? boudRect.y + boudRect.height - 1 + : mat_height - 1; float roi_width = br_x - tl_x + 1; float roi_height = br_y - tl_y + 1; @@ -2511,16 +3046,17 @@ void clearBorder(const Mat &img, Rect& cropRect) { //! a new rect not out the range of mat dst = Rect_(tl_x, tl_y, roi_width, roi_height); return dst; - } - Rect rectFit(const Rect &src, const int mat_width, const int mat_height) { + Rect rectFit(const Rect &src, const int mat_width, const int mat_height) + { float w = (float)src.width; float h = (float)src.height; float new_w = h * 0.5f; float new_h = h * 1.05f; - if (new_w <= w || new_h <= h) { + if (new_w <= w || new_h <= h) + { return src; } @@ -2537,11 +3073,11 @@ void clearBorder(const Mat &img, Rect& cropRect) { float tl_y = boudRect.y > 0 ? boudRect.y : 0; float br_x = boudRect.x + boudRect.width - 1 <= mat_width - 1 - ? boudRect.x + boudRect.width - 1 - : mat_width - 1; + ? boudRect.x + boudRect.width - 1 + : mat_width - 1; float br_y = boudRect.y + boudRect.height - 1 < mat_height - 1 - ? boudRect.y + boudRect.height - 1 - : mat_height - 1; + ? boudRect.y + boudRect.height - 1 + : mat_height - 1; float roi_width = br_x - tl_x + 1; float roi_height = br_y - tl_y + 1; @@ -2553,13 +3089,12 @@ void clearBorder(const Mat &img, Rect& cropRect) { //! a new rect not out the range of mat dst = Rect_(tl_x, tl_y, roi_width - 1, roi_height - 1); return dst; - } - - void writeTempImage(const Mat &outImg, const string path, int index) { + void writeTempImage(const Mat &outImg, const string path, int index) + { std::stringstream ss(std::stringstream::in | std::stringstream::out); - time_t t = time(0); // get time now + time_t t = time(0); // get time now struct tm *now = localtime(&t); char buf[80]; strftime(buf, sizeof(buf), "%Y-%m-%d %H_%M_%S", now);