Compare commits
2 Commits
develop
...
zhaohaoyi_
Author | SHA1 | Date |
---|---|---|
heureux | 3e7dd9deaa | 10 months ago |
heureux | 494b46daee | 11 months ago |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,454 @@
|
||||
#include "easypr/core/chars_identify.h"
|
||||
#include "easypr/core/character.hpp"
|
||||
#include "easypr/core/core_func.h"
|
||||
#include "easypr/core/feature.h"
|
||||
#include "easypr/core/params.h"
|
||||
#include "easypr/config.h"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
namespace easypr {
|
||||
|
||||
CharsIdentify* CharsIdentify::instance_ = nullptr;
|
||||
|
||||
CharsIdentify* CharsIdentify::instance() {
|
||||
if (!instance_) {
|
||||
instance_ = new CharsIdentify;
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
CharsIdentify::CharsIdentify() {
|
||||
LOAD_ANN_MODEL(ann_, kDefaultAnnPath);
|
||||
LOAD_ANN_MODEL(annChinese_, kChineseAnnPath);
|
||||
LOAD_ANN_MODEL(annGray_, kGrayAnnPath);
|
||||
|
||||
kv_ = std::shared_ptr<Kv>(new Kv);
|
||||
kv_->load(kChineseMappingPath);
|
||||
|
||||
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::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::LoadChineseMapping(std::string path) {
|
||||
kv_->clear();
|
||||
kv_->load(path);
|
||||
}
|
||||
|
||||
void CharsIdentify::classify(cv::Mat featureRows, std::vector<int>& out_maxIndexs,
|
||||
std::vector<float>& out_maxVals, std::vector<bool> isChineseVec){
|
||||
int rowNum = featureRows.rows;
|
||||
|
||||
cv::Mat output(rowNum, kCharsTotalNumber, CV_32FC1);
|
||||
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];
|
||||
if (!isChinses) {
|
||||
result = 0;
|
||||
for (int j = 0; j < kCharactersNumber; j++) {
|
||||
float val = output_row.at<float>(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<float>(j);
|
||||
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
result = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
out_maxIndexs[output_index] = result;
|
||||
out_maxVals[output_index] = maxVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CharsIdentify::classify(std::vector<CCharacter>& charVec){
|
||||
size_t charVecSize = charVec.size();
|
||||
|
||||
if (charVecSize == 0)
|
||||
return;
|
||||
|
||||
Mat featureRows;
|
||||
for (size_t index = 0; index < charVecSize; index++) {
|
||||
Mat charInput = charVec[index].getCharacterMat();
|
||||
Mat feature = charFeatures(charInput, kPredictSize);
|
||||
featureRows.push_back(feature);
|
||||
}
|
||||
|
||||
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<float>(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;
|
||||
}
|
||||
else {
|
||||
result = kCharactersNumber;
|
||||
for (int j = kCharactersNumber; j < kCharsTotalNumber; j++) {
|
||||
float val = output_row.at<float>(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<CCharacter>& charVec){
|
||||
size_t charVecSize = charVec.size();
|
||||
if (charVecSize == 0)
|
||||
return;
|
||||
|
||||
Mat featureRows;
|
||||
for (size_t index = 0; index < charVecSize; index++) {
|
||||
Mat charInput = charVec[index].getCharacterMat();
|
||||
cv::Mat feature;
|
||||
extractFeature(charInput, feature);
|
||||
featureRows.push_back(feature);
|
||||
}
|
||||
|
||||
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<float>(j);
|
||||
//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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void CharsIdentify::classifyChinese(std::vector<CCharacter>& charVec){
|
||||
size_t charVecSize = charVec.size();
|
||||
|
||||
if (charVecSize == 0)
|
||||
return;
|
||||
|
||||
Mat featureRows;
|
||||
for (size_t index = 0; index < charVecSize; index++) {
|
||||
Mat charInput = charVec[index].getCharacterMat();
|
||||
Mat feature = charFeatures(charInput, kChineseSize);
|
||||
featureRows.push_back(feature);
|
||||
}
|
||||
|
||||
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);
|
||||
bool isChinese = true;
|
||||
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
|
||||
for (int j = 0; j < kChineseNumber; j++) {
|
||||
float val = output_row.at<float>(j);
|
||||
//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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int CharsIdentify::classify(cv::Mat f, float& maxVal, bool isChinses, bool isAlphabet){
|
||||
int result = 0;
|
||||
|
||||
cv::Mat output(1, kCharsTotalNumber, CV_32FC1);
|
||||
ann_->predict(f, output);
|
||||
|
||||
maxVal = -2.f;
|
||||
if (!isChinses) {
|
||||
if (!isAlphabet) {
|
||||
result = 0;
|
||||
for (int j = 0; j < kCharactersNumber; j++) {
|
||||
float val = output.at<float>(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<float>(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<float>(j);
|
||||
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
result = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
//std::cout << "maxVal:" << maxVal << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CharsIdentify::isCharacter(cv::Mat input, std::string& label, float& maxVal, bool isChinese) {
|
||||
cv::Mat feature = charFeatures(input, kPredictSize);
|
||||
auto index = static_cast<int>(classify(feature, maxVal, isChinese));
|
||||
|
||||
if (isChinese) {
|
||||
//std::cout << "maxVal:" << maxVal << std::endl;
|
||||
}
|
||||
|
||||
float chineseMaxThresh = 0.2f;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> CharsIdentify::identifyChinese(cv::Mat input, float& out, bool& isChinese) {
|
||||
cv::Mat feature = charFeatures(input, kChineseSize);
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
|
||||
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||
annChinese_->predict(feature, output);
|
||||
|
||||
for (int j = 0; j < kChineseNumber; j++) {
|
||||
float val = output.at<float>(j);
|
||||
//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;
|
||||
}
|
||||
else if (maxVal > 0.9){
|
||||
isChinese = true;
|
||||
}
|
||||
|
||||
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<std::string, std::string> CharsIdentify::identifyChineseGray(cv::Mat input, float& out, bool& isChinese) {
|
||||
cv::Mat feature;
|
||||
extractFeature(input, feature);
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||
annGray_->predict(feature, output);
|
||||
|
||||
for (int j = 0; j < kChineseNumber; j++) {
|
||||
float val = output.at<float>(j);
|
||||
//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;
|
||||
} else if (maxVal > 0.9){
|
||||
isChinese = true;
|
||||
}
|
||||
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<std::string, std::string> CharsIdentify::identify(cv::Mat input, bool isChinese, bool isAlphabet) {
|
||||
cv::Mat feature = charFeatures(input, kPredictSize);
|
||||
float maxVal = -2;
|
||||
auto index = static_cast<int>(classify(feature, maxVal, isChinese, isAlphabet));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int CharsIdentify::identify(std::vector<cv::Mat> inputs, std::vector<std::pair<std::string, std::string>>& outputs,
|
||||
std::vector<bool> isChineseVec) {
|
||||
Mat featureRows;
|
||||
size_t input_size = inputs.size();
|
||||
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<int> maxIndexs;
|
||||
std::vector<float> maxVals;
|
||||
classify(featureRows, maxIndexs, maxVals, isChineseVec);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
#include "easypr/core/chars_recognise.h"
|
||||
#include "easypr/core/character.hpp"
|
||||
#include "easypr/util/util.h"
|
||||
#include <ctime>
|
||||
|
||||
namespace easypr {
|
||||
|
||||
CCharsRecognise::CCharsRecognise() { m_charsSegment = new CCharsSegment(); }
|
||||
|
||||
CCharsRecognise::~CCharsRecognise() { SAFE_RELEASE(m_charsSegment); }
|
||||
|
||||
int CCharsRecognise::charsRecognise(Mat plate, std::string& plateLicense) {
|
||||
std::vector<Mat> matChars;
|
||||
int result = m_charsSegment->charsSegment(plate, matChars);
|
||||
if (result == 0) {
|
||||
int num = matChars.size();
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
Mat charMat = matChars.at(j);
|
||||
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);
|
||||
}
|
||||
else {
|
||||
isChinses = false;
|
||||
auto character = CharsIdentify::instance()->identify(charMat, isChinses);
|
||||
plateLicense.append(character.second);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (plateLicense.size() < 7) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int CCharsRecognise::charsRecognise(CPlate& plate, std::string& plateLicense) {
|
||||
std::vector<Mat> matChars;
|
||||
std::vector<Mat> grayChars;
|
||||
Mat plateMat = plate.getPlateMat();
|
||||
if (0) writeTempImage(plateMat, "plateMat/plate");
|
||||
Color color;
|
||||
if (plate.getPlateLocateType() == CMSER) {
|
||||
color = plate.getPlateColor();
|
||||
}
|
||||
else {
|
||||
int w = plateMat.cols;
|
||||
int h = plateMat.rows;
|
||||
Mat tmpMat = plateMat(Rect_<double>(w * 0.1, h * 0.1, w * 0.8, h * 0.8));
|
||||
color = getPlateType(tmpMat, true);
|
||||
}
|
||||
|
||||
int result = m_charsSegment->charsSegmentUsingOSTU(plateMat, matChars, grayChars, color);
|
||||
|
||||
if (result == 0) {
|
||||
int num = matChars.size();
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
Mat charMat = matChars.at(j);
|
||||
Mat grayChar = grayChars.at(j);
|
||||
if (color != Color::BLUE)
|
||||
grayChar = 255 - grayChar;
|
||||
|
||||
bool isChinses = false;
|
||||
std::pair<std::string, std::string> character;
|
||||
float maxVal;
|
||||
if (0 == j) {
|
||||
isChinses = true;
|
||||
bool judge = true;
|
||||
character = CharsIdentify::instance()->identifyChineseGray(grayChar, maxVal, judge);
|
||||
plateLicense.append(character.second);
|
||||
|
||||
// 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);
|
||||
plateLicense.append(character.second);
|
||||
}
|
||||
else {
|
||||
isChinses = false;
|
||||
SHOW_IMAGE(charMat, 0);
|
||||
character = CharsIdentify::instance()->identify(charMat, isChinses);
|
||||
plateLicense.append(character.second);
|
||||
}
|
||||
|
||||
CCharacter charResult;
|
||||
charResult.setCharacterMat(charMat);
|
||||
charResult.setCharacterGrayMat(grayChar);
|
||||
if (isChinses)
|
||||
charResult.setCharacterStr(character.first);
|
||||
else
|
||||
charResult.setCharacterStr(character.second);
|
||||
|
||||
plate.addReutCharacter(charResult);
|
||||
}
|
||||
if (plateLicense.size() < 7) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,466 @@
|
||||
#include "easypr/core/feature.h"
|
||||
#include "easypr/core/core_func.h"
|
||||
#include "thirdparty/LBP/lbp.hpp"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
|
||||
Mat getHistogram(Mat in) {
|
||||
const int VERTICAL = 0;
|
||||
const int HORIZONTAL = 1;
|
||||
|
||||
// Histogram features
|
||||
Mat vhist = ProjectedHistogram(in, VERTICAL);
|
||||
Mat hhist = ProjectedHistogram(in, HORIZONTAL);
|
||||
|
||||
// Last 10 is the number of moments components
|
||||
int numCols = vhist.cols + hhist.cols;
|
||||
|
||||
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < vhist.cols; i++) {
|
||||
out.at<float>(j) = vhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int i = 0; i < hhist.cols; i++) {
|
||||
out.at<float>(j) = hhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void getHistogramFeatures(const Mat& image, Mat& features) {
|
||||
Mat grayImage;
|
||||
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||
|
||||
//grayImage = histeq(grayImage);
|
||||
|
||||
Mat img_threshold;
|
||||
threshold(grayImage, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||
//Mat img_threshold = grayImage.clone();
|
||||
//spatial_ostu(img_threshold, 8, 2, getPlateType(image, false));
|
||||
|
||||
features = getHistogram(img_threshold);
|
||||
}
|
||||
|
||||
// compute color histom
|
||||
void getColorFeatures(const Mat& src, Mat& features) {
|
||||
Mat src_hsv;
|
||||
|
||||
//grayImage = histeq(grayImage);
|
||||
cvtColor(src, src_hsv, CV_BGR2HSV);
|
||||
int channels = src_hsv.channels();
|
||||
int nRows = src_hsv.rows;
|
||||
|
||||
// consider multi channel image
|
||||
int nCols = src_hsv.cols * channels;
|
||||
if (src_hsv.isContinuous()) {
|
||||
nCols *= nRows;
|
||||
nRows = 1;
|
||||
}
|
||||
|
||||
const int sz = 180;
|
||||
int h[sz] = { 0 };
|
||||
|
||||
uchar* p;
|
||||
for (int i = 0; i < nRows; ++i) {
|
||||
p = src_hsv.ptr<uchar>(i);
|
||||
for (int j = 0; j < nCols; j += 3) {
|
||||
int H = int(p[j]); // 0-180
|
||||
if (H > sz - 1) H = sz - 1;
|
||||
if (H < 0) H = 0;
|
||||
h[H]++;
|
||||
}
|
||||
}
|
||||
|
||||
Mat mhist = Mat::zeros(1, sz, CV_32F);
|
||||
for (int j = 0; j < sz; j++) {
|
||||
mhist.at<float>(j) = (float)h[j];
|
||||
}
|
||||
|
||||
// Normalize histogram
|
||||
double min, max;
|
||||
minMaxLoc(mhist, &min, &max);
|
||||
|
||||
if (max > 0)
|
||||
mhist.convertTo(mhist, -1, 1.0f / max, 0);
|
||||
|
||||
features = mhist;
|
||||
}
|
||||
|
||||
|
||||
void getHistomPlusColoFeatures(const Mat& image, Mat& features) {
|
||||
// TODO
|
||||
Mat feature1, feature2;
|
||||
getHistogramFeatures(image, feature1);
|
||||
getColorFeatures(image, feature2);
|
||||
hconcat(feature1.reshape(1, 1), feature2.reshape(1, 1), features);
|
||||
}
|
||||
|
||||
|
||||
void getSIFTFeatures(const Mat& image, Mat& features) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
//HOG Features
|
||||
void getHOGFeatures(const Mat& image, Mat& features) {
|
||||
//HOG descripter
|
||||
HOGDescriptor hog(cvSize(128, 64), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 3); //these parameters work well
|
||||
std::vector<float> descriptor;
|
||||
|
||||
// resize input image to (128,64) for compute
|
||||
Size dsize = Size(128,64);
|
||||
Mat trainImg = Mat(dsize, CV_32S);
|
||||
resize(image, trainImg, dsize);
|
||||
|
||||
// compute descripter
|
||||
hog.compute(trainImg, descriptor, Size(8, 8));
|
||||
|
||||
// copy the result
|
||||
Mat mat_featrue(descriptor);
|
||||
mat_featrue.copyTo(features);
|
||||
}
|
||||
|
||||
|
||||
void getHSVHistFeatures(const Mat& image, Mat& features) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
//! LBP feature
|
||||
void getLBPFeatures(const Mat& image, Mat& features) {
|
||||
|
||||
Mat grayImage;
|
||||
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||
|
||||
Mat lbpimage;
|
||||
lbpimage = libfacerec::olbp(grayImage);
|
||||
Mat lbp_hist = libfacerec::spatial_histogram(lbpimage, 32, 4, 4);
|
||||
|
||||
features = lbp_hist;
|
||||
}
|
||||
|
||||
Mat charFeatures(Mat in, int sizeData) {
|
||||
const int VERTICAL = 0;
|
||||
const int HORIZONTAL = 1;
|
||||
|
||||
// cut the cetner, will afect 5% perices.
|
||||
Rect _rect = GetCenterRect(in);
|
||||
Mat tmpIn = CutTheRect(in, _rect);
|
||||
//Mat tmpIn = in.clone();
|
||||
|
||||
// Low data feature
|
||||
Mat lowData;
|
||||
resize(tmpIn, lowData, Size(sizeData, sizeData));
|
||||
|
||||
// Histogram features
|
||||
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||
|
||||
// Last 10 is the number of moments components
|
||||
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
|
||||
|
||||
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||
// Asign values to
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < vhist.cols; i++) {
|
||||
out.at<float>(j) = vhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int i = 0; i < hhist.cols; i++) {
|
||||
out.at<float>(j) = hhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int x = 0; x < lowData.cols; x++) {
|
||||
for (int y = 0; y < lowData.rows; y++) {
|
||||
out.at<float>(j) += (float)lowData.at <unsigned char>(x, y);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << out << std::endl;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Mat charFeatures2(Mat in, int sizeData) {
|
||||
const int VERTICAL = 0;
|
||||
const int HORIZONTAL = 1;
|
||||
|
||||
// cut the cetner, will afect 5% perices.
|
||||
Rect _rect = GetCenterRect(in);
|
||||
Mat tmpIn = CutTheRect(in, _rect);
|
||||
//Mat tmpIn = in.clone();
|
||||
|
||||
// Low data feature
|
||||
Mat lowData;
|
||||
resize(tmpIn, lowData, Size(sizeData, sizeData));
|
||||
|
||||
// Histogram features
|
||||
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||
|
||||
// Last 10 is the number of moments components
|
||||
int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;
|
||||
|
||||
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < vhist.cols; i++) {
|
||||
out.at<float>(j) = vhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int i = 0; i < hhist.cols; i++) {
|
||||
out.at<float>(j) = hhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int x = 0; x < lowData.cols; x++) {
|
||||
for (int y = 0; y < lowData.rows; y++) {
|
||||
out.at<float>(j) += (float)lowData.at <unsigned char>(x, y);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cout << out << std::endl;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Mat charProjectFeatures(const Mat& in, int sizeData) {
|
||||
const int VERTICAL = 0;
|
||||
const int HORIZONTAL = 1;
|
||||
|
||||
SHOW_IMAGE(in, 0);
|
||||
// cut the cetner, will afect 5% perices.
|
||||
|
||||
Mat lowData;
|
||||
resize(in, lowData, Size(sizeData, sizeData));
|
||||
|
||||
SHOW_IMAGE(lowData, 0);
|
||||
// Histogram features
|
||||
Mat vhist = ProjectedHistogram(lowData, VERTICAL);
|
||||
Mat hhist = ProjectedHistogram(lowData, HORIZONTAL);
|
||||
|
||||
// Last 10 is the number of moments components
|
||||
int numCols = vhist.cols + hhist.cols;
|
||||
|
||||
Mat out = Mat::zeros(1, numCols, CV_32F);
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < vhist.cols; i++) {
|
||||
out.at<float>(j) = vhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
for (int i = 0; i < hhist.cols; i++) {
|
||||
out.at<float>(j) = hhist.at<float>(i);
|
||||
j++;
|
||||
}
|
||||
//std::cout << out << std::endl;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void getGrayCharFeatures(const Mat& grayChar, Mat& features) {
|
||||
// TODO: check channnels == 1
|
||||
SHOW_IMAGE(grayChar, 0);
|
||||
SHOW_IMAGE(255 - grayChar, 0);
|
||||
|
||||
// resize to uniform size, like 20x32
|
||||
bool useResize = false;
|
||||
bool useConvert = true;
|
||||
bool useMean = true;
|
||||
bool useLBP = false;
|
||||
|
||||
Mat char_mat;
|
||||
if (useResize) {
|
||||
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||
} else {
|
||||
char_mat = grayChar;
|
||||
}
|
||||
SHOW_IMAGE(char_mat, 0);
|
||||
|
||||
// convert to float
|
||||
Mat float_img;
|
||||
if (useConvert) {
|
||||
float scale = 1.f / 255;
|
||||
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||
} else {
|
||||
float_img = char_mat;
|
||||
}
|
||||
SHOW_IMAGE(float_img, 0);
|
||||
|
||||
// cut from mean, it can be optional
|
||||
|
||||
Mat mean_img;
|
||||
if (useMean) {
|
||||
float_img -= mean(float_img);
|
||||
mean_img = float_img;
|
||||
} else {
|
||||
mean_img = float_img;
|
||||
}
|
||||
SHOW_IMAGE(mean_img, 0);
|
||||
|
||||
// use lbp to get features, it can be changed to other
|
||||
Mat feautreImg;
|
||||
if (useLBP) {
|
||||
Mat lbpimage = libfacerec::olbp(char_mat);
|
||||
SHOW_IMAGE(lbpimage, 0);
|
||||
feautreImg = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||
} else {
|
||||
feautreImg = mean_img.reshape(1, 1);
|
||||
}
|
||||
|
||||
// return back
|
||||
features = feautreImg;
|
||||
}
|
||||
|
||||
|
||||
void getGrayPlusProject(const Mat& grayChar, Mat& features)
|
||||
{
|
||||
// TODO: check channnels == 1
|
||||
SHOW_IMAGE(grayChar, 0);
|
||||
SHOW_IMAGE(255 - grayChar, 0);
|
||||
|
||||
// resize to uniform size, like 20x32
|
||||
bool useResize = false;
|
||||
bool useConvert = true;
|
||||
bool useMean = true;
|
||||
bool useLBP = false;
|
||||
|
||||
Mat char_mat;
|
||||
if (useResize) {
|
||||
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||
}
|
||||
else {
|
||||
char_mat = grayChar;
|
||||
}
|
||||
SHOW_IMAGE(char_mat, 0);
|
||||
|
||||
// convert to float
|
||||
Mat float_img;
|
||||
if (useConvert) {
|
||||
float scale = 1.f / 255;
|
||||
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||
}
|
||||
else {
|
||||
float_img = char_mat;
|
||||
}
|
||||
SHOW_IMAGE(float_img, 0);
|
||||
|
||||
// cut from mean, it can be optional
|
||||
|
||||
Mat mean_img;
|
||||
if (useMean) {
|
||||
float_img -= mean(float_img);
|
||||
mean_img = float_img;
|
||||
}
|
||||
else {
|
||||
mean_img = float_img;
|
||||
}
|
||||
SHOW_IMAGE(mean_img, 0);
|
||||
|
||||
// use lbp to get features, it can be changed to other
|
||||
Mat feautreImg;
|
||||
if (useLBP) {
|
||||
Mat lbpimage = libfacerec::olbp(char_mat);
|
||||
SHOW_IMAGE(lbpimage, 0);
|
||||
feautreImg = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||
}
|
||||
else {
|
||||
feautreImg = mean_img.reshape(1, 1);
|
||||
}
|
||||
SHOW_IMAGE(grayChar, 0);
|
||||
Mat binaryChar;
|
||||
threshold(grayChar, binaryChar, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||
SHOW_IMAGE(binaryChar, 0);
|
||||
Mat projectFeature = charProjectFeatures(binaryChar, 32);
|
||||
|
||||
hconcat(feautreImg.reshape(1, 1), projectFeature.reshape(1, 1), features);
|
||||
}
|
||||
|
||||
|
||||
void getGrayPlusLBP(const Mat& grayChar, Mat& features)
|
||||
{
|
||||
// TODO: check channnels == 1
|
||||
SHOW_IMAGE(grayChar, 0);
|
||||
SHOW_IMAGE(255 - grayChar, 0);
|
||||
|
||||
// resize to uniform size, like 20x32
|
||||
bool useResize = false;
|
||||
bool useConvert = true;
|
||||
bool useMean = true;
|
||||
bool useLBP = true;
|
||||
|
||||
Mat char_mat;
|
||||
if (useResize) {
|
||||
char_mat.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||
resize(grayChar, char_mat, char_mat.size(), 0, 0, INTER_LINEAR);
|
||||
}
|
||||
else {
|
||||
char_mat = grayChar;
|
||||
}
|
||||
SHOW_IMAGE(char_mat, 0);
|
||||
|
||||
// convert to float
|
||||
Mat float_img;
|
||||
if (useConvert) {
|
||||
float scale = 1.f / 255;
|
||||
char_mat.convertTo(float_img, CV_32FC1, scale, 0);
|
||||
}
|
||||
else {
|
||||
float_img = char_mat;
|
||||
}
|
||||
SHOW_IMAGE(float_img, 0);
|
||||
|
||||
// cut from mean, it can be optional
|
||||
|
||||
Mat mean_img;
|
||||
if (useMean) {
|
||||
float_img -= mean(float_img);
|
||||
mean_img = float_img;
|
||||
}
|
||||
else {
|
||||
mean_img = float_img;
|
||||
}
|
||||
SHOW_IMAGE(mean_img, 0);
|
||||
|
||||
// use lbp to get features, it can be changed to other
|
||||
Mat originImage = mean_img.clone();
|
||||
Mat lbpimage = libfacerec::olbp(mean_img);
|
||||
SHOW_IMAGE(lbpimage, 0);
|
||||
lbpimage = libfacerec::spatial_histogram(lbpimage, kCharLBPPatterns, kCharLBPGridX, kCharLBPGridY);
|
||||
|
||||
// 32x20 + 16x16
|
||||
hconcat(mean_img.reshape(1, 1), lbpimage.reshape(1, 1), features);
|
||||
}
|
||||
|
||||
void getLBPplusHistFeatures(const Mat& image, Mat& features) {
|
||||
Mat grayImage;
|
||||
cvtColor(image, grayImage, CV_RGB2GRAY);
|
||||
|
||||
Mat lbpimage;
|
||||
lbpimage = libfacerec::olbp(grayImage);
|
||||
Mat lbp_hist = libfacerec::spatial_histogram(lbpimage, 64, 8, 4);
|
||||
//features = lbp_hist.reshape(1, 1);
|
||||
|
||||
Mat greyImage;
|
||||
cvtColor(image, greyImage, CV_RGB2GRAY);
|
||||
|
||||
//grayImage = histeq(grayImage);
|
||||
Mat img_threshold;
|
||||
threshold(greyImage, img_threshold, 0, 255,
|
||||
CV_THRESH_OTSU + CV_THRESH_BINARY);
|
||||
Mat histomFeatures = getHistogram(img_threshold);
|
||||
|
||||
hconcat(lbp_hist.reshape(1, 1), histomFeatures.reshape(1, 1), features);
|
||||
//std::cout << features << std::endl;
|
||||
//features = histomFeatures;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
#include "easypr/core/params.h"
|
||||
|
||||
namespace easypr {
|
||||
CParams* CParams::instance_ = nullptr;
|
||||
|
||||
CParams* CParams::instance() {
|
||||
if (!instance_) {
|
||||
instance_ = new CParams;
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
}/*! \namespace easypr*/
|
@ -0,0 +1,77 @@
|
||||
#include "easypr/core/plate_detect.h"
|
||||
#include "easypr/util/util.h"
|
||||
#include "easypr/core/core_func.h"
|
||||
#include "easypr/config.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
CPlateDetect::CPlateDetect() {
|
||||
m_plateLocate = new CPlateLocate();
|
||||
m_maxPlates = 3;
|
||||
m_type = 0;
|
||||
m_showDetect = false;
|
||||
}
|
||||
|
||||
CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); }
|
||||
|
||||
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int type,
|
||||
bool showDetectArea, int img_index) {
|
||||
std::vector<CPlate> sobel_Plates;
|
||||
sobel_Plates.reserve(16);
|
||||
std::vector<CPlate> color_Plates;
|
||||
color_Plates.reserve(16);
|
||||
std::vector<CPlate> mser_Plates;
|
||||
mser_Plates.reserve(16);
|
||||
std::vector<CPlate> all_result_Plates;
|
||||
all_result_Plates.reserve(64);
|
||||
#pragma omp parallel sections
|
||||
{
|
||||
#pragma omp section
|
||||
{
|
||||
if (!type || type & PR_DETECT_SOBEL) {
|
||||
m_plateLocate->plateSobelLocate(src, sobel_Plates, img_index);
|
||||
}
|
||||
}
|
||||
#pragma omp section
|
||||
{
|
||||
if (!type || type & PR_DETECT_COLOR) {
|
||||
m_plateLocate->plateColorLocate(src, color_Plates, img_index);
|
||||
}
|
||||
}
|
||||
#pragma omp section
|
||||
{
|
||||
if (!type || type & PR_DETECT_CMSER) {
|
||||
m_plateLocate->plateMserLocate(src, mser_Plates, img_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto plate : sobel_Plates) {
|
||||
plate.setPlateLocateType(SOBEL);
|
||||
all_result_Plates.push_back(plate);
|
||||
}
|
||||
for (auto plate : color_Plates) {
|
||||
plate.setPlateLocateType(COLOR);
|
||||
all_result_Plates.push_back(plate);
|
||||
}
|
||||
for (auto plate : mser_Plates) {
|
||||
plate.setPlateLocateType(CMSER);
|
||||
all_result_Plates.push_back(plate);
|
||||
}
|
||||
// use nms to judge plate
|
||||
PlateJudge::instance()->plateJudgeUsingNMS(all_result_Plates, resultVec, m_maxPlates);
|
||||
|
||||
if (0)
|
||||
showDectectResults(src, resultVec, m_maxPlates);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int img_index) {
|
||||
int result = plateDetect(src, resultVec, m_type, false, img_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CPlateDetect::LoadSVM(std::string path) {
|
||||
PlateJudge::instance()->LoadModel(path);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
#include "easypr/core/plate_recognize.h"
|
||||
#include "easypr/config.h"
|
||||
#include "thirdparty/textDetect/erfilter.hpp"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
CPlateRecognize::CPlateRecognize() {
|
||||
m_showResult = false;
|
||||
}
|
||||
|
||||
|
||||
// main method, plate recognize, contain two parts
|
||||
// 1. plate detect
|
||||
// 2. chars recognize
|
||||
int CPlateRecognize::plateRecognize(const Mat& src, std::vector<CPlate> &plateVecOut, int img_index) {
|
||||
// resize to uniform sizes
|
||||
float scale = 1.f;
|
||||
Mat img = uniformResize(src, scale);
|
||||
|
||||
// 1. plate detect
|
||||
std::vector<CPlate> plateVec;
|
||||
int resultPD = plateDetect(img, plateVec, img_index);
|
||||
if (resultPD == 0) {
|
||||
size_t num = plateVec.size();
|
||||
for (size_t j = 0; j < num; j++) {
|
||||
CPlate& item = plateVec.at(j);
|
||||
Mat plateMat = item.getPlateMat();
|
||||
SHOW_IMAGE(plateMat, 0);
|
||||
|
||||
// scale the rect to src;
|
||||
item.setPlateScale(scale);
|
||||
RotatedRect rect = item.getPlatePos();
|
||||
item.setPlatePos(scaleBackRRect(rect, 1.f / scale));
|
||||
|
||||
// get plate color
|
||||
Color color = item.getPlateColor();
|
||||
if (color == UNKNOWN) {
|
||||
color = getPlateType(plateMat, true);
|
||||
item.setPlateColor(color);
|
||||
}
|
||||
std::string plateColor = getPlateColor(color);
|
||||
if (0) {
|
||||
std::cout << "plateColor:" << plateColor << std::endl;
|
||||
}
|
||||
|
||||
// 2. chars recognize
|
||||
std::string plateIdentify = "";
|
||||
int resultCR = charsRecognise(item, plateIdentify);
|
||||
if (resultCR == 0) {
|
||||
std::string license = plateColor + ":" + plateIdentify;
|
||||
item.setPlateStr(license);
|
||||
plateVecOut.push_back(item);
|
||||
if (0) std::cout << "resultCR:" << resultCR << std::endl;
|
||||
}
|
||||
else {
|
||||
std::string license = plateColor;
|
||||
item.setPlateStr(license);
|
||||
plateVecOut.push_back(item);
|
||||
if (0) std::cout << "resultCR:" << resultCR << std::endl;
|
||||
}
|
||||
}
|
||||
if (getResultShow()) {
|
||||
// param type: 0 detect, 1 recognize;
|
||||
int showType = 1;
|
||||
if (0 == showType)
|
||||
showDectectResults(img, plateVec, num);
|
||||
else
|
||||
showDectectResults(img, plateVecOut, num);
|
||||
}
|
||||
}
|
||||
return resultPD;
|
||||
}
|
||||
|
||||
void CPlateRecognize::LoadSVM(std::string path) {
|
||||
PlateJudge::instance()->LoadModel(path);
|
||||
}
|
||||
|
||||
void CPlateRecognize::LoadANN(std::string path) {
|
||||
CharsIdentify::instance()->LoadModel(path);
|
||||
}
|
||||
|
||||
void CPlateRecognize::LoadChineseANN(std::string path) {
|
||||
CharsIdentify::instance()->LoadChineseModel(path);
|
||||
}
|
||||
|
||||
void CPlateRecognize::LoadGrayChANN(std::string path) {
|
||||
CharsIdentify::instance()->LoadGrayChANN(path);
|
||||
}
|
||||
|
||||
void CPlateRecognize::LoadChineseMapping(std::string path) {
|
||||
CharsIdentify::instance()->LoadChineseMapping(path);
|
||||
}
|
||||
|
||||
// deprected
|
||||
int CPlateRecognize::plateRecognize(const Mat& src, std::vector<std::string> &licenseVec) {
|
||||
vector<CPlate> plates;
|
||||
int resultPR = plateRecognize(src, plates, 0);
|
||||
|
||||
for (auto plate : plates) {
|
||||
licenseVec.push_back(plate.getPlateStr());
|
||||
}
|
||||
return resultPR;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 43 KiB |
@ -0,0 +1,277 @@
|
||||
#include <numeric>
|
||||
#include <ctime>
|
||||
|
||||
#include "easypr/train/annCh_train.h"
|
||||
#include "easypr/config.h"
|
||||
#include "easypr/core/chars_identify.h"
|
||||
#include "easypr/core/feature.h"
|
||||
#include "easypr/core/core_func.h"
|
||||
#include "easypr/util/util.h"
|
||||
#include "easypr/train/create_data.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
AnnChTrain::AnnChTrain(const char* chars_folder, const char* xml)
|
||||
: chars_folder_(chars_folder), ann_xml_(xml)
|
||||
{
|
||||
ann_ = cv::ml::ANN_MLP::create();
|
||||
type = 1;
|
||||
kv_ = std::shared_ptr<Kv>(new Kv);
|
||||
kv_->load("resources/text/province_mapping");
|
||||
extractFeature = getGrayPlusProject;
|
||||
}
|
||||
|
||||
void AnnChTrain::train()
|
||||
{
|
||||
int classNumber = 0;
|
||||
int input_number = 0;
|
||||
int hidden_number = 0;
|
||||
int output_number = 0;
|
||||
|
||||
bool useLBP = false;
|
||||
if (useLBP)
|
||||
input_number = kCharLBPPatterns * kCharLBPGridX * kCharLBPGridY;
|
||||
else
|
||||
input_number = kGrayCharHeight * kGrayCharWidth;
|
||||
|
||||
input_number += 64;
|
||||
|
||||
classNumber = kChineseNumber;
|
||||
hidden_number = kCharHiddenNeurans;
|
||||
output_number = classNumber;
|
||||
cv::Mat layers;
|
||||
|
||||
int first_hidden_neurons = 48;
|
||||
int second_hidden_neurons = 32;
|
||||
|
||||
int N = input_number;
|
||||
int m = output_number;
|
||||
//int first_hidden_neurons = int(std::sqrt((m + 2) * N) + 2 * std::sqrt(N / (m + 2)));
|
||||
//int second_hidden_neurons = int(m * std::sqrt(N / (m + 2)));
|
||||
|
||||
bool useTLFN = false;
|
||||
if (!useTLFN) {
|
||||
layers.create(1, 3, CV_32SC1);
|
||||
layers.at<int>(0) = input_number;
|
||||
layers.at<int>(1) = hidden_number;
|
||||
layers.at<int>(2) = output_number;
|
||||
}
|
||||
else {
|
||||
fprintf(stdout, ">> Use two-layers neural networks,\n");
|
||||
fprintf(stdout, ">> First_hidden_neurons: %d \n", first_hidden_neurons);
|
||||
fprintf(stdout, ">> Second_hidden_neurons: %d \n", second_hidden_neurons);
|
||||
|
||||
layers.create(1, 4, CV_32SC1);
|
||||
layers.at<int>(0) = input_number;
|
||||
layers.at<int>(1) = first_hidden_neurons;
|
||||
layers.at<int>(2) = second_hidden_neurons;
|
||||
layers.at<int>(3) = output_number;
|
||||
}
|
||||
|
||||
ann_->setLayerSizes(layers);
|
||||
ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1);
|
||||
ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP);
|
||||
ann_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 30000, 0.0001));
|
||||
ann_->setBackpropWeightScale(0.1);
|
||||
ann_->setBackpropMomentumScale(0.1);
|
||||
|
||||
auto files = Utils::getFiles(chars_folder_);
|
||||
if (files.size() == 0) {
|
||||
fprintf(stdout, "No file found in the train folder!\n");
|
||||
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
|
||||
fprintf(stdout, "Copy train data folder(like \"annCh\") under \"tmp\". \n");
|
||||
return;
|
||||
}
|
||||
|
||||
// using raw data or raw + synthic data.
|
||||
trainVal(m_number_for_count);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> AnnChTrain::identifyGrayChinese(cv::Mat input) {
|
||||
Mat feature;
|
||||
extractFeature(input, feature);
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
|
||||
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||
ann_->predict(feature, output);
|
||||
|
||||
for (int j = 0; j < kChineseNumber; j++) {
|
||||
float val = output.at<float>(j);
|
||||
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
result = j;
|
||||
}
|
||||
}
|
||||
|
||||
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||
const char* key = kChars[index];
|
||||
std::string s = key;
|
||||
std::string province = kv_->get(s);
|
||||
|
||||
return std::make_pair(s, province);
|
||||
}
|
||||
|
||||
void AnnChTrain::test() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
void AnnChTrain::trainVal(size_t number_for_count) {
|
||||
assert(chars_folder_);
|
||||
cv::Mat train_samples;
|
||||
std::vector<cv::Mat> train_images, val_images;
|
||||
std::vector<int> train_label, val_labels;
|
||||
float percentage = 0.7f;
|
||||
int classNumber = kChineseNumber;
|
||||
|
||||
for (int i = 0; i < classNumber; ++i) {
|
||||
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
|
||||
char sub_folder[512] = { 0 };
|
||||
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
|
||||
std::string test_char(char_key);
|
||||
// if (test_char != "zh_yun") continue;
|
||||
|
||||
fprintf(stdout, ">> Testing characters %s in %s \n", char_key, sub_folder);
|
||||
auto chars_files = utils::getFiles(sub_folder);
|
||||
size_t char_size = chars_files.size();
|
||||
fprintf(stdout, ">> Characters count: %d \n", (int)char_size);
|
||||
|
||||
std::vector<cv::Mat> matVec;
|
||||
matVec.reserve(number_for_count);
|
||||
for (auto file : chars_files) {
|
||||
std::cout << file << std::endl;
|
||||
auto img = cv::imread(file, IMREAD_GRAYSCALE); // a grayscale image
|
||||
Mat img_resize;
|
||||
img_resize.create(kGrayCharHeight, kGrayCharWidth, CV_8UC1);
|
||||
resize(img, img_resize, img_resize.size(), 0, 0, INTER_LINEAR);
|
||||
matVec.push_back(img_resize);
|
||||
}
|
||||
// genrate the synthetic images
|
||||
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;
|
||||
auto img = matVec.at(ran_num);
|
||||
SHOW_IMAGE(img, 0);
|
||||
auto simg = generateSyntheticImage(img);
|
||||
SHOW_IMAGE(simg, 0);
|
||||
matVec.push_back(simg);
|
||||
}
|
||||
fprintf(stdout, ">> Characters count: %d \n", (int)matVec.size());
|
||||
|
||||
// random sort the mat;
|
||||
srand(unsigned(time(NULL)));
|
||||
random_shuffle(matVec.begin(), matVec.end());
|
||||
|
||||
int mat_size = (int)matVec.size();
|
||||
int split_index = int((float)mat_size * percentage);
|
||||
for (int j = mat_size - 1; j >= 0; j--) {
|
||||
Mat img = matVec.at(j);
|
||||
if (1) {
|
||||
Mat feature;
|
||||
extractFeature(img, feature);
|
||||
if (j <= split_index) {
|
||||
train_samples.push_back(feature);
|
||||
train_images.push_back(img);
|
||||
train_label.push_back(i);
|
||||
}
|
||||
else {
|
||||
val_images.push_back(img);
|
||||
val_labels.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// generate train data
|
||||
train_samples.convertTo(train_samples, CV_32F);
|
||||
cv::Mat train_classes = cv::Mat::zeros((int)train_label.size(), classNumber, CV_32F);
|
||||
for (int i = 0; i < train_classes.rows; ++i)
|
||||
train_classes.at<float>(i, train_label[i]) = 1.f;
|
||||
auto train_data = cv::ml::TrainData::create(train_samples, cv::ml::SampleTypes::ROW_SAMPLE, train_classes);
|
||||
|
||||
// train the data, calculate the cost time
|
||||
std::cout << "Training ANN chinese model, please wait..." << std::endl;
|
||||
long start = utils::getTimestamp();
|
||||
ann_->train(train_data);
|
||||
long end = utils::getTimestamp();
|
||||
ann_->save(ann_xml_);
|
||||
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;
|
||||
|
||||
// test the accuracy_rate in train
|
||||
if (1) {
|
||||
int corrects_all = 0, sum_all = train_images.size();
|
||||
std::cout << "train_images size: " << sum_all << std::endl;
|
||||
for (size_t i = 0; i < train_images.size(); ++i) {
|
||||
cv::Mat img = train_images.at(i);
|
||||
int label = train_label.at(i);
|
||||
auto char_key = kChars[label + kCharsTotalNumber - classNumber];
|
||||
std::pair<std::string, std::string> ch = identifyGrayChinese(img);
|
||||
if (ch.first == char_key)
|
||||
corrects_all++;
|
||||
}
|
||||
float accuracy_rate = (float)corrects_all / (float)sum_all;
|
||||
std::cout << "Train error_rate: " << (1.f - accuracy_rate) * 100.f << "% "<< std::endl;
|
||||
}
|
||||
|
||||
// test the accuracy_rate in val
|
||||
if (1) {
|
||||
int corrects_all = 0, sum_all = val_images.size();
|
||||
std::cout << "val_images: " << sum_all << std::endl;
|
||||
for (size_t i = 0; i < val_images.size(); ++i) {
|
||||
cv::Mat img = val_images.at(i);
|
||||
int label = val_labels.at(i);
|
||||
auto char_key = kChars[label + kCharsTotalNumber - classNumber];
|
||||
std::pair<std::string, std::string> ch = identifyGrayChinese(img);
|
||||
if (ch.first == char_key)
|
||||
corrects_all++;
|
||||
}
|
||||
float accuracy_rate = (float)corrects_all / (float)sum_all;
|
||||
std::cout << "Test error_rate: " << (1.f - accuracy_rate) * 100.f << "% "<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> AnnChTrain::tdata() {
|
||||
assert(chars_folder_);
|
||||
|
||||
cv::Mat samples;
|
||||
std::vector<int> labels;
|
||||
|
||||
std::cout << "Collecting chars in " << chars_folder_ << std::endl;
|
||||
|
||||
int classNumber = 0;
|
||||
if (type == 0) classNumber = kCharsTotalNumber;
|
||||
if (type == 1) classNumber = kChineseNumber;
|
||||
|
||||
for (int i = 0; i < classNumber; ++i) {
|
||||
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
|
||||
char sub_folder[512] = {0};
|
||||
|
||||
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
|
||||
std::cout << " >> Featuring characters " << char_key << " in "
|
||||
<< sub_folder << std::endl;
|
||||
|
||||
auto chars_files = utils::getFiles(sub_folder);
|
||||
for (auto file : chars_files) {
|
||||
auto img = cv::imread(file, 0); // a grayscale image
|
||||
auto fps = charFeatures2(img, kPredictSize);
|
||||
|
||||
samples.push_back(fps);
|
||||
labels.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat samples_;
|
||||
samples.convertTo(samples_, CV_32F);
|
||||
cv::Mat train_classes =
|
||||
cv::Mat::zeros((int)labels.size(), classNumber, CV_32F);
|
||||
|
||||
for (int i = 0; i < train_classes.rows; ++i) {
|
||||
train_classes.at<float>(i, labels[i]) = 1.f;
|
||||
}
|
||||
|
||||
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
|
||||
train_classes);
|
||||
}
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
#include <numeric>
|
||||
#include <ctime>
|
||||
|
||||
#include "easypr/train/ann_train.h"
|
||||
#include "easypr/config.h"
|
||||
#include "easypr/core/chars_identify.h"
|
||||
#include "easypr/core/feature.h"
|
||||
#include "easypr/core/core_func.h"
|
||||
#include "easypr/train/create_data.h"
|
||||
#include "easypr/util/util.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
AnnTrain::AnnTrain(const char* chars_folder, const char* xml)
|
||||
: chars_folder_(chars_folder), ann_xml_(xml) {
|
||||
ann_ = cv::ml::ANN_MLP::create();
|
||||
// type=0, all characters
|
||||
// type=1, only chinese
|
||||
type = 0;
|
||||
kv_ = std::shared_ptr<Kv>(new Kv);
|
||||
kv_->load("resources/text/province_mapping");
|
||||
}
|
||||
|
||||
void AnnTrain::train() {
|
||||
|
||||
int classNumber = 0;
|
||||
|
||||
cv::Mat layers;
|
||||
|
||||
int input_number = 0;
|
||||
int hidden_number = 0;
|
||||
int output_number = 0;
|
||||
|
||||
if (type == 0) {
|
||||
classNumber = kCharsTotalNumber;
|
||||
|
||||
input_number = kAnnInput;
|
||||
hidden_number = kNeurons;
|
||||
output_number = classNumber;
|
||||
}
|
||||
else if (type == 1) {
|
||||
classNumber = kChineseNumber;
|
||||
|
||||
input_number = kAnnInput;
|
||||
hidden_number = kNeurons;
|
||||
output_number = classNumber;
|
||||
}
|
||||
|
||||
int N = input_number;
|
||||
int m = output_number;
|
||||
int first_hidden_neurons = int(std::sqrt((m + 2) * N) + 2 * std::sqrt(N / (m + 2)));
|
||||
int second_hidden_neurons = int(m * std::sqrt(N / (m + 2)));
|
||||
|
||||
bool useTLFN = false;
|
||||
if (!useTLFN) {
|
||||
layers.create(1, 3, CV_32SC1);
|
||||
layers.at<int>(0) = input_number;
|
||||
layers.at<int>(1) = hidden_number;
|
||||
layers.at<int>(2) = output_number;
|
||||
}
|
||||
else {
|
||||
// Two-layers neural networks is hard to train, So do not try it
|
||||
fprintf(stdout, ">> Use two-layers neural networks,\n");
|
||||
fprintf(stdout, ">> First_hidden_neurons: %d \n", first_hidden_neurons);
|
||||
fprintf(stdout, ">> Second_hidden_neurons: %d \n", second_hidden_neurons);
|
||||
|
||||
layers.create(1, 4, CV_32SC1);
|
||||
layers.at<int>(0) = input_number;
|
||||
layers.at<int>(1) = first_hidden_neurons;
|
||||
layers.at<int>(2) = second_hidden_neurons;
|
||||
layers.at<int>(3) = output_number;
|
||||
}
|
||||
|
||||
ann_->setLayerSizes(layers);
|
||||
ann_->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM, 1, 1);
|
||||
ann_->setTrainMethod(cv::ml::ANN_MLP::TrainingMethods::BACKPROP);
|
||||
ann_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 30000, 0.0001));
|
||||
ann_->setBackpropWeightScale(0.1);
|
||||
ann_->setBackpropMomentumScale(0.1);
|
||||
|
||||
auto files = Utils::getFiles(chars_folder_);
|
||||
if (files.size() == 0) {
|
||||
fprintf(stdout, "No file found in the train folder!\n");
|
||||
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
|
||||
fprintf(stdout, "Copy train data folder(like \"ann\") under \"tmp\". \n");
|
||||
return;
|
||||
}
|
||||
|
||||
//using raw data or raw + synthic data.
|
||||
auto traindata = sdata(350);
|
||||
|
||||
std::cout << "Training ANN model, please wait..." << std::endl;
|
||||
long start = utils::getTimestamp();
|
||||
ann_->train(traindata);
|
||||
long end = utils::getTimestamp();
|
||||
ann_->save(ann_xml_);
|
||||
|
||||
test();
|
||||
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;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> AnnTrain::identifyChinese(cv::Mat input) {
|
||||
cv::Mat feature = charFeatures2(input, kPredictSize);
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
|
||||
cv::Mat output(1, kChineseNumber, CV_32FC1);
|
||||
ann_->predict(feature, output);
|
||||
|
||||
for (int j = 0; j < kChineseNumber; j++) {
|
||||
float val = output.at<float>(j);
|
||||
// std::cout << "j:" << j << "val:" << val << std::endl;
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
result = j;
|
||||
}
|
||||
}
|
||||
|
||||
auto index = result + kCharsTotalNumber - kChineseNumber;
|
||||
const char* key = kChars[index];
|
||||
std::string s = key;
|
||||
std::string province = kv_->get(s);
|
||||
|
||||
return std::make_pair(s, province);
|
||||
}
|
||||
|
||||
|
||||
std::pair<std::string, std::string> AnnTrain::identify(cv::Mat input) {
|
||||
cv::Mat feature = charFeatures2(input, kPredictSize);
|
||||
float maxVal = -2;
|
||||
int result = 0;
|
||||
|
||||
//std::cout << feature << std::endl;
|
||||
cv::Mat output(1, kCharsTotalNumber, CV_32FC1);
|
||||
ann_->predict(feature, output);
|
||||
//std::cout << output << std::endl;
|
||||
for (int j = 0; j < kCharsTotalNumber; j++) {
|
||||
float val = output.at<float>(j);
|
||||
//std::cout << "j:" << j << "val:" << val << std::endl;
|
||||
if (val > maxVal) {
|
||||
maxVal = val;
|
||||
result = j;
|
||||
}
|
||||
}
|
||||
|
||||
auto index = result;
|
||||
if (index < kCharactersNumber) {
|
||||
return std::make_pair(kChars[index], kChars[index]);
|
||||
}
|
||||
else {
|
||||
const char* key = kChars[index];
|
||||
std::string s = key;
|
||||
std::string province = kv_->get(s);
|
||||
return std::make_pair(s, province);
|
||||
}
|
||||
}
|
||||
|
||||
void AnnTrain::test() {
|
||||
assert(chars_folder_);
|
||||
|
||||
int classNumber = 0;
|
||||
if (type == 0) classNumber = kCharsTotalNumber;
|
||||
if (type == 1) classNumber = kChineseNumber;
|
||||
|
||||
int corrects_all = 0, sum_all = 0;
|
||||
std::vector<float> rate_list;
|
||||
for (int i = 0; i < classNumber; ++i) {
|
||||
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
|
||||
char sub_folder[512] = { 0 };
|
||||
|
||||
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
|
||||
fprintf(stdout, ">> Testing characters %s in %s \n", char_key, sub_folder);
|
||||
|
||||
auto chars_files = utils::getFiles(sub_folder);
|
||||
int corrects = 0, sum = 0;
|
||||
std::vector<std::pair<std::string, std::string>> error_files;
|
||||
|
||||
for (auto file : chars_files) {
|
||||
auto img = cv::imread(file, 0); // a grayscale image
|
||||
if (!img.data) {
|
||||
//cout << "Null pointer!" << endl;
|
||||
continue;
|
||||
}
|
||||
std::pair<std::string, std::string> ch;
|
||||
|
||||
if (type == 0) ch = identify(img);
|
||||
if (type == 1) ch = identifyChinese(img);
|
||||
|
||||
if (ch.first == char_key) {
|
||||
++corrects;
|
||||
++corrects_all;
|
||||
} else {
|
||||
error_files.push_back(std::make_pair(utils::getFileName(file), ch.second));
|
||||
}
|
||||
++sum;
|
||||
++sum_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);
|
||||
|
||||
std::string error_string;
|
||||
auto end = error_files.end();
|
||||
if (error_files.size() >= 10) {
|
||||
end -= static_cast<size_t>(error_files.size() * (1 - 0.1));
|
||||
}
|
||||
for (auto k = error_files.begin(); k != end; ++k) {
|
||||
auto kv = *k;
|
||||
error_string.append(" ").append(kv.first).append(": ").append(
|
||||
kv.second);
|
||||
if (k != end - 1) {
|
||||
error_string.append(",\n");
|
||||
} else {
|
||||
error_string.append("\n ...");
|
||||
}
|
||||
}
|
||||
fprintf(stdout, ">> [\n%s\n ]\n", error_string.c_str());
|
||||
}
|
||||
fprintf(stdout, ">> [sum_all: %d, correct_all: %d, rate: %.4f]\n", sum_all, corrects_all,
|
||||
(float)corrects_all / (sum_all == 0 ? 1 : sum_all));
|
||||
|
||||
double rate_sum = std::accumulate(rate_list.begin(), rate_list.end(), 0.0);
|
||||
double rate_mean = rate_sum / (rate_list.size() == 0 ? 1 : rate_list.size());
|
||||
|
||||
fprintf(stdout, ">> [classNumber: %d, avg_rate: %.4f]\n", classNumber, rate_mean);
|
||||
}
|
||||
|
||||
cv::Mat getSyntheticImage(const Mat& image) {
|
||||
int rand_type = rand();
|
||||
Mat result = image.clone();
|
||||
|
||||
if (rand_type % 2 == 0) {
|
||||
int ran_x = rand() % 5 - 2;
|
||||
int ran_y = rand() % 5 - 2;
|
||||
|
||||
result = translateImg(result, ran_x, ran_y);
|
||||
}
|
||||
else if (rand_type % 2 != 0) {
|
||||
float angle = float(rand() % 15 - 7);
|
||||
|
||||
result = rotateImg(result, angle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> AnnTrain::sdata(size_t number_for_count) {
|
||||
assert(chars_folder_);
|
||||
|
||||
cv::Mat samples;
|
||||
std::vector<int> labels;
|
||||
|
||||
int classNumber = 0;
|
||||
if (type == 0) classNumber = kCharsTotalNumber;
|
||||
if (type == 1) classNumber = kChineseNumber;
|
||||
|
||||
srand((unsigned)time(0));
|
||||
for (int i = 0; i < classNumber; ++i) {
|
||||
|
||||
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
|
||||
char sub_folder[512] = { 0 };
|
||||
|
||||
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
|
||||
fprintf(stdout, ">> Testing characters %s in %s \n", char_key, sub_folder);
|
||||
|
||||
auto chars_files = utils::getFiles(sub_folder);
|
||||
size_t char_size = chars_files.size();
|
||||
fprintf(stdout, ">> Characters count: %d \n", int(char_size));
|
||||
|
||||
std::vector<cv::Mat> matVec;
|
||||
matVec.reserve(number_for_count);
|
||||
for (auto file : chars_files) {
|
||||
auto img = cv::imread(file, 0); // a grayscale image
|
||||
matVec.push_back(img);
|
||||
}
|
||||
|
||||
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;
|
||||
auto img = matVec.at(ran_num);
|
||||
auto simg = getSyntheticImage(img);
|
||||
matVec.push_back(simg);
|
||||
if (1) {
|
||||
std::stringstream ss(std::stringstream::in | std::stringstream::out);
|
||||
ss << sub_folder << "/" << i << "_" << t << "_" << ran_num << ".jpg";
|
||||
imwrite(ss.str(), simg);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, ">> Characters count: %d \n", (int)matVec.size());
|
||||
|
||||
for (auto img : matVec) {
|
||||
auto fps = charFeatures2(img, kPredictSize);
|
||||
|
||||
samples.push_back(fps);
|
||||
labels.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat samples_;
|
||||
samples.convertTo(samples_, CV_32F);
|
||||
cv::Mat train_classes =
|
||||
cv::Mat::zeros((int)labels.size(), classNumber, CV_32F);
|
||||
|
||||
for (int i = 0; i < train_classes.rows; ++i) {
|
||||
train_classes.at<float>(i, labels[i]) = 1.f;
|
||||
}
|
||||
|
||||
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
|
||||
train_classes);
|
||||
}
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> AnnTrain::tdata() {
|
||||
assert(chars_folder_);
|
||||
|
||||
cv::Mat samples;
|
||||
std::vector<int> labels;
|
||||
|
||||
std::cout << "Collecting chars in " << chars_folder_ << std::endl;
|
||||
|
||||
int classNumber = 0;
|
||||
if (type == 0) classNumber = kCharsTotalNumber;
|
||||
if (type == 1) classNumber = kChineseNumber;
|
||||
|
||||
for (int i = 0; i < classNumber; ++i) {
|
||||
auto char_key = kChars[i + kCharsTotalNumber - classNumber];
|
||||
char sub_folder[512] = {0};
|
||||
|
||||
sprintf(sub_folder, "%s/%s", chars_folder_, char_key);
|
||||
std::cout << " >> Featuring characters " << char_key << " in "
|
||||
<< sub_folder << std::endl;
|
||||
|
||||
auto chars_files = utils::getFiles(sub_folder);
|
||||
for (auto file : chars_files) {
|
||||
auto img = cv::imread(file, 0); // a grayscale image
|
||||
auto fps = charFeatures2(img, kPredictSize);
|
||||
|
||||
samples.push_back(fps);
|
||||
labels.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat samples_;
|
||||
samples.convertTo(samples_, CV_32F);
|
||||
cv::Mat train_classes =
|
||||
cv::Mat::zeros((int)labels.size(), classNumber, CV_32F);
|
||||
|
||||
for (int i = 0; i < train_classes.rows; ++i) {
|
||||
train_classes.at<float>(i, labels[i]) = 1.f;
|
||||
}
|
||||
|
||||
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE,
|
||||
train_classes);
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
#include "easypr/train/create_data.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
int getBoderColor(Mat img) {
|
||||
assert(img.channels() == 1);
|
||||
assert(img.type() == CV_8UC1);
|
||||
int w = img.cols;
|
||||
int h = img.rows;
|
||||
|
||||
float sum = 0;
|
||||
for (int i = 0; i < h; ++i) {
|
||||
sum += img.at<unsigned char>(i, 0);
|
||||
sum += img.at<unsigned char>(i, w-1);
|
||||
}
|
||||
for (int j = 0; j < w; ++j) {
|
||||
sum += img.at<unsigned char>(0, j);
|
||||
sum += img.at<unsigned char>(h-1, j);
|
||||
}
|
||||
|
||||
float avg = sum / float(w + w + h + h);
|
||||
return int(avg);
|
||||
}
|
||||
|
||||
// shift an image
|
||||
Mat translateImg(Mat img, int offsetx, int offsety, int bk){
|
||||
Mat dst;
|
||||
//cout << test << endl;
|
||||
Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, offsetx, 0, 1, offsety);
|
||||
//cout << trans_mat << endl;
|
||||
warpAffine(img, dst, trans_mat, img.size(), 1, 0, Scalar(bk));
|
||||
return dst;
|
||||
}
|
||||
|
||||
// rotate an image
|
||||
Mat rotateImg(Mat source, float angle, int bk){
|
||||
Point2f src_center(source.cols / 2.0F, source.rows / 2.0F);
|
||||
Mat rot_mat = getRotationMatrix2D(src_center, angle, 1.0);
|
||||
Mat dst;
|
||||
warpAffine(source, dst, rot_mat, source.size(), 1, 0, Scalar(bk));
|
||||
return dst;
|
||||
}
|
||||
|
||||
// crop the image
|
||||
Mat cropImg(Mat src, int x, int y, int shift, int bk){
|
||||
int width = src.cols;
|
||||
int height = src.rows;
|
||||
|
||||
int crop_width = width - shift;
|
||||
int crop_height = height - shift;
|
||||
|
||||
int x_shift = shift;
|
||||
int y_shift = shift;
|
||||
|
||||
x = x < x_shift ? x : x_shift;
|
||||
y = y < y_shift ? y : y_shift;
|
||||
|
||||
Rect rect = Rect(x, y, crop_width, crop_height);
|
||||
|
||||
Mat dst = src(rect);
|
||||
resize(dst, dst, Size(width, height));
|
||||
return dst;
|
||||
}
|
||||
|
||||
Mat generateSyntheticImage(const Mat& image, int use_swap) {
|
||||
int rd = rand();
|
||||
int bkColor = getBoderColor(image);
|
||||
Mat result = image.clone();
|
||||
if (0 && (rd >> 6 & 1)) {
|
||||
int shift = 2;
|
||||
int ran_x = rand() % shift;
|
||||
int ran_y = rand() % shift;
|
||||
result = cropImg(result, ran_x, ran_y, shift, bkColor);
|
||||
}
|
||||
if (0 && (rd >> 4 & 1)) {
|
||||
int ran_x = rand() % 2 - 1;
|
||||
int ran_y = rand() % 2 - 1;
|
||||
result = translateImg(result, ran_x, ran_y, bkColor);
|
||||
}
|
||||
if (1 && (rd >> 2 & 1)) {
|
||||
float angle = float(rand() % 100) * 0.1f - 5.f;
|
||||
result = rotateImg(result, angle, bkColor);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -0,0 +1,196 @@
|
||||
#include "easypr/train/svm_train.h"
|
||||
#include "easypr/util/util.h"
|
||||
#include "easypr/config.h"
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#include <ctime>
|
||||
#endif
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::ml;
|
||||
|
||||
namespace easypr {
|
||||
|
||||
SvmTrain::SvmTrain(const char* plates_folder, const char* xml)
|
||||
: plates_folder_(plates_folder), svm_xml_(xml) {
|
||||
assert(plates_folder);
|
||||
assert(xml);
|
||||
|
||||
extractFeature = getHistomPlusColoFeatures;
|
||||
}
|
||||
|
||||
void SvmTrain::train() {
|
||||
svm_ = cv::ml::SVM::create();
|
||||
svm_->setType(cv::ml::SVM::C_SVC);
|
||||
svm_->setKernel(cv::ml::SVM::RBF);
|
||||
svm_->setDegree(0.1);
|
||||
// 1.4 bug fix: old 1.4 ver gamma is 1
|
||||
svm_->setGamma(0.1);
|
||||
svm_->setCoef0(0.1);
|
||||
svm_->setC(1);
|
||||
svm_->setNu(0.1);
|
||||
svm_->setP(0.1);
|
||||
svm_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 20000, 0.0001));
|
||||
|
||||
this->prepare();
|
||||
|
||||
if (train_file_list_.size() == 0) {
|
||||
fprintf(stdout, "No file found in the train folder!\n");
|
||||
fprintf(stdout, "You should create a folder named \"tmp\" in EasyPR main folder.\n");
|
||||
fprintf(stdout, "Copy train data folder(like \"SVM\") under \"tmp\". \n");
|
||||
return;
|
||||
}
|
||||
auto train_data = tdata();
|
||||
|
||||
fprintf(stdout, ">> Training SVM model, please wait...\n");
|
||||
long start = utils::getTimestamp();
|
||||
svm_->trainAuto(train_data, 10, SVM::getDefaultGrid(SVM::C),
|
||||
SVM::getDefaultGrid(SVM::GAMMA), SVM::getDefaultGrid(SVM::P),
|
||||
SVM::getDefaultGrid(SVM::NU), SVM::getDefaultGrid(SVM::COEF),
|
||||
SVM::getDefaultGrid(SVM::DEGREE), true);
|
||||
//svm_->train(train_data);
|
||||
|
||||
long end = utils::getTimestamp();
|
||||
fprintf(stdout, ">> Training done. Time elapse: %ldms\n", end - start);
|
||||
fprintf(stdout, ">> Saving model file...\n");
|
||||
svm_->save(svm_xml_);
|
||||
|
||||
fprintf(stdout, ">> Your SVM Model was saved to %s\n", svm_xml_);
|
||||
fprintf(stdout, ">> Testing...\n");
|
||||
|
||||
this->test();
|
||||
|
||||
}
|
||||
|
||||
void SvmTrain::test() {
|
||||
// 1.4 bug fix: old 1.4 ver there is no null judge
|
||||
// if (NULL == svm_)
|
||||
LOAD_SVM_MODEL(svm_, svm_xml_);
|
||||
|
||||
if (test_file_list_.empty()) {
|
||||
this->prepare();
|
||||
}
|
||||
|
||||
double count_all = test_file_list_.size();
|
||||
double ptrue_rtrue = 0;
|
||||
double ptrue_rfalse = 0;
|
||||
double pfalse_rtrue = 0;
|
||||
double pfalse_rfalse = 0;
|
||||
|
||||
for (auto item : test_file_list_) {
|
||||
auto image = cv::imread(item.file);
|
||||
if (!image.data) {
|
||||
std::cout << "no" << std::endl;
|
||||
continue;
|
||||
}
|
||||
cv::Mat feature;
|
||||
extractFeature(image, feature);
|
||||
|
||||
auto predict = int(svm_->predict(feature));
|
||||
//std::cout << "predict: " << predict << std::endl;
|
||||
|
||||
auto real = item.label;
|
||||
if (predict == kForward && real == kForward) ptrue_rtrue++;
|
||||
if (predict == kForward && real == kInverse) ptrue_rfalse++;
|
||||
if (predict == kInverse && real == kForward) pfalse_rtrue++;
|
||||
if (predict == kInverse && real == kInverse) pfalse_rfalse++;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
double precise = 0;
|
||||
if (ptrue_rtrue + ptrue_rfalse != 0) {
|
||||
precise = ptrue_rtrue / (ptrue_rtrue + ptrue_rfalse);
|
||||
std::cout << "precise: " << precise << std::endl;
|
||||
} else {
|
||||
std::cout << "precise: "
|
||||
<< "NA" << std::endl;
|
||||
}
|
||||
|
||||
double recall = 0;
|
||||
if (ptrue_rtrue + pfalse_rtrue != 0) {
|
||||
recall = ptrue_rtrue / (ptrue_rtrue + pfalse_rtrue);
|
||||
std::cout << "recall: " << recall << std::endl;
|
||||
} else {
|
||||
std::cout << "recall: "
|
||||
<< "NA" << std::endl;
|
||||
}
|
||||
|
||||
double Fsocre = 0;
|
||||
if (precise + recall != 0) {
|
||||
Fsocre = 2 * (precise * recall) / (precise + recall);
|
||||
std::cout << "Fsocre: " << Fsocre << std::endl;
|
||||
} else {
|
||||
std::cout << "Fsocre: "
|
||||
<< "NA" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SvmTrain::prepare() {
|
||||
srand(unsigned(time(NULL)));
|
||||
|
||||
char buffer[260] = {0};
|
||||
|
||||
sprintf(buffer, "%s/has/train", plates_folder_);
|
||||
auto has_file_train_list = utils::getFiles(buffer);
|
||||
std::random_shuffle(has_file_train_list.begin(), has_file_train_list.end());
|
||||
|
||||
sprintf(buffer, "%s/has/test", plates_folder_);
|
||||
auto has_file_test_list = utils::getFiles(buffer);
|
||||
std::random_shuffle(has_file_test_list.begin(), has_file_test_list.end());
|
||||
|
||||
sprintf(buffer, "%s/no/train", plates_folder_);
|
||||
auto no_file_train_list = utils::getFiles(buffer);
|
||||
std::random_shuffle(no_file_train_list.begin(), no_file_train_list.end());
|
||||
|
||||
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());
|
||||
|
||||
fprintf(stdout, ">> Collecting train data...\n");
|
||||
|
||||
for (auto file : has_file_train_list)
|
||||
train_file_list_.push_back({ file, kForward });
|
||||
|
||||
for (auto file : no_file_train_list)
|
||||
train_file_list_.push_back({ file, kInverse });
|
||||
|
||||
fprintf(stdout, ">> Collecting test data...\n");
|
||||
|
||||
for (auto file : has_file_test_list)
|
||||
test_file_list_.push_back({ file, kForward });
|
||||
|
||||
for (auto file : no_file_test_list)
|
||||
test_file_list_.push_back({ file, kInverse });
|
||||
}
|
||||
|
||||
cv::Ptr<cv::ml::TrainData> SvmTrain::tdata() {
|
||||
cv::Mat samples;
|
||||
std::vector<int> responses;
|
||||
|
||||
for (auto f : train_file_list_) {
|
||||
auto image = cv::imread(f.file);
|
||||
if (!image.data) {
|
||||
fprintf(stdout, ">> Invalid image: %s ignore.\n", f.file.c_str());
|
||||
continue;
|
||||
}
|
||||
cv::Mat feature;
|
||||
extractFeature(image, feature);
|
||||
feature = feature.reshape(1, 1);
|
||||
|
||||
samples.push_back(feature);
|
||||
responses.push_back(int(f.label));
|
||||
}
|
||||
|
||||
cv::Mat samples_, responses_;
|
||||
samples.convertTo(samples_, CV_32FC1);
|
||||
cv::Mat(responses).copyTo(responses_);
|
||||
|
||||
return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE, responses_);
|
||||
}
|
||||
|
||||
} // namespace easypr
|
@ -0,0 +1,8 @@
|
||||
#include "easypr/train/train.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
ITrain::ITrain() {}
|
||||
|
||||
ITrain::~ITrain() {}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
#include "easypr/util/kv.h"
|
||||
#include "easypr/util/util.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
Kv::Kv() { } //Kv类的构造函数
|
||||
|
||||
void Kv::load(const std::string &file) { //一个成员函数,用于从指定的文件中加载键值对
|
||||
this->clear();//清空当前的键值对
|
||||
std::ifstream reader(file);//打开文件
|
||||
assert(reader);
|
||||
|
||||
if (reader.is_open()) {
|
||||
while (!reader.eof()) {
|
||||
std::string line;
|
||||
std::getline(reader, line);
|
||||
if (line.empty()) continue;
|
||||
//打开文件并读取每一行
|
||||
const auto parse = [](const std::string &str) {//解析键和值
|
||||
std::string tmp, key, value;
|
||||
for (size_t i = 0, len = str.length(); i < len; ++i) {
|
||||
const char ch = str[i];
|
||||
if (ch == ' ') {//遇到空格时
|
||||
if (i > 0 && str[i - 1] != ' ' && key.empty()) {
|
||||
key = tmp;
|
||||
tmp.clear();
|
||||
}
|
||||
}//将之前的字符串作为键,空格后的字符串作为值
|
||||
else {
|
||||
tmp.push_back(ch);
|
||||
}
|
||||
if (i == len - 1) {
|
||||
value = tmp;
|
||||
}
|
||||
}
|
||||
return std::make_pair(key, value);
|
||||
};
|
||||
|
||||
auto kv = parse(line);
|
||||
this->add(kv.first, kv.second);//解析出的键值对添加到存储中
|
||||
}
|
||||
reader.close();//关闭文件
|
||||
}
|
||||
}
|
||||
|
||||
std::string Kv::get(const std::string &key) {//是一个成员函数,用于获取给定键的值
|
||||
if (data_.find(key) == data_.end()) {
|
||||
std::cerr << "[Kv] cannot find " << key << std::endl;//如果键不存在,它会打印一个错误消息
|
||||
return "";//返回一个空字符串。
|
||||
}
|
||||
return data_.at(key);//获取给定键的值
|
||||
}
|
||||
|
||||
void Kv::add(const std::string &key, const std::string &value) {//是一个成员函数,用于添加一个键值对
|
||||
if (data_.find(key) != data_.end()) {//如果键已经存在
|
||||
fprintf(stderr,
|
||||
"[Kv] find duplicate: %s = %s , ignore\n",
|
||||
key.c_str(),
|
||||
value.c_str());//会打印一个错误消息并忽略这个添加操作
|
||||
} else {
|
||||
std::string v(value);
|
||||
#ifdef OS_WINDOWS//如果在Windows操作系统上
|
||||
v = utils::utf8_to_gbk(value.c_str()); //将值从UTF-8编码转换为GBK编码。
|
||||
#endif
|
||||
data_[key] = v;//添加一个键值对
|
||||
}
|
||||
}
|
||||
|
||||
void Kv::remove(const std::string &key) {//是一个成员函数,用于删除一个键值对。
|
||||
if (data_.find(key) == data_.end()) {//如果键不存在
|
||||
std::cerr << "[Kv] cannot find " << key << std::endl;//打印一个错误消息。
|
||||
return;
|
||||
}
|
||||
data_.erase(key);//删除一个键值对。
|
||||
}
|
||||
|
||||
void Kv::clear() {//是一个成员函数,用于清空所有的键值对。
|
||||
data_.clear();//用于清空所有的键值对。
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in new issue