diff --git a/121.txt b/121.txt new file mode 100644 index 0000000..93a5f6a --- /dev/null +++ b/121.txt @@ -0,0 +1 @@ +123123 \ No newline at end of file diff --git a/src/src/core/chars_identify.cpp b/src/src/core/chars_identify.cpp index 452978f..a88d727 100644 --- a/src/src/core/chars_identify.cpp +++ b/src/src/core/chars_identify.cpp @@ -18,7 +18,6 @@ CharsIdentify* CharsIdentify::instance() { return instance_; } -// 主要用于加载和管理预训练的神经网络模型,用于字符识别 CharsIdentify::CharsIdentify() { LOAD_ANN_MODEL(ann_, kDefaultAnnPath); LOAD_ANN_MODEL(annChinese_, kChineseAnnPath); @@ -59,24 +58,18 @@ void CharsIdentify::LoadChineseMapping(std::string path) { 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++) { @@ -88,8 +81,6 @@ void CharsIdentify::classify(cv::Mat featureRows, std::vector& out_maxIndex } } } - // 如果该行是中文字符, - // 则从kCharactersNumber开始遍历后面的预测结果,找出值最大的那个,并记录其索引和值。 else { result = kCharactersNumber; for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) { @@ -101,20 +92,18 @@ void CharsIdentify::classify(cv::Mat featureRows, std::vector& out_maxIndex } } } - // 将记录的最大索引和最大值分别赋值给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(); @@ -122,14 +111,9 @@ void CharsIdentify::classify(std::vector& charVec){ featureRows.push_back(feature); } - // 创建一个输出矩阵output,并使用预先训练好的模型(ann_)对特征进行预测。 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); @@ -151,8 +135,6 @@ void CharsIdentify::classify(std::vector& charVec){ } label = std::make_pair(kChars[result], kChars[result]).second; } - // 如果字符是中文字符,函数则从预测结果的后面部分开始查找最大值,并记录其索引和值。 - // 然后,函数根据这个最大值和索引确定预测的字符,并通过键值对(kv_)查找对应的省份,将字符和省份作为标签。 else { result = kCharactersNumber; for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) { @@ -170,33 +152,28 @@ void CharsIdentify::classify(std::vector& charVec){ } /*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),然后使用预先训练好的模型(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); @@ -221,8 +198,6 @@ void CharsIdentify::classifyChineseGray(std::vector& charVec){ isChinese = false; } - // 根据这个最大值和索引确定预测的字符。 - // 这是通过查找kChars数组实现的,其中kChars可能是一个预定义的字符集。 auto index = result + kCharsTotalNumber - kChineseNumber; const char* key = kChars[index]; std::string s = key; @@ -231,15 +206,12 @@ void CharsIdentify::classifyChineseGray(std::vector& charVec){ /*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(); @@ -247,20 +219,15 @@ void CharsIdentify::classifyChinese(std::vector& charVec){ 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); } - // 创建一个输出矩阵(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); @@ -285,8 +252,6 @@ void CharsIdentify::classifyChinese(std::vector& charVec){ isChinese = false; } - // 计算索引值,并使用该索引从kChars数组中获取对应的字符。 - // 同时,通过键值对(kv_)查找与该字符对应的省份。 auto index = result + kCharsTotalNumber - kChineseNumber; const char* key = kChars[index]; std::string s = key; @@ -295,24 +260,18 @@ void CharsIdentify::classifyChinese(std::vector& charVec){ /*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) { @@ -350,27 +309,20 @@ int CharsIdentify::classify(cv::Mat f, float& maxVal, bool isChinses, bool isAlp } } } - // 返回索引值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; } 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; @@ -386,10 +338,8 @@ bool CharsIdentify::isCharacter(cv::Mat input, std::string& label, float& maxVal 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; @@ -397,8 +347,6 @@ std::pair CharsIdentify::identifyChinese(cv::Mat input 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; @@ -409,8 +357,6 @@ std::pair CharsIdentify::identifyChinese(cv::Mat input } // no match - // 如果索引值为-1(即没有匹配的字符), - // 则将result设置为0,maxVal设置为0,并将isChinese设置为false,表示输入的字符不是中文。 if (-1 == result) { result = 0; maxVal = 0; @@ -419,7 +365,7 @@ std::pair CharsIdentify::identifyChinese(cv::Mat input else if (maxVal > 0.9){ isChinese = true; } - // 通过索引值获取字符的标签和省份,并将最大值保存到out中。函数返回一个由字符标签和省份组成的pair。 + auto index = result + kCharsTotalNumber - kChineseNumber; const char* key = kChars[index]; std::string s = key; @@ -428,18 +374,15 @@ std::pair CharsIdentify::identifyChinese(cv::Mat input 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; @@ -449,8 +392,6 @@ std::pair CharsIdentify::identifyChineseGray(cv::Mat i } } // no match - // 如果索引值为-1(即没有匹配的字符), - // 则将result设置为0,maxVal设置为0,并将isChinese设置为false,表示输入的字符不是中文 if (-1 == result) { result = 0; maxVal = 0; @@ -458,7 +399,6 @@ std::pair CharsIdentify::identifyChineseGray(cv::Mat i } else if (maxVal > 0.9){ isChinese = true; } - // 通过索引值获取字符的标签和省份,并将最大值保存到out中。函数返回一个由字符标签和省份组成的pair。 auto index = result + kCharsTotalNumber - kChineseNumber; const char* key = kChars[index]; std::string s = key; @@ -467,15 +407,11 @@ std::pair CharsIdentify::identifyChineseGray(cv::Mat i 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]); } @@ -487,14 +423,10 @@ std::pair CharsIdentify::identify(cv::Mat input, bool } } -// 用于处理一组输入的图像数据并识别出对应的字符和省份。 -// 函数参数包括输入图像数据(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); @@ -503,13 +435,8 @@ int CharsIdentify::identify(std::vector inputs, 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) { diff --git a/src/src/core/chars_recognise.cpp b/src/src/core/chars_recognise.cpp index d101fd3..26831a9 100644 --- a/src/src/core/chars_recognise.cpp +++ b/src/src/core/chars_recognise.cpp @@ -6,29 +6,43 @@ namespace easypr { CCharsRecognise::CCharsRecognise() { m_charsSegment = new CCharsSegment(); } +//定义了CCharsRecognise类的构造函数,创建一个新的CCharsSegment类的实例,并将其地址赋值给成员变量m_charsSegment CCharsRecognise::~CCharsRecognise() { SAFE_RELEASE(m_charsSegment); } +//定义了CCharsRecognise类的析构函数, +//调用了SAFE_RELEASE宏来释放(即删除并置空)m_charsSegment指针指向的CCharsSegment对象 int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) { + //车牌字符识别函数,接收两个参数,一个是Mat类型的plate,它是需要进行识别的车牌图像; + //另一个是std::string&类型的plateLicense,它是一个引用,用于存储识别出来的车牌号码。 std::vector matChars; + //matChars,用于存储经过字符分割后的单个字符图像。 int result = m_charsSegment->charsSegment(plate, matChars); + //调用m_charsSegment对象的charsSegment函数进行字符分割, + //将分割后的字符存储在matChars中。charsSegment函数的返回值存储在result变量中。 if (result == 0) { + //result = 0则表示字符分割成功,接着进行车牌号码的识别 int num = matChars.size(); for (int j = 0; j < num; j++) { Mat charMat = matChars.at(j); + //循环遍历matChars中的每个字符图像 bool isChinses = false; float maxVal = 0; if (j == 0) { bool judge = true; isChinses = true; + //第一个字符,则认为是中文汉字 auto character = CharsIdentify::instance()->identifyChinese(charMat, maxVal, judge); plateLicense.append(character.second); + //调用CharsIdentify::instance()->identifyChinese函数进行识别,并将识别结果追加到plateLicense中 } else { isChinses = false; + //如果不是第一个字符,则认为不是中文汉字, auto character = CharsIdentify::instance()->identify(charMat, isChinses); plateLicense.append(character.second); + //调用CharsIdentify::instance()->identify函数进行识别,并将识别结果追加到plateLicense中。 } } @@ -36,20 +50,27 @@ int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) { if (plateLicense.size() < 7) { return -1; } + // 检查plateLicense的长度是否小于7,如果小于7,则返回 - 1,表示车牌号码识别失败。否则,返回result,表示车牌号码识别成功。 return result; } int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) { +//同样是用来识别车牌的函数,数接收两个参数,一个是CPlate&类型的plate,它是需要进行识别的车牌对象; +// 另一个是std::string&类型的plateLicense,它是一个引用,用于存储识别出来的车牌号码 std::vector matChars; std::vector grayChars; + //matChars用于存储经过字符分割后的单个字符图像,grayChars用于存储灰度图像。 Mat plateMat = plate.getPlateMat(); + //调用plate.getPlateMat()获取车牌图像,并将其存储在plateMat中。 if (0) writeTempImage(plateMat, "plateMat/plate"); Color color; if (plate.getPlateLocateType() == CMSER) { color = plate.getPlateColor(); } + //选择不同的方式来获取车牌颜色。如果车牌定位类型为CMSER,则直接使用plate.getPlateColor()获取车牌颜色; + //否则,从车牌图像中截取一部分区域,并调用getPlateType()函数获取车牌类型。 else { int w = plateMat.cols; int h = plateMat.rows; @@ -58,35 +79,42 @@ int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) { } int result = m_charsSegment->charsSegmentUsingOSTU(plateMat, matChars, grayChars, color); - + //调用charsSegmentUsingOSTU方法对输入的车牌图像进行字符分割 if (result == 0) { int num = matChars.size(); for (int j = 0; j < num; j++) { + //for循环遍历每一个字符图像 Mat charMat = matChars.at(j); Mat grayChar = grayChars.at(j); + //通过matChars.at(j)和grayChars.at(j)获取第j个字符图像和其灰度图像 if (color != Color::BLUE) grayChar = 255 - grayChar; - + //如果车牌颜色不是蓝色,则对灰度图像进行操作 bool isChinses = false; std::pair character; float maxVal; if (0 == j) { isChinses = true; bool judge = true; + //设定第一个字符为中文字符 character = CharsIdentify::instance()->identifyChineseGray(grayChar, maxVal, judge); plateLicense.append(character.second); - + //使用CharsIdentify::instance()->identifyChineseGray方法识别灰度图像grayChar, + // 并将结果存储在character对中。 // set plate chinese mat and str plate.setChineseMat(grayChar); plate.setChineseKey(character.first); + //设置车牌的中文图像和字符串。 if (0) writeTempImage(grayChar, "char_data/" + character.first + "/chars_"); } else if (1 == j) { isChinses = false; bool isAbc = true; character = CharsIdentify::instance()->identify(charMat, isChinses, isAbc); + //使用CharsIdentify::instance()->identify方法识别字符图像charMat plateLicense.append(character.second); + //将识别的第二个字符串添加到plateLicense } else { isChinses = false; @@ -96,17 +124,21 @@ int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) { } CCharacter charResult; + //创建CCharacter对象并设置其图像和字符串。 charResult.setCharacterMat(charMat); charResult.setCharacterGrayMat(grayChar); if (isChinses) charResult.setCharacterStr(character.first); else charResult.setCharacterStr(character.second); - + + //如果isChinses为true,则使用识别的第一个字符串作为字符的字符串;否则,使用识别的第二个字符串。 plate.addReutCharacter(charResult); + //字符结果添加到车牌对象中。 } if (plateLicense.size() < 7) { return -1; + //最后,如果识别的字符数量少于7个,函数返回 - 1;否则返回result } } diff --git a/src/src/core/params.cpp b/src/src/core/params.cpp index 8809043..2b0ccef 100644 --- a/src/src/core/params.cpp +++ b/src/src/core/params.cpp @@ -9,4 +9,5 @@ namespace easypr { } return instance_; } -}/*! \namespace easypr*/ \ No newline at end of file +}/*! \namespace easypr*/ +//杩欐浠g爜鏄竴涓崟渚嬫ā寮忕殑瀹炵幇銆侰Params绫荤殑instance_鎴愬憳鍙橀噺琚垵濮嬪寲涓簄ullptr锛岀劧鍚巌nstance()鍑芥暟鐢ㄦ潵鑾峰彇CParams绫荤殑鍞竴瀹炰緥銆傚鏋渋nstance_涓虹┖锛屽氨浼氬垱寤轰竴涓柊鐨勫疄渚嬪苟杩斿洖锛屽惁鍒欑洿鎺ヨ繑鍥炵幇鏈夌殑瀹炰緥銆傝繖鏍峰彲浠ョ‘淇濆湪绋嬪簭杩愯鏈熼棿鍙湁涓涓狢Params绫荤殑瀹炰緥瀛樺湪銆 \ No newline at end of file diff --git a/src/src/core/plate_detect.cpp b/src/src/core/plate_detect.cpp index 26d1ad9..37dedfc 100644 --- a/src/src/core/plate_detect.cpp +++ b/src/src/core/plate_detect.cpp @@ -11,7 +11,7 @@ namespace easypr { m_type = 0; m_showDetect = false; } - +// 杩欐浠g爜鏄疌PlateDetect绫荤殑鏋勯犲嚱鏁帮紝鍒濆鍖栦簡涓浜涙垚鍛樺彉閲忥紝鍖呮嫭m_plateLocate鎸囬拡銆乵_maxPlates銆乵_type鍜宮_showDetect銆 CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); } int CPlateDetect::plateDetect(Mat src, std::vector &resultVec, int type, @@ -24,6 +24,11 @@ namespace easypr { mser_Plates.reserve(16); std::vector all_result_Plates; all_result_Plates.reserve(64); + // 杩欓儴鍒嗕唬鐮佸畾涔変簡CPlateDetect绫荤殑鏋愭瀯鍑芥暟鍜宲lateDetect鏂规硶銆 +// 鍦╬lateDetect鏂规硶涓紝浣跨敤浜哋penMP骞惰澶勭悊鏉ュ悓鏃惰繘琛孲obel銆侀鑹插拰MSER涓夌杞︾墝瀹氫綅鏂规硶銆 +// 鏈鍚庝娇鐢∟MS锛堥潪鏋佸ぇ鍊兼姂鍒讹級鏉ュ垽鏂溅鐗岋紝骞惰繑鍥炵粨鏋溿 +// plateDetect鏂规硶杩樻湁涓涓噸杞界増鏈紝鍏朵腑showDetectArea鍙傛暟涓篺alse銆 +// LoadSVM鏂规硶鐢ㄤ簬鍔犺浇SVM妯″瀷銆 #pragma omp parallel sections { #pragma omp section @@ -32,12 +37,16 @@ namespace easypr { m_plateLocate->plateSobelLocate(src, sobel_Plates, img_index); } } + // 杩欓儴鍒嗕唬鐮佷娇鐢ㄤ簡OpenMP骞惰澶勭悊鏉ヨ繘琛孲obel杞︾墝瀹氫綅鏂规硶銆 +// 濡傛灉type涓0鎴栬呭寘鍚玃R_DETECT_SOBEL鏍囧織浣嶏紝鍒欒皟鐢╩_plateLocate鐨刾lateSobelLocate鏂规硶銆 +// plateSobelLocate鏂规硶鐢ㄤ簬杩涜Sobel绠楀瓙瀹氫綅杞︾墝銆 #pragma omp section { if (!type || type & PR_DETECT_COLOR) { m_plateLocate->plateColorLocate(src, color_Plates, img_index); } } + //杩欐浠g爜鏄娇鐢∣penMP骞惰澶勭悊鏉ヨ皟鐢╩_plateLocate鐨刾lateColorLocate鏂规硶锛岀敤浜庤繘琛岄鑹插畾浣嶈溅鐗屻傛牴鎹畉ype鍙傛暟鐨勫硷紝鍒ゆ柇鏄惁闇瑕佽繘琛岄鑹插畾浣 #pragma omp section { if (!type || type & PR_DETECT_CMSER) { @@ -64,7 +73,11 @@ namespace easypr { showDectectResults(src, resultVec, m_maxPlates); return 0; } - +// 杩欓儴鍒嗕唬鐮佹槸CPlateDetect绫荤殑plateDetect鏂规硶鐨勪竴閮ㄥ垎銆 +// 棣栧厛閫氳繃OpenMP骞惰澶勭悊璋冪敤plateMserLocate鏂规硶鏉ヨ繘琛孧SER杞︾墝瀹氫綅銆 +// 鐒跺悗灏嗗畾浣嶇粨鏋滃垎鍒缃负瀵瑰簲鐨勭被鍨嬶紙SOBEL銆丆OLOR銆丆MSER锛夊苟瀛樺叆all_result_Plates涓 +// 鏈鍚庝娇鐢∟MS锛堥潪鏋佸ぇ鍊兼姂鍒讹級鏉ュ垽鏂溅鐗岋紝骞跺皢缁撴灉瀛樺叆resultVec涓 +// 浠g爜涓殑if (0)鏉′欢璇彞浼间箮娌℃湁瀹為檯浣滅敤锛屽彲鑳介渶瑕佽繘涓姝ョ‘璁ゅ叾鎰忓浘銆 int CPlateDetect::plateDetect(Mat src, std::vector &resultVec, int img_index) { int result = plateDetect(src, resultVec, m_type, false, img_index); return result; @@ -74,4 +87,6 @@ namespace easypr { PlateJudge::instance()->LoadModel(path); } -} \ No newline at end of file +} +// 杩欐浠g爜涓殑plateDetect鏂规硶鏄疌PlateDetect绫荤殑鎴愬憳鍑芥暟锛岀敤浜庤皟鐢ㄥ彟涓涓噸杞界増鏈殑plateDetect鏂规硶锛屽苟杩斿洖缁撴灉銆 +// LoadSVM鏂规硶鐢ㄤ簬鍔犺浇SVM妯″瀷锛屽叾涓皟鐢ㄤ簡PlateJudge绫荤殑LoadModel鏂规硶銆 \ No newline at end of file diff --git a/src/src/core/plate_judge.cpp b/src/src/core/plate_judge.cpp index 4dfa032..619a48c 100644 --- a/src/src/core/plate_judge.cpp +++ b/src/src/core/plate_judge.cpp @@ -1,9 +1,11 @@ +//璇ヤ唬鐮佷富瑕佺敤浜庤溅鐗岃瘑鍒傚畠瀹氫箟浜嗕竴涓悕涓篜lateJudge鐨勭被锛岃绫诲寘鍚簡涓绯诲垪鐢ㄤ簬杞︾墝璇嗗埆鐨勬柟娉曘 + #include "easypr/core/plate_judge.h" #include "easypr/config.h" #include "easypr/core/core_func.h" #include "easypr/core/params.h" -namespace easypr { +namespace easypr { //杩欓儴鍒嗕唬鐮佸疄鐜颁簡鍗曚緥妯″紡锛岀‘淇漃lateJudge绫诲彧鏈変竴涓疄渚 PlateJudge* PlateJudge::instance_ = nullptr; @@ -14,7 +16,7 @@ namespace easypr { return instance_; } - PlateJudge::PlateJudge() { + PlateJudge::PlateJudge() { //PlateJudge鍐冲畾浜嗕娇鐢ㄥ摢绉嶇壒寰佹彁鍙栨柟娉曘 bool useLBP = false; if (useLBP) { LOAD_SVM_MODEL(svm_, kLBPSvmPath); @@ -26,7 +28,7 @@ namespace easypr { } } - void PlateJudge::LoadModel(std::string path) { + void PlateJudge::LoadModel(std::string path) { //LoadModel鍑芥暟鐢ㄤ簬鍔犺浇SVM妯″瀷銆 if (path != std::string(kDefaultSvmPath)) { if (!svm_->empty()) svm_->clear(); @@ -36,7 +38,7 @@ namespace easypr { // set the score of plate // 0 is plate, -1 is not. - int PlateJudge::plateSetScore(CPlate& plate) { + int PlateJudge::plateSetScore(CPlate& plate) { //plateSetScore鍑芥暟鐢ㄤ簬璁剧疆杞︾墝鐨勮瘎鍒嗐 Mat features; extractFeature(plate.getPlateMat(), features); float score = svm_->predict(features, noArray(), cv::ml::StatModel::Flags::RAW_OUTPUT); @@ -53,53 +55,60 @@ namespace easypr { else return -1; } - int PlateJudge::plateJudge(const Mat& plateMat) { + int PlateJudge::plateJudge(const Mat& plateMat) { //plateJudge鍑芥暟鐢ㄤ簬鍒ゆ柇杈撳叆鐨勫浘鍍忔槸鍚︿负杞︾墝銆 CPlate plate; plate.setPlateMat(plateMat); return plateSetScore(plate); } - int PlateJudge::plateJudge(const std::vector &inVec, - std::vector &resultVec) { - int num = inVec.size(); - for (int j = 0; j < num; j++) { - Mat inMat = inVec[j]; + int PlateJudge::plateJudge(const std::vector &inVec, //inVec鏄緭鍏ョ殑鍥惧儚鍚戦噺锛宺esultVec鏄緭鍑虹殑缁撴灉鍚戦噺銆 + std::vector &resultVec) { //瀵筰nVec涓殑姣忎竴寮犲浘鍍忚繘琛岃溅鐗屽垽鏂傚鏋滃垽鏂粨鏋滀负杞︾墝锛堝嵆plateJudge(inMat)鐨勮繑鍥炲间负0锛夛紝鍒欏皢璇ュ浘鍍忔坊鍔犲埌resultVec涓 + int num = inVec.size(); // 鑾峰彇杈撳叆鍥惧儚鐨勬暟閲 + for (int j = 0; j < num; j++) { // 閬嶅巻姣忎竴寮犲浘鍍 + Mat inMat = inVec[j]; // 鑾峰彇褰撳墠鍥惧儚 int response = -1; - response = plateJudge(inMat); + response = plateJudge(inMat); // 瀵瑰綋鍓嶅浘鍍忚繘琛岃溅鐗屽垽鏂 - if (response == 0) resultVec.push_back(inMat); + if (response == 0) resultVec.push_back(inMat); // 濡傛灉鍒ゆ柇缁撴灉涓鸿溅鐗岋紝灏嗚鍥惧儚娣诲姞鍒扮粨鏋滃悜閲忎腑 } - return 0; + return 0; // 杩斿洖0锛岃〃绀烘墽琛屾垚鍔 } - int PlateJudge::plateJudge(const std::vector &inVec, - std::vector &resultVec) { - int num = inVec.size(); - for (int j = 0; j < num; j++) { - CPlate inPlate = inVec[j]; +//灞炰簬PlateJudge绫伙紝鐢ㄤ簬鍒ゆ柇杈撳叆鐨勮溅鐗屽悜閲忎腑鍝簺鏄湁鏁堢殑杞︾墝銆傝繖涓柟娉曠殑杈撳叆鏄竴涓狢Plate瀵硅薄鐨勫悜閲廼nVec锛岃緭鍑烘槸涓涓湁鏁堣溅鐗岀殑CPlate瀵硅薄鐨勫悜閲弐esultVec銆 + int PlateJudge::plateJudge(const std::vector &inVec, + std::vector &resultVec) { //鎺ユ敹涓や釜鍙傛暟锛氫竴涓狢Plate瀵硅薄鐨勫悜閲廼nVec锛堣緭鍏ョ殑杞︾墝鍚戦噺锛夊拰涓涓狢Plate瀵硅薄鐨勫悜閲弐esultVec锛堣緭鍑虹殑鏈夋晥杞︾墝鍚戦噺锛夈 + int num = inVec.size(); // 鑾峰彇杈撳叆鍚戦噺鐨勫ぇ灏 + for (int j = 0; j < num; j++) { //閬嶅巻杈撳叆鍚戦噺涓殑姣忎竴涓厓绱 + +//鑾峰彇褰撳墠鐨凜Plate瀵硅薄鍜屽畠鐨勮溅鐗屽浘鍍 + CPlate inPlate = inVec[j]; Mat inMat = inPlate.getPlateMat(); + +//璋冪敤plateJudge鏂规硶鍒ゆ柇褰撳墠鐨勮溅鐗屽浘鍍忔槸鍚︽湁鏁堬紝缁撴灉瀛樺偍鍦╮esponse涓 int response = -1; response = plateJudge(inMat); +//濡傛灉褰撳墠鐨勮溅鐗屽浘鍍忔湁鏁堬紙response绛変簬0锛夛紝鍒欏皢褰撳墠鐨凜Plate瀵硅薄娣诲姞鍒扮粨鏋滃悜閲忎腑 if (response == 0) resultVec.push_back(inPlate); +//濡傛灉褰撳墠鐨勮溅鐗屽浘鍍忔棤鏁堬紝閭d箞瀵硅溅鐗屽浘鍍忚繘琛岃鍓拰璋冩暣澶у皬 else { int w = inMat.cols; int h = inMat.rows; Mat tmpmat = inMat(Rect_(w * 0.05, h * 0.1, w * 0.9, h * 0.8)); Mat tmpDes = inMat.clone(); resize(tmpmat, tmpDes, Size(inMat.size())); - +//鍐嶆璋冪敤plateJudge鏂规硶鍒ゆ柇璋冩暣鍚庣殑杞︾墝鍥惧儚鏄惁鏈夋晥锛屽鏋滄湁鏁堝垯灏嗗綋鍓嶇殑CPlate瀵硅薄娣诲姞鍒扮粨鏋滃悜閲忎腑 response = plateJudge(tmpDes); if (response == 0) resultVec.push_back(inPlate); } } - return 0; + return 0; //缁撴潫寰幆骞惰繑鍥0锛岃〃绀烘柟娉曟墽琛屾垚鍔 } // non-maximum suppression - void NMS(std::vector &inVec, std::vector &resultVec, double overlap) { + void NMS(std::vector &inVec, std::vector &resultVec, double overlap) { //NMS鍑芥暟瀹炵幇浜嗛潪鏋佸ぇ鍊兼姂鍒讹紝鐢ㄤ簬娑堥櫎閲嶅彔鐨勮溅鐗屻 std::sort(inVec.begin(), inVec.end()); std::vector::iterator it = inVec.begin(); for (; it != inVec.end(); ++it) { @@ -123,7 +132,7 @@ namespace easypr { } // judge plate using nms - int PlateJudge::plateJudgeUsingNMS(const std::vector &inVec, std::vector &resultVec, int maxPlates) { + int PlateJudge::plateJudgeUsingNMS(const std::vector &inVec, std::vector &resultVec, int maxPlates) { //plateJudgeUsingNMS鍑芥暟浣跨敤闈炴瀬澶у兼姂鍒惰繘琛岃溅鐗岃瘑鍒 std::vector plateVec; int num = inVec.size(); bool useCascadeJudge = true; diff --git a/src/src/core/plate_recognize.cpp b/src/src/core/plate_recognize.cpp index a351709..175659e 100644 --- a/src/src/core/plate_recognize.cpp +++ b/src/src/core/plate_recognize.cpp @@ -7,7 +7,10 @@ namespace easypr { CPlateRecognize::CPlateRecognize() { m_showResult = false; } - +// 杩欐浠g爜鏄疌++涓殑绫诲畾涔夛紝瀹氫箟浜咰PlateRecognize绫荤殑鏋勯犲嚱鏁帮紝鍒濆鍖栦簡m_showResult鎴愬憳鍙橀噺涓篺alse銆 +// 璇ョ被鍖呭惈plateRecognize鏂规硶锛岀敤浜庤溅鐗岃瘑鍒紝鍖呮嫭杞︾墝妫娴嬪拰瀛楃璇嗗埆涓ら儴鍒嗐 +// 杩樺寘鍚簡涓浜汱oad鏂规硶锛岀敤浜庡姞杞芥ā鍨嬫枃浠躲 +// 浠g爜涓娇鐢ㄤ簡OpenCV搴撲腑鐨凪at绫诲拰涓浜涜嚜瀹氫箟鐨勭被鍜屾柟娉曘 // main method, plate recognize, contain two parts // 1. plate detect @@ -20,6 +23,9 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe // 1. plate detect std::vector plateVec; int resultPD = plateDetect(img, plateVec, img_index); + // 杩欐浠g爜鏄疌++涓殑绫绘柟娉旵PlateRecognize::plateRecognize鐨勪竴閮ㄥ垎锛岀敤浜庤溅鐗岃瘑鍒 +// 棣栧厛瀵硅緭鍏ュ浘鍍忚繘琛岀粺涓灏哄鐨勮皟鏁达紝鐒跺悗杩涜杞︾墝妫娴嬶紝灏嗙粨鏋滃瓨鍌ㄥ湪plateVec涓 +// 杩斿洖鍊紃esultPD琛ㄧず杞︾墝妫娴嬬殑缁撴灉锛0琛ㄧず鎴愬姛锛岄潪0琛ㄧず澶辫触銆 if (resultPD == 0) { size_t num = plateVec.size(); for (size_t j = 0; j < num; j++) { @@ -31,7 +37,12 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe item.setPlateScale(scale); RotatedRect rect = item.getPlatePos(); item.setPlatePos(scaleBackRRect(rect, 1.f / scale)); - +// 濡傛灉杞︾墝妫娴嬫垚鍔燂紙resultPD == 0锛夛紝鍒欏姣忎釜妫娴嬪埌鐨勮溅鐗岃繘琛屽鐞嗭細 +// 1. 鑾峰彇杞︾墝鍥惧儚骞舵樉绀 +// 2. 璋冩暣杞︾墝浣嶇疆鍜屽ぇ灏 +// 3. 鑾峰彇杞︾墝棰滆壊骞惰瘑鍒瓧绗 +// 4. 灏嗚瘑鍒粨鏋滃瓨鍌紝骞舵牴鎹瘑鍒粨鏋滆缃溅鐗屽瓧绗︿覆 +// 5. 鏍规嵁鏄剧ず绫诲瀷灞曠ず妫娴嬬粨鏋滄垨璇嗗埆缁撴灉 // get plate color Color color = item.getPlateColor(); if (color == UNKNOWN) { @@ -42,7 +53,10 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe if (0) { std::cout << "plateColor:" << plateColor << std::endl; } - +// 杩欐浠g爜鐢ㄤ簬鑾峰彇杞︾墝棰滆壊骞惰繘琛屽鐞嗐 +// 棣栧厛鑾峰彇杞︾墝棰滆壊锛屽鏋滈鑹蹭负UNKNOWN锛屽垯璋冪敤getPlateType鏂规硶鑾峰彇棰滆壊骞惰缃埌item涓 +// 鐒跺悗灏嗛鑹茶浆鎹负瀛楃涓插舰寮忓苟瀛樺偍鍦╬lateColor涓 +// 鏈鍚庯紝閫氳繃鏉′欢鍒ゆ柇锛屽鏋滄潯浠朵负0锛屽垯杈撳嚭plateColor鍒版帶鍒跺彴銆 // 2. chars recognize std::string plateIdentify = ""; int resultCR = charsRecognise(item, plateIdentify); @@ -59,6 +73,11 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe if (0) std::cout << "resultCR:" << resultCR << std::endl; } } + // 杩欐浠g爜鏄疌++涓殑绫绘柟娉旵PlateRecognize::plateRecognize鐨勪竴閮ㄥ垎锛岀敤浜庡瓧绗﹁瘑鍒 +// 棣栧厛瀹氫箟浜嗕竴涓┖瀛楃涓瞤lateIdentify锛屽苟璋冪敤charsRecognise鏂规硶杩涜瀛楃璇嗗埆锛岀粨鏋滃瓨鍌ㄥ湪resultCR涓 +// 濡傛灉resultCR涓0锛屽垯灏唒lateColor鍜宲lateIdentify鎷兼帴鎴恖icense瀛楃涓诧紝骞惰缃埌item涓紝鐒跺悗灏唅tem瀛樺叆plateVecOut涓 +// 鍚﹀垯锛屽彧灏唒lateColor璁剧疆鍒發icense瀛楃涓蹭腑锛岀劧鍚庡皢item瀛樺叆plateVecOut涓 +// 鏈鍚庯紝鏍规嵁鏉′欢鍒ゆ柇锛屽鏋滄潯浠朵负0锛屽垯杈撳嚭resultCR鍒版帶鍒跺彴銆 if (getResultShow()) { // param type: 0 detect, 1 recognize; int showType = 1; @@ -70,7 +89,9 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &plateVe } return resultPD; } - +// 杩欐浠g爜鏄疌++涓殑绫绘柟娉旵PlateRecognize::plateRecognize鐨勪竴閮ㄥ垎锛岀敤浜庢牴鎹甮etResultShow()鐨勮繑鍥炲煎喅瀹氭槸鍚﹀睍绀烘娴嬬粨鏋滄垨璇嗗埆缁撴灉銆 +// 濡傛灉getResultShow()杩斿洖true锛屽垯鏍规嵁showType鐨勫奸夋嫨灞曠ず妫娴嬬粨鏋滄垨璇嗗埆缁撴灉銆 +// 鏈鍚庤繑鍥瀝esultPD锛岃〃绀鸿溅鐗屾娴嬬殑缁撴灉銆 void CPlateRecognize::LoadSVM(std::string path) { PlateJudge::instance()->LoadModel(path); } @@ -78,7 +99,12 @@ void CPlateRecognize::LoadSVM(std::string path) { void CPlateRecognize::LoadANN(std::string path) { CharsIdentify::instance()->LoadModel(path); } +// 璇勬敞璇ヤ唬鐮 +// 杩欓儴鍒嗕唬鐮佸畾涔変簡CPlateRecognize绫荤殑涓や釜鏂规硶锛歀oadSVM鍜孡oadANN銆 +// LoadSVM鏂规硶鐢ㄤ簬鍔犺浇SVM妯″瀷锛岃皟鐢≒lateJudge绫荤殑瀹炰緥鐨凩oadModel鏂规硶鍔犺浇妯″瀷銆 +// LoadANN鏂规硶鐢ㄤ簬鍔犺浇ANN妯″瀷锛岃皟鐢–harsIdentify绫荤殑瀹炰緥鐨凩oadModel鏂规硶鍔犺浇妯″瀷銆 +// 杩欎簺鏂规硶鐢ㄤ簬鍦ㄨ溅鐗岃瘑鍒繃绋嬩腑鍔犺浇鐩稿叧鐨勬満鍣ㄥ涔犳ā鍨嬨 void CPlateRecognize::LoadChineseANN(std::string path) { CharsIdentify::instance()->LoadChineseModel(path); } @@ -90,7 +116,9 @@ void CPlateRecognize::LoadGrayChANN(std::string path) { void CPlateRecognize::LoadChineseMapping(std::string path) { CharsIdentify::instance()->LoadChineseMapping(path); } - +// 杩欓儴鍒嗕唬鐮佸畾涔変簡CPlateRecognize绫荤殑涓変釜鏂规硶锛歀oadChineseANN銆丩oadGrayChANN鍜孡oadChineseMapping銆 +// 杩欎簺鏂规硶鐢ㄤ簬鍦ㄥ瓧绗﹁瘑鍒繃绋嬩腑鍔犺浇鐩稿叧鐨勪腑鏂囧瓧绗﹁瘑鍒ā鍨嬪拰鏄犲皠鏂囦欢銆 +// 鍒嗗埆璋冪敤CharsIdentify绫荤殑瀹炰緥鐨凩oadChineseModel銆丩oadGrayChANN鍜孡oadChineseMapping鏂规硶鍔犺浇妯″瀷鍜屾槧灏勬枃浠躲 // deprected int CPlateRecognize::plateRecognize(const Mat& src, std::vector &licenseVec) { vector plates; @@ -102,4 +130,8 @@ int CPlateRecognize::plateRecognize(const Mat& src, std::vector &li return resultPR; } -} \ No newline at end of file +} +// 杩欐浠g爜瀹氫箟浜咰PlateRecognize绫荤殑涓涓柊鏂规硶plateRecognize锛屾帴鍙椾竴涓緭鍏ュ浘鍍忓拰涓涓瓧绗︿覆鍚戦噺浣滀负鍙傛暟銆 +// 鍦ㄦ柟娉曞唴閮紝棣栧厛鍒涘缓浜嗕竴涓狢Plate绫诲瀷鐨勫悜閲弍lates锛屽苟璋冪敤浜嗗師鏈夌殑plateRecognize鏂规硶鏉ヨ繘琛岃溅鐗岃瘑鍒 +// 鐒跺悗閬嶅巻plates鍚戦噺涓殑姣忎釜杞︾墝锛屽皢鍏惰溅鐗屽瓧绗︿覆瀛樺叆浼犲叆鐨刲icenseVec鍚戦噺涓 +// 鏈鍚庤繑鍥炰簡杞︾墝璇嗗埆鐨勭粨鏋渞esultPR銆 \ No newline at end of file diff --git a/src/src/train/ann_train.cpp b/src/src/train/ann_train.cpp index 51aec1f..09525c4 100644 --- a/src/src/train/ann_train.cpp +++ b/src/src/train/ann_train.cpp @@ -21,6 +21,10 @@ AnnTrain::AnnTrain(const char* chars_folder, const char* xml) kv_->load("resources/text/province_mapping"); } +// 杩欐浠g爜鏄疌++鐨勭被鎴愬憳鍑芥暟AnnTrain::AnnTrain鐨勬瀯閫犲嚱鏁板疄鐜般 +// 鏋勯犲嚱鏁版帴鍙椾袱涓弬鏁癱hars_folder鍜寈ml锛屽苟灏嗗叾鍒嗗埆璧嬪肩粰鎴愬憳鍙橀噺chars_folder_鍜宎nn_xml_銆 +// 鐒跺悗浣跨敤cv::ml::ANN_MLP::create()鍒涘缓浜嗕竴涓缁忕綉缁滃璞nn_銆 +// type琚垵濮嬪寲涓0锛宬v_琚垵濮嬪寲涓轰竴涓姞杞戒簡"resources/text/province_mapping"鐨凨v瀵硅薄銆 void AnnTrain::train() { int classNumber = 0; @@ -45,7 +49,9 @@ void AnnTrain::train() { hidden_number = kNeurons; output_number = classNumber; } - +// 杩欐浠g爜鏄疉nnTrain绫荤殑train鏂规硶锛屾牴鎹畉ype鐨勫奸夋嫨涓嶅悓鐨刢lassNumber銆乮nput_number銆乭idden_number鍜宱utput_number銆 +// 褰搕ype涓0鏃讹紝classNumber涓簁CharsTotalNumber锛宨nput_number涓簁AnnInput锛宧idden_number涓簁Neurons锛宱utput_number涓篶lassNumber銆 +// 褰搕ype涓1鏃讹紝classNumber涓簁ChineseNumber锛宨nput_number涓簁AnnInput锛宧idden_number涓簁Neurons锛宱utput_number涓篶lassNumber銆 int N = input_number; int m = output_number; int first_hidden_neurons = int(std::sqrt((m + 2) * N) + 2 * std::sqrt(N / (m + 2))); @@ -70,7 +76,9 @@ void AnnTrain::train() { layers.at(2) = second_hidden_neurons; layers.at(3) = output_number; } - +//杩欐浠g爜鏍规嵁杈撳叆鍜岃緭鍑虹殑鏁伴噺璁$畻浜嗕袱涓殣钘忓眰鐨勭缁忓厓鏁伴噺锛 +//骞舵牴鎹竷灏斿彉閲弖seTLFN鐨勫奸夋嫨浜嗗垱寤轰笁灞傛垨鍥涘眰鐨勭缁忕綉缁滃眰銆傚鏋渦seTLFN涓篺alse锛屽垯鍒涘缓涓夊眰锛屽惁鍒欏垱寤哄洓灞傘 +//鍦ㄥ垱寤哄洓灞傛椂锛岃緭鍑轰簡涓や釜闅愯棌灞傜殑绁炵粡鍏冩暟閲忋 ann_->setLayerSizes(layers); ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1); ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP); @@ -99,7 +107,11 @@ void AnnTrain::train() { std::cout << "Your ANN Model was saved to " << ann_xml_ << std::endl; std::cout << "Training done. Time elapse: " << (end - start) / (1000 * 60) << "minute" << std::endl; } - +//杩欐浠g爜鏄竴涓狢++绫绘垚鍛樺嚱鏁癆nnTrain::train鐨勫疄鐜般 +//鍦ㄨ繖娈典唬鐮佷腑锛岀缁忕綉缁渁nn_琚缁冨苟淇濆瓨鍒癮nn_xml_鏂囦欢涓 +//璁粌鏁版嵁閫氳繃sdata(350)鍑芥暟鑾峰彇锛岀劧鍚庝娇鐢╝nn_->train(traindata)杩涜璁粌銆 +//璁粌瀹屾垚鍚庯紝浼氳緭鍑"Your ANN Model was saved to "浠ュ強璁粌鎵鑺辫垂鐨勬椂闂淬 +//鍚屾椂锛岃繕浼氳皟鐢╰est()鍑芥暟杩涜娴嬭瘯銆傚鏋滆缁冩枃浠跺す涓病鏈夋枃浠讹紝鍒欎細杈撳嚭鐩稿簲鐨勬彁绀轰俊鎭 std::pair AnnTrain::identifyChinese(cv::Mat input) { cv::Mat feature = charFeatures2(input, kPredictSize); float maxVal = -2; @@ -124,7 +136,10 @@ std::pair AnnTrain::identifyChinese(cv::Mat input) { return std::make_pair(s, province); } - +// 杩欐浠g爜鏄疉nnTrain绫荤殑identifyChinese鏂规硶锛屾帴鍙椾竴涓猚v::Mat绫诲瀷鐨勮緭鍏ュ弬鏁癷nput銆 +// 棣栧厛璋冪敤charFeatures2鍑芥暟鎻愬彇鐗瑰緛锛岀劧鍚庝娇鐢ㄧ缁忕綉缁渁nn_瀵圭壒寰佽繘琛岄娴嬶紝寰楀埌杈撳嚭output銆 +// 鎺ョ潃閬嶅巻output锛屾壘鍒版渶澶у煎搴旂殑绱㈠紩result锛屽苟璁$畻鍑烘渶缁堢殑绱㈠紩index銆 +// 鏈鍚庢牴鎹甶ndex鑾峰彇瀵瑰簲鐨勫瓧绗ey锛屽啀閫氳繃kv_鑾峰彇瀵瑰簲鐨勭渷浠絧rovince锛屾渶缁堣繑鍥炰竴涓寘鍚瓧绗﹀拰鐪佷唤鐨刾air銆 std::pair AnnTrain::identify(cv::Mat input) { cv::Mat feature = charFeatures2(input, kPredictSize); @@ -155,7 +170,10 @@ std::pair AnnTrain::identify(cv::Mat input) { return std::make_pair(s, province); } } - +// 杩欐浠g爜鏄疉nnTrain绫荤殑identify鏂规硶锛屾帴鍙椾竴涓猚v::Mat绫诲瀷鐨勮緭鍏ュ弬鏁癷nput銆 +// 棣栧厛璋冪敤charFeatures2鍑芥暟鎻愬彇鐗瑰緛锛岀劧鍚庝娇鐢ㄧ缁忕綉缁渁nn_瀵圭壒寰佽繘琛岄娴嬶紝寰楀埌杈撳嚭output銆 +// 鎺ョ潃閬嶅巻output锛屾壘鍒版渶澶у煎搴旂殑绱㈠紩result锛屽苟璁$畻鍑烘渶缁堢殑绱㈠紩index銆 +// 鏈鍚庢牴鎹甶ndex鍒ゆ柇杩斿洖鐨勫瓧绗﹀拰鐪佷唤淇℃伅锛岃繑鍥炰竴涓寘鍚瓧绗﹀拰鐪佷唤鐨刾air銆 void AnnTrain::test() { assert(chars_folder_); @@ -175,7 +193,10 @@ void AnnTrain::test() { auto chars_files = utils::getFiles(sub_folder); int corrects = 0, sum = 0; std::vector> error_files; - +// 杩欐浠g爜鏄疉nnTrain绫荤殑test鏂规硶锛岀敤浜庢祴璇曞瓧绗﹁瘑鍒殑鍑嗙‘鐜囥 +// 棣栧厛鏍规嵁type鐨勫肩‘瀹歝lassNumber锛岀劧鍚庨亶鍘嗘瘡涓瓧绗︾殑鏂囦欢澶硅繘琛屾祴璇曘 +// 鍦ㄦ祴璇曡繃绋嬩腑锛屼細缁熻姝g‘璇嗗埆鐨勫瓧绗︽暟閲忓拰鎬绘祴璇曞瓧绗︽暟閲忥紝浠ュ強姣忎釜瀛楃鐨勮瘑鍒噯纭巼銆 +// 鏈鍚庤緭鍑烘荤殑娴嬭瘯缁撴灉鍜屽钩鍧囧噯纭巼銆 for (auto file : chars_files) { auto img = cv::imread(file, 0); // a grayscale image if (!img.data) { @@ -196,6 +217,11 @@ void AnnTrain::test() { ++sum; ++sum_all; } +// 杩欐浠g爜鏄竴涓惊鐜紝閬嶅巻chars_files涓殑鏂囦欢锛屽姣忎釜鏂囦欢杩涜澶勭悊銆 +// 棣栧厛浣跨敤OpenCV鐨刬mread鍑芥暟璇诲彇鏂囦欢涓虹伆搴﹀浘鍍廼mg锛岀劧鍚庡垽鏂璱mg鏄惁涓虹┖銆 +// 濡傛灉type涓0锛屽垯璋冪敤identify鍑芥暟瀵瑰浘鍍忚繘琛岃瘑鍒紝鍚﹀垯璋冪敤identifyChinese鍑芥暟銆 +// 濡傛灉璇嗗埆缁撴灉涓巆har_key鐩稿悓锛屽垯灏哻orrects鍜宑orrects_all鍔犱竴锛屽惁鍒欏皢閿欒淇℃伅鍔犲叆error_files銆 +// 鏈鍚庡皢sum鍜宻um_all鍔犱竴銆 float rate = (float)corrects / (sum == 0 ? 1 : sum); fprintf(stdout, ">> [sum: %d, correct: %d, rate: %.2f]\n", sum, corrects, rate); rate_list.push_back(rate); @@ -217,6 +243,10 @@ void AnnTrain::test() { } fprintf(stdout, ">> [\n%s\n ]\n", error_string.c_str()); } +// 杩欐浠g爜璁$畻浜嗚瘑鍒噯纭巼锛屽苟杈撳嚭浜嗘瘡涓瓧绗︾殑璇嗗埆缁撴灉鍜岄敊璇俊鎭 +// 棣栧厛璁$畻浜嗚瘑鍒噯纭巼rate锛屽苟灏嗗叾鍔犲叆rate_list涓 +// 鐒跺悗鏋勫缓浜嗛敊璇俊鎭瓧绗︿覆error_string锛岄亶鍘唀rror_files骞跺皢閿欒淇℃伅娣诲姞鍒板瓧绗︿覆涓 +// 鏈鍚庝娇鐢╢printf杈撳嚭浜嗘荤殑璇嗗埆缁撴灉鍜岄敊璇俊鎭 fprintf(stdout, ">> [sum_all: %d, correct_all: %d, rate: %.4f]\n", sum_all, corrects_all, (float)corrects_all / (sum_all == 0 ? 1 : sum_all)); @@ -226,6 +256,8 @@ void AnnTrain::test() { fprintf(stdout, ">> [classNumber: %d, avg_rate: %.4f]\n", classNumber, rate_mean); } +// 杩欐浠g爜鐢ㄤ簬杈撳嚭鎬荤殑娴嬭瘯缁撴灉鍜屽钩鍧囧噯纭巼銆 +// 棣栧厛杈撳嚭鎬荤殑娴嬭瘯缁撴灉鍜屽噯纭巼锛岀劧鍚庤绠椾簡姣忎釜瀛楃鐨勮瘑鍒噯纭巼鐨勫钩鍧囧煎苟杈撳嚭銆 cv::Mat getSyntheticImage(const Mat& image) { int rand_type = rand(); Mat result = image.clone(); @@ -244,7 +276,11 @@ cv::Mat getSyntheticImage(const Mat& image) { return result; } - +// 璇ヤ唬鐮佸畾涔変簡涓涓嚱鏁癵etSyntheticImage锛屾帴鍙椾竴涓猚v::Mat绫诲瀷鐨勫弬鏁癷mage銆 +//棣栧厛鐢熸垚涓涓殢鏈烘暟rand_type锛岀劧鍚庡皢result鍒濆鍖栦负image鐨勫厠闅嗐 +//濡傛灉rand_type涓哄伓鏁帮紝鍒欑敓鎴愪袱涓殢鏈烘暟ran_x鍜宺an_y锛岀劧鍚庤皟鐢╰ranslateImg鍑芥暟瀵箁esult杩涜骞崇Щ鎿嶄綔銆 +// 濡傛灉rand_type涓哄鏁帮紝鍒欑敓鎴愪竴涓殢鏈鸿搴ngle锛岀劧鍚庤皟鐢╮otateImg鍑芥暟瀵箁esult杩涜鏃嬭浆鎿嶄綔銆 +// 鏈鍚庤繑鍥瀝esult銆 cv::Ptr AnnTrain::sdata(size_t number_for_count) { assert(chars_folder_); @@ -256,6 +292,9 @@ cv::Ptr AnnTrain::sdata(size_t number_for_count) { if (type == 1) classNumber = kChineseNumber; srand((unsigned)time(0)); + // 杩欐浠g爜鏄疉nnTrain绫荤殑sdata鏂规硶锛岀敤浜庣敓鎴愯缁冩暟鎹 +// 棣栧厛妫鏌hars_folder_鏄惁瀛樺湪锛岀劧鍚庡垵濮嬪寲samples鍜宭abels銆 +// 鏍规嵁type鐨勫肩‘瀹歝lassNumber锛岀劧鍚庝娇鐢╯rand鍑芥暟鍒濆鍖栭殢鏈烘暟绉嶅瓙銆 for (int i = 0; i < classNumber; ++i) { auto char_key = kChars[i + kCharsTotalNumber - classNumber]; @@ -274,7 +313,11 @@ cv::Ptr AnnTrain::sdata(size_t number_for_count) { auto img = cv::imread(file, 0); // a grayscale image matVec.push_back(img); } - +// 杩欐浠g爜鏄竴涓惊鐜紝閬嶅巻姣忎釜瀛楃鏂囦欢澶逛腑鐨勬枃浠讹紝骞跺皢鏂囦欢璇诲彇涓虹伆搴﹀浘鍍忓悗瀛樺叆matVec涓 +// 棣栧厛鏍规嵁寰幆鍙橀噺i璁$畻鍑哄綋鍓嶅瓧绗︾殑鍏抽敭瀛梒har_key锛屽苟鏋勫缓瀵瑰簲鐨勫瓙鏂囦欢澶硅矾寰剆ub_folder銆 +// 鐒跺悗浣跨敤utils::getFiles鍑芥暟鑾峰彇瀛愭枃浠跺す涓殑鏂囦欢鍒楄〃chars_files锛屽苟缁熻鏂囦欢鏁伴噺char_size銆 +// 鎺ョ潃鍒濆鍖栦簡涓涓瓨鍌ㄧ伆搴﹀浘鍍忕殑鍚戦噺matVec锛屽苟棰勭暀浜唍umber_for_count涓厓绱犵殑绌洪棿銆 +// 閬嶅巻chars_files锛屼娇鐢╟v::imread鍑芥暟璇诲彇鏂囦欢涓虹伆搴﹀浘鍍廼mg锛屽苟灏嗗叾瀛樺叆matVec涓 for (int t = 0; t < (int)number_for_count - (int)char_size; t++) { int rand_range = char_size + t; int ran_num = rand() % rand_range; @@ -287,7 +330,10 @@ cv::Ptr AnnTrain::sdata(size_t number_for_count) { imwrite(ss.str(), simg); } } - +// 杩欐浠g爜鏄竴涓惊鐜紝鐢ㄤ簬鐢熸垚鍚堟垚鍥惧儚骞朵繚瀛樺埌鏂囦欢涓 +// 棣栧厛寰幆鍙橀噺t浠0鍒皀umber_for_count - char_size锛岀劧鍚庣敓鎴愪竴涓殢鏈烘暟ran_num銆 +// 鎺ョ潃浠巑atVec涓幏鍙栧搴旂储寮曠殑鍥惧儚img锛屽苟璋冪敤getSyntheticImage鍑芥暟鐢熸垚鍚堟垚鍥惧儚simg銆 +// 灏唖img娣诲姞鍒癿atVec涓紝骞朵娇鐢╥mwrite鍑芥暟灏唖img淇濆瓨涓烘枃浠躲 fprintf(stdout, ">> Characters count: %d \n", (int)matVec.size()); for (auto img : matVec) { @@ -297,7 +343,8 @@ cv::Ptr AnnTrain::sdata(size_t number_for_count) { labels.push_back(i); } } - +// 杩欐浠g爜鐢ㄤ簬缁熻瀛楃鏁伴噺骞跺皢鐗瑰緛鍜屾爣绛炬坊鍔犲埌璁粌鏁版嵁涓 +// 棣栧厛浣跨敤fprintf杈撳嚭瀛楃鏁伴噺锛岀劧鍚庨亶鍘唌atVec涓殑鍥惧儚锛屽姣忎釜鍥惧儚鎻愬彇鐗瑰緛骞跺皢鐗瑰緛鍜屾爣绛炬坊鍔犲埌璁粌鏁版嵁涓 cv::Mat samples_; samples.convertTo(samples_, CV_32F); cv::Mat train_classes = @@ -310,7 +357,10 @@ cv::Ptr AnnTrain::sdata(size_t number_for_count) { return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE, train_classes); } - +// 璇ラ儴鍒嗕唬鐮佹槸AnnTrain绫荤殑tdata鏂规硶锛岀敤浜庣敓鎴愯缁冩暟鎹 +// 棣栧厛灏唖amples杞崲涓篊V_32F绫诲瀷鐨剆amples_锛岀劧鍚庡垵濮嬪寲train_classes涓哄叏闆剁煩闃点 +// 鎺ョ潃閬嶅巻train_classes鐨勬瘡涓琛岋紝灏嗗搴斾綅缃殑鍊艰涓1銆 +// 鏈鍚庝娇鐢╟v::ml::TrainData::create鍑芥暟鍒涘缓骞惰繑鍥炶缁冩暟鎹璞° cv::Ptr AnnTrain::tdata() { assert(chars_folder_); @@ -322,7 +372,9 @@ cv::Ptr AnnTrain::tdata() { int classNumber = 0; if (type == 0) classNumber = kCharsTotalNumber; if (type == 1) classNumber = kChineseNumber; - +// 杩欐浠g爜鏄疉nnTrain绫荤殑tdata鏂规硶锛岀敤浜庣敓鎴愯缁冩暟鎹 +// 棣栧厛妫鏌hars_folder_鏄惁瀛樺湪锛岀劧鍚庡垵濮嬪寲samples鍜宭abels銆 +// 鏍规嵁type鐨勫肩‘瀹歝lassNumber銆 for (int i = 0; i < classNumber; ++i) { auto char_key = kChars[i + kCharsTotalNumber - classNumber]; char sub_folder[512] = {0}; @@ -340,7 +392,11 @@ cv::Ptr AnnTrain::tdata() { labels.push_back(i); } } - +// 杩欐浠g爜鏄竴涓惊鐜紝閬嶅巻姣忎釜瀛楃鏂囦欢澶逛腑鐨勬枃浠讹紝骞跺皢鏂囦欢璇诲彇涓虹伆搴﹀浘鍍忓悗鎻愬彇鐗瑰緛骞舵坊鍔犲埌璁粌鏁版嵁涓 +// 棣栧厛鏍规嵁寰幆鍙橀噺i璁$畻鍑哄綋鍓嶅瓧绗︾殑鍏抽敭瀛梒har_key锛屽苟鏋勫缓瀵瑰簲鐨勫瓙鏂囦欢澶硅矾寰剆ub_folder銆 +// 鐒跺悗浣跨敤utils::getFiles鍑芥暟鑾峰彇瀛愭枃浠跺す涓殑鏂囦欢鍒楄〃chars_files锛屽苟閬嶅巻姣忎釜鏂囦欢銆 +// 瀵规瘡涓枃浠朵娇鐢╟v::imread鍑芥暟璇诲彇涓虹伆搴﹀浘鍍廼mg锛岀劧鍚庤皟鐢╟harFeatures2鍑芥暟鎻愬彇鐗瑰緛fps锛屽苟灏嗗叾娣诲姞鍒皊amples涓 +// 鍚屾椂灏嗗綋鍓嶅瓧绗︾殑鏍囩i娣诲姞鍒發abels涓 cv::Mat samples_; samples.convertTo(samples_, CV_32F); cv::Mat train_classes = @@ -354,3 +410,6 @@ cv::Ptr AnnTrain::tdata() { train_classes); } } +// 璇ラ儴鍒嗕唬鐮佹槸鐢ㄤ簬鐢熸垚璁粌鏁版嵁鐨勪竴閮ㄥ垎锛岄鍏堝皢samples杞崲涓篊V_32F绫诲瀷鐨剆amples_锛 +// 鐒跺悗鍒濆鍖杢rain_classes涓哄叏闆剁煩闃碉紝鎺ョ潃閬嶅巻train_classes鐨勬瘡涓琛岋紝灏嗗搴斾綅缃殑鍊艰涓1銆 +// 鏈鍚庝娇鐢╟v::ml::TrainData::create鍑芥暟鍒涘缓骞惰繑鍥炶缁冩暟鎹璞° \ No newline at end of file diff --git a/src/src/train/create_data.cpp b/src/src/train/create_data.cpp index 5eb713a..dd8c5b8 100644 --- a/src/src/train/create_data.cpp +++ b/src/src/train/create_data.cpp @@ -7,7 +7,10 @@ namespace easypr { assert(img.type() == CV_8UC1); int w = img.cols; int h = img.rows; - +// 璇ラ儴鍒嗕唬鐮佸畾涔変簡涓涓悕涓篻etBorderColor鐨勫嚱鏁帮紝鐢ㄤ簬鑾峰彇鍥惧儚杈圭晫鐨勯鑹插 +// 鍑芥暟棣栧厛纭繚杈撳叆鍥惧儚鏄崟閫氶亾鐏板害鍥惧儚锛岀劧鍚庤幏鍙栧浘鍍忕殑瀹藉害鍜岄珮搴 +// 鎺ヤ笅鏉ラ氳繃閬嶅巻鍥惧儚杈圭晫鍍忕礌鐨勬柟寮忚绠楄竟鐣岄鑹茬殑骞冲潎鍊硷紝骞惰繑鍥炴暣鏁扮被鍨嬬殑骞冲潎鍊 +// 璇ユ枃浠惰繕鍖呭惈浜嗕竴浜涘叾浠栧浘鍍忓鐞嗗嚱鏁帮紝濡傚钩绉汇佹棆杞佽鍓拰鐢熸垚鍚堟垚鍥惧儚鐨勫嚱鏁 float sum = 0; for (int i = 0; i < h; ++i) { sum += img.at(i, 0); @@ -21,7 +24,9 @@ namespace easypr { float avg = sum / float(w + w + h + h); return int(avg); } - +// 璇ラ儴鍒嗕唬鐮佽绠椾簡鍥惧儚杈圭晫鍍忕礌鐨勫钩鍧囧间綔涓鸿竟鐣岄鑹插硷紝骞惰繑鍥炴暣鏁扮被鍨嬬殑骞冲潎鍊 +// 棣栧厛瀵瑰浘鍍忚竟鐣屽儚绱犺繘琛岄亶鍘嗭紝绱姞鍍忕礌鍊煎埌sum涓 +// 鐒跺悗璁$畻骞冲潎鍊糰vg锛屽苟杩斿洖鏁存暟绫诲瀷鐨勫钩鍧囧 // shift an image Mat translateImg(Mat img, int offsetx, int offsety, int bk){ Mat dst; @@ -31,7 +36,12 @@ namespace easypr { warpAffine(img, dst, trans_mat, img.size(), 1, 0, Scalar(bk)); return dst; } - +// 璇ラ儴鍒嗕唬鐮佸畾涔変簡涓涓悕涓簍ranslateImg鐨勫嚱鏁帮紝鐢ㄤ簬瀵瑰浘鍍忚繘琛屽钩绉绘搷浣 +// 鍑芥暟鎺ュ彈杈撳叆鍥惧儚img浠ュ強x鍜寉鏂瑰悜鐨勫亸绉婚噺offsetx鍜宱ffsety锛屼互鍙婅儗鏅鑹瞓k +// 鍦ㄥ嚱鏁板唴閮紝棣栧厛鍒涘缓浜嗕竴涓┖鐨勭洰鏍囧浘鍍廳st +// 鐒跺悗瀹氫箟浜嗕竴涓2x3鐨勫彉鎹㈢煩闃祎rans_mat锛岀敤浜庤〃绀哄钩绉绘搷浣 +// 鏈鍚庤皟鐢╳arpAffine鍑芥暟瀵硅緭鍏ュ浘鍍忚繘琛屽钩绉绘搷浣滐紝骞跺皢缁撴灉瀛樺偍鍦ㄧ洰鏍囧浘鍍廳st涓 +// 鏈缁堣繑鍥炲钩绉诲悗鐨勭洰鏍囧浘鍍廳st // rotate an image Mat rotateImg(Mat source, float angle, int bk){ Point2f src_center(source.cols / 2.0F, source.rows / 2.0F); @@ -40,7 +50,12 @@ namespace easypr { warpAffine(source, dst, rot_mat, source.size(), 1, 0, Scalar(bk)); return dst; } - +// 璇ラ儴鍒嗕唬鐮佸畾涔変簡涓涓悕涓簉otateImg鐨勫嚱鏁帮紝鐢ㄤ簬瀵瑰浘鍍忚繘琛屾棆杞搷浣 +// 鍑芥暟鎺ュ彈杈撳叆鍥惧儚source浠ュ強鏃嬭浆瑙掑害angle鍜岃儗鏅鑹瞓k浣滀负鍙傛暟 +// 鍦ㄥ嚱鏁板唴閮紝棣栧厛璁$畻浜嗚緭鍏ュ浘鍍忕殑涓績鐐瑰潗鏍噑rc_center +// 鐒跺悗鍒╃敤getRotationMatrix2D鍑芥暟璁$畻浜嗘棆杞煩闃祌ot_mat +// 鏈鍚庤皟鐢╳arpAffine鍑芥暟瀵硅緭鍏ュ浘鍍忚繘琛屾棆杞搷浣滐紝骞跺皢缁撴灉瀛樺偍鍦ㄧ洰鏍囧浘鍍廳st涓 +// 鏈缁堣繑鍥炴棆杞悗鐨勭洰鏍囧浘鍍廳st // crop the image Mat cropImg(Mat src, int x, int y, int shift, int bk){ int width = src.cols; @@ -61,7 +76,13 @@ namespace easypr { resize(dst, dst, Size(width, height)); return dst; } - +// 璇ラ儴鍒嗕唬鐮佸畾涔変簡涓涓悕涓篶ropImg鐨勫嚱鏁帮紝鐢ㄤ簬瀵瑰浘鍍忚繘琛岃鍓搷浣 +// 鍑芥暟鎺ュ彈杈撳叆鍥惧儚src浠ュ強瑁佸壀璧峰鐐圭殑x鍜寉鍧愭爣锛岃鍓昂瀵竤hift鍜岃儗鏅鑹瞓k浣滀负鍙傛暟 +// 鍦ㄥ嚱鏁板唴閮紝棣栧厛鑾峰彇杈撳叆鍥惧儚鐨勫搴﹀拰楂樺害 +// 鐒跺悗璁$畻瑁佸壀鍚庣殑鍥惧儚瀹藉害鍜岄珮搴 +// 鎺ョ潃鏍规嵁瑁佸壀璧峰鐐瑰拰瑁佸壀灏哄璁$畻瑁佸壀鍖哄煙鐨勭煩褰ect +// 灏嗚鍓悗鐨勫浘鍍忓瓨鍌ㄥ湪鐩爣鍥惧儚dst涓紝骞惰繘琛屽ぇ灏忚皟鏁 +// 鏈缁堣繑鍥炶鍓悗鐨勭洰鏍囧浘鍍廳st Mat generateSyntheticImage(const Mat& image, int use_swap) { int rd = rand(); int bkColor = getBoderColor(image); @@ -85,4 +106,10 @@ namespace easypr { return result; } } - +// 璇ラ儴鍒嗕唬鐮佸畾涔変簡涓涓悕涓篻enerateSyntheticImage鐨勫嚱鏁帮紝鐢ㄤ簬鐢熸垚鍚堟垚鍥惧儚 +// 鍑芥暟鎺ュ彈杈撳叆鍥惧儚image浠ュ強涓涓暣鏁板弬鏁皍se_swap +// 棣栧厛閫氳繃rand鍑芥暟鐢熸垚涓涓殢鏈烘暟rd +// 鐒跺悗璋冪敤getBorderColor鍑芥暟鑾峰彇杈撳叆鍥惧儚鐨勮竟鐣岄鑹插硷紝骞跺瓨鍌ㄥ湪bkColor涓 +// 鎺ョ潃瀵硅緭鍏ュ浘鍍忚繘琛屽厠闅嗭紝瀛樺偍鍦╮esult涓 +// 涔嬪悗鏍规嵁闅忔満鏁皉d鐨勪笉鍚屼綅杩涜瑁佸壀銆佸钩绉诲拰鏃嬭浆鎿嶄綔锛屽苟灏嗙粨鏋滃瓨鍌ㄥ湪result涓 +// 鏈缁堣繑鍥炲悎鎴愬悗鐨勭洰鏍囧浘鍍弐esult diff --git a/src/src/train/svm_train.cpp b/src/src/train/svm_train.cpp index edd711e..154c5fd 100644 --- a/src/src/train/svm_train.cpp +++ b/src/src/train/svm_train.cpp @@ -18,7 +18,9 @@ SvmTrain::SvmTrain(const char* plates_folder, const char* xml) extractFeature = getHistomPlusColoFeatures; } - +// 杩欐浠g爜鏄疭vmTrain绫荤殑鏋勯犲嚱鏁帮紝鎺ュ彈涓や釜const char*绫诲瀷鐨勫弬鏁皃lates_folder鍜寈ml銆 +// 鍦ㄦ瀯閫犲嚱鏁颁腑锛屼娇鐢ㄤ簡assert鍑芥暟鏉ョ‘淇漰lates_folder鍜寈ml涓嶄负绌恒 +// 鍚屾椂锛屽皢extractFeature璁剧疆涓篻etHistomPlusColoFeatures鍑芥暟銆 void SvmTrain::train() { svm_ = cv::ml::SVM::create(); svm_->setType(cv::ml::SVM::C_SVC); @@ -31,7 +33,10 @@ void SvmTrain::train() { svm_->setNu(0.1); svm_->setP(0.1); svm_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 20000, 0.0001)); - +// 杩欐浠g爜鏄疭vmTrain绫荤殑train鏂规硶锛岀敤浜庤缁冩敮鎸佸悜閲忔満妯″瀷銆 +// 鍦ㄦ柟娉曚腑锛屽垱寤轰簡涓涓猄VM瀵硅薄锛屽苟璁剧疆浜嗗叾绫诲瀷銆佹牳鍑芥暟绫诲瀷銆佸弬鏁扮瓑銆 +// 鏈鍚庤皟鐢ㄤ簡trainAuto鏂规硶杩涜妯″瀷璁粌锛屽苟淇濆瓨浜嗚缁冨ソ鐨勬ā鍨嬨 +// 璇ユ柟娉曡繕鍖呮嫭浜嗕竴浜涙墦鍗拌緭鍑哄拰娴嬭瘯鏂规硶鐨勮皟鐢ㄣ this->prepare(); if (train_file_list_.size() == 0) { @@ -41,7 +46,9 @@ void SvmTrain::train() { return; } auto train_data = tdata(); - +// 杩欐浠g爜鏄湪svm_train.cpp鏂囦欢涓殑train鏂规硶涓殑涓閮ㄥ垎銆 +// 棣栧厛璋冪敤浜唒repare鏂规硶锛岀劧鍚庢鏌rain_file_list_鐨勫ぇ灏忔槸鍚︿负0锛屽鏋滄槸鍒欒緭鍑烘彁绀轰俊鎭苟杩斿洖銆 +// 鏈鍚庤皟鐢ㄤ簡tdata鏂规硶骞跺皢杩斿洖鍊艰祴缁檛rain_data銆 fprintf(stdout, ">> Training SVM model, please wait...\n"); long start = utils::getTimestamp(); svm_->trainAuto(train_data, 10, SVM::getDefaultGrid(SVM::C), @@ -49,7 +56,10 @@ void SvmTrain::train() { SVM::getDefaultGrid(SVM::NU), SVM::getDefaultGrid(SVM::COEF), SVM::getDefaultGrid(SVM::DEGREE), true); //svm_->train(train_data); - +// 杩欐浠g爜鏄湪svm_train.cpp鏂囦欢涓殑train鏂规硶涓殑涓閮ㄥ垎銆 +// 棣栧厛杈撳嚭璁粌鎻愮ず淇℃伅锛岀劧鍚庤幏鍙栧綋鍓嶆椂闂存埑浣滀负璁粌寮濮嬫椂闂淬 +// 鎺ョ潃璋冪敤svm_->trainAuto鏂规硶杩涜鏀寔鍚戦噺鏈烘ā鍨嬬殑鑷姩璁粌锛屼娇鐢ㄤ簡榛樿鐨勫弬鏁扮綉鏍煎拰骞惰鍖栥 +// 鏈鍚庢敞閲婃帀浜嗘棫鐨剆vm_->train鏂规硶鐨勮皟鐢ㄣ long end = utils::getTimestamp(); fprintf(stdout, ">> Training done. Time elapse: %ldms\n", end - start); fprintf(stdout, ">> Saving model file...\n"); @@ -61,7 +71,11 @@ void SvmTrain::train() { this->test(); } - +// 杩欐浠g爜鏄疭vmTrain绫讳腑鐨則est鏂规硶锛岀敤浜庢祴璇曟敮鎸佸悜閲忔満妯″瀷銆 +// 棣栧厛閫氳繃utils::getTimestamp()鑾峰彇褰撳墠鏃堕棿鎴充綔涓烘祴璇曠粨鏉熸椂闂达紝骞惰绠楄缁冩椂闂淬 +// 鐒跺悗杈撳嚭璁粌瀹屾垚鐨勬彁绀轰俊鎭紝骞朵繚瀛樿缁冨ソ鐨勬ā鍨嬪埌鎸囧畾鐨剎ml鏂囦欢涓 +// 鎺ョ潃杈撳嚭妯″瀷淇濆瓨鐨勮矾寰勶紝骞舵彁绀鸿繘琛屾祴璇曘 +// 鏈鍚庤皟鐢ㄤ簡SvmTrain绫讳腑鐨則est鏂规硶杩涜妯″瀷娴嬭瘯銆 void SvmTrain::test() { // 1.4 bug fix: old 1.4 ver there is no null judge // if (NULL == svm_) @@ -70,13 +84,16 @@ void SvmTrain::test() { if (test_file_list_.empty()) { this->prepare(); } - + // 杩欐浠g爜鏄疭vmTrain绫讳腑鐨則est鏂规硶銆 +// 棣栧厛閫氳繃LOAD_SVM_MODEL瀹忓姞杞戒簡svm妯″瀷銆 +// 鐒跺悗妫鏌est_file_list_鏄惁涓虹┖锛屽鏋滀负绌哄垯璋冪敤prepare鏂规硶銆 double count_all = test_file_list_.size(); double ptrue_rtrue = 0; double ptrue_rfalse = 0; double pfalse_rtrue = 0; double pfalse_rfalse = 0; - +// 杩欐浠g爜鐢ㄤ簬鍒濆鍖栧洓涓猟ouble绫诲瀷鐨勫彉閲忥紝鍒嗗埆琛ㄧず鎬绘暟銆佺湡姝d緥銆佸亣鍙嶄緥鍜屽亣姝d緥銆 +// 杩欎簺鍙橀噺灏嗗湪鍚庣画鐨勫惊鐜腑鐢ㄤ簬缁熻鏀寔鍚戦噺鏈烘ā鍨嬬殑棰勬祴缁撴灉銆 for (auto item : test_file_list_) { auto image = cv::imread(item.file); if (!image.data) { @@ -85,7 +102,10 @@ void SvmTrain::test() { } cv::Mat feature; extractFeature(image, feature); - +// 杩欐浠g爜鏄湪SvmTrain绫讳腑鐨則est鏂规硶涓殑寰幆閮ㄥ垎銆 +// 棣栧厛閬嶅巻test_file_list_涓殑姣忎釜item锛岀劧鍚庝娇鐢∣penCV鐨刬mread鍑芥暟璇诲彇鍥惧儚鏂囦欢銆 +// 濡傛灉鍥惧儚鏁版嵁涓虹┖锛屽垯杈撳嚭"no"骞剁户缁笅涓娆″惊鐜 +// 鍚﹀垯鍒涘缓涓涓猚v::Mat绫诲瀷鐨刦eature鍙橀噺锛屽苟璋冪敤extractFeature鍑芥暟鎻愬彇鍥惧儚鐗瑰緛銆 auto predict = int(svm_->predict(feature)); //std::cout << "predict: " << predict << std::endl; @@ -95,13 +115,17 @@ void SvmTrain::test() { if (predict == kInverse && real == kForward) pfalse_rtrue++; if (predict == kInverse && real == kInverse) pfalse_rfalse++; } - +// 杩欐浠g爜鐢ㄤ簬瀵瑰浘鍍忕壒寰佽繘琛岄娴嬶紝骞剁粺璁¢娴嬬粨鏋滅殑鐪熸渚嬨佸亣鍙嶄緥銆佸亣姝d緥鍜岀湡鍙嶄緥鐨勬暟閲忋 +// 棣栧厛浣跨敤svm_->predict鍑芥暟瀵圭壒寰佽繘琛岄娴嬶紝鐒跺悗鏍规嵁棰勬祴缁撴灉鍜岀湡瀹炴爣绛捐繘琛岀粺璁° +// 鏈鍚庢牴鎹粺璁$粨鏋滆绠楃簿纭巼锛坧recise锛夈佸彫鍥炵巼锛坮ecall锛夊拰F鍒嗘暟锛團socre锛夈 std::cout << "count_all: " << count_all << std::endl; std::cout << "ptrue_rtrue: " << ptrue_rtrue << std::endl; std::cout << "ptrue_rfalse: " << ptrue_rfalse << std::endl; std::cout << "pfalse_rtrue: " << pfalse_rtrue << std::endl; std::cout << "pfalse_rfalse: " << pfalse_rfalse << std::endl; - +// 璇勬敞璇ヤ唬鐮 +// 杩欐浠g爜鐢ㄤ簬杈撳嚭count_all銆乸true_rtrue銆乸true_rfalse銆乸false_rtrue鍜宲false_rfalse鐨勫 +// 杩欎簺鍊煎垎鍒〃绀烘绘暟銆佺湡姝d緥銆佸亣鍙嶄緥銆佸亣姝d緥鍜岀湡鍙嶄緥鐨勬暟閲 double precise = 0; if (ptrue_rtrue + ptrue_rfalse != 0) { precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse); @@ -110,7 +134,8 @@ void SvmTrain::test() { std::cout << "precise: " << "NA" << std::endl; } - +// 杩欐浠g爜鐢ㄤ簬璁$畻绮剧‘鐜囷紙precision锛夛紝濡傛灉ptrue_rtrue鍜宲true_rfalse涔嬪拰涓嶄负0锛 +// 鍒欒绠楃簿纭巼骞惰緭鍑猴紝鍚﹀垯杈撳嚭"NA" double recall = 0; if (ptrue_rtrue + pfalse_rtrue != 0) { recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue); @@ -119,7 +144,8 @@ void SvmTrain::test() { std::cout << "recall: " << "NA" << std::endl; } - +// 杩欐浠g爜璁$畻骞惰緭鍑哄彫鍥炵巼锛坮ecall锛夛紝鍙洖鐜囪〃绀鸿姝g‘棰勬祴涓烘渚嬬殑鏍锋湰鏁板崰鎵鏈夊疄闄呮渚嬫牱鏈暟鐨勬瘮渚嬨 +// 濡傛灉ptrue_rtrue鍜宲false_rtrue涔嬪拰涓嶄负0锛屽垯璁$畻鍙洖鐜囧苟杈撳嚭锛屽惁鍒欒緭鍑"NA"銆 double Fsocre = 0; if (precise + recall != 0) { Fsocre = 2 * (precise * recall) / (precise + recall); @@ -129,7 +155,9 @@ void SvmTrain::test() { << "NA" << std::endl; } } - +// 璇勬敞璇ヤ唬鐮 +// 杩欐浠g爜鐢ㄤ簬璁$畻骞惰緭鍑篎鍒嗘暟锛團socre锛夛紝F鍒嗘暟鏄簿纭巼锛坧recision锛夊拰鍙洖鐜囷紙recall锛夌殑璋冨拰骞冲潎鏁般 +// 棣栧厛鍒ゆ柇绮剧‘鐜囧拰鍙洖鐜囦箣鍜屾槸鍚︿负0锛屽鏋滀笉涓0锛屽垯璁$畻F鍒嗘暟骞惰緭鍑猴紝鍚﹀垯杈撳嚭"NA"銆 void SvmTrain::prepare() { srand(unsigned(time(NULL))); @@ -150,7 +178,10 @@ void SvmTrain::prepare() { sprintf(buffer, "%s/no/test", plates_folder_); auto no_file_test_list = utils::getFiles(buffer); std::random_shuffle(no_file_test_list.begin(), no_file_test_list.end()); - +// 璇ヤ唬鐮佹槸SvmTrain绫讳腑鐨刾repare鏂规硶锛岀敤浜庡噯澶囪缁冨拰娴嬭瘯鏁版嵁闆嗐 +// 棣栧厛浣跨敤srand鍜宼ime鍑芥暟鐢熸垚闅忔満绉嶅瓙銆 +// 鐒跺悗閫氳繃sprintf鍜寀tils::getFiles鍑芥暟鑾峰彇璁粌鍜屾祴璇曟暟鎹泦鐨勬枃浠跺垪琛紝骞跺鍒楄〃杩涜闅忔満鎵撲贡銆 +// 鏈鍚庡皢鑾峰彇鍒扮殑鏂囦欢鍒楄〃鍒嗗埆瀛樺偍鍒癶as_file_train_list銆乭as_file_test_list銆乶o_file_train_list鍜宯o_file_test_list涓 fprintf(stdout, ">> Collecting train data...\n"); for (auto file : has_file_train_list) @@ -167,11 +198,19 @@ void SvmTrain::prepare() { for (auto file : no_file_test_list) test_file_list_.push_back({ file, kInverse }); } - +// 璇ラ儴鍒嗕唬鐮佺敤浜庢敹闆嗚缁冩暟鎹拰娴嬭瘯鏁版嵁銆 +// 棣栧厛杈撳嚭鎻愮ず淇℃伅"Collecting train data..."锛岀劧鍚庨亶鍘唄as_file_train_list鍜宯o_file_train_list锛 +// 灏嗘枃浠惰矾寰勫拰鏍囩鍒嗗埆娣诲姞鍒皌rain_file_list_涓 +// 鐒跺悗杈撳嚭鎻愮ず淇℃伅"Collecting test data..."锛屽苟閬嶅巻has_file_test_list鍜宯o_file_test_list锛 +// 灏嗘枃浠惰矾寰勫拰鏍囩鍒嗗埆娣诲姞鍒皌est_file_list_涓 cv::Ptr SvmTrain::tdata() { cv::Mat samples; std::vector responses; - +// 杩欐浠g爜鏄疭vmTrain绫讳腑鐨則data鏂规硶锛岀敤浜庡噯澶囨敮鎸佸悜閲忔満璁粌鏁版嵁銆 +// 棣栧厛瀹氫箟浜嗕竴涓猚v::Mat绫诲瀷鐨剆amples鍜屼竴涓猻td::vector绫诲瀷鐨剅esponses銆 +// 鐒跺悗閬嶅巻train_file_list_涓殑姣忎釜鏂囦欢锛屼娇鐢∣penCV鐨刬mread鍑芥暟璇诲彇鍥惧儚鏂囦欢锛屾彁鍙栧浘鍍忕壒寰侊紝骞跺皢鐗瑰緛杞崲涓轰竴琛岀殑褰㈠紡銆 +// 灏嗙壒寰佸拰鏍囩鍒嗗埆娣诲姞鍒皊amples鍜宺esponses涓 +// 鏈鍚庡皢samples鍜宺esponses杞崲涓篊V_32FC1绫诲瀷鐨剆amples_鍜宺esponses_锛屽苟浣跨敤cv::ml::TrainData::create鍒涘缓璁粌鏁版嵁瀵硅薄骞惰繑鍥炪 for (auto f : train_file_list_) { auto image = cv::imread(f.file); if (!image.data) { @@ -185,7 +224,11 @@ cv::Ptr SvmTrain::tdata() { samples.push_back(feature); responses.push_back(int(f.label)); } - +// 杩欐浠g爜鏄湪SvmTrain绫讳腑鐨則data鏂规硶涓殑寰幆閮ㄥ垎銆 +// 棣栧厛閬嶅巻train_file_list_涓殑姣忎釜鏂囦欢锛屼娇鐢∣penCV鐨刬mread鍑芥暟璇诲彇鍥惧儚鏂囦欢銆 +// 濡傛灉鍥惧儚鏁版嵁涓虹┖锛屽垯杈撳嚭"Invalid image: 鏂囦欢璺緞 ignore."骞剁户缁笅涓娆″惊鐜 +// 鍚﹀垯鍒涘缓涓涓猚v::Mat绫诲瀷鐨刦eature鍙橀噺锛屽苟璋冪敤extractFeature鍑芥暟鎻愬彇鍥惧儚鐗瑰緛銆 +// 灏嗙壒寰佽浆鎹负涓琛岀殑褰㈠紡锛岀劧鍚庡皢鐗瑰緛鍜屾爣绛惧垎鍒坊鍔犲埌samples鍜宺esponses涓 cv::Mat samples_, responses_; samples.convertTo(samples_, CV_32FC1); cv::Mat(responses).copyTo(responses_); @@ -194,3 +237,6 @@ cv::Ptr SvmTrain::tdata() { } } // namespace easypr +// 杩欐浠g爜鏄湪SvmTrain绫讳腑鐨則data鏂规硶涓殑鏈鍚庨儴鍒嗐 +// 棣栧厛灏唖amples杞崲涓篊V_32FC1绫诲瀷鐨剆amples_锛岀劧鍚庡皢responses澶嶅埗鍒皉esponses_涓 +// 鏈鍚庝娇鐢╟v::ml::TrainData::create鍒涘缓骞惰繑鍥炶缁冩暟鎹璞° \ No newline at end of file diff --git a/src/src/train/train.cpp b/src/src/train/train.cpp index 79cd69f..b692833 100644 --- a/src/src/train/train.cpp +++ b/src/src/train/train.cpp @@ -6,3 +6,4 @@ ITrain::ITrain() {} ITrain::~ITrain() {} } +//杩欐浠g爜鏄竴涓狢++鏂囦欢锛屽畾涔変簡涓涓悕涓篒Train鐨勭被锛屽寘鍚簡涓涓粯璁ゆ瀯閫犲嚱鏁板拰涓涓瀽鏋勫嚱鏁般傝繖浜涘嚱鏁伴兘浣嶄簬easypr鍛藉悕绌洪棿涓 \ No newline at end of file