Compare commits
32 Commits
zhaohaoyi_
...
develop
Binary file not shown.
Binary file not shown.
@ -1,454 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
#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
@ -1,466 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#include "easypr/core/params.h"
|
||||
|
||||
namespace easypr {
|
||||
CParams* CParams::instance_ = nullptr;
|
||||
|
||||
CParams* CParams::instance() {
|
||||
if (!instance_) {
|
||||
instance_ = new CParams;
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
}/*! \namespace easypr*/
|
@ -1,77 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
#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
After Width: | Height: | Size: 43 KiB |
@ -1,277 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,356 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
#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.
@ -1,196 +0,0 @@
|
||||
#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
|
@ -1,8 +0,0 @@
|
||||
#include "easypr/train/train.h"
|
||||
|
||||
namespace easypr {
|
||||
|
||||
ITrain::ITrain() {}
|
||||
|
||||
ITrain::~ITrain() {}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
#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();//用于清空所有的键值对。
|
||||
}
|
||||
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
#include "easypr/util/util.h"
|
||||
#include <string>
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#define PATH_DELIMITER '\\'
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
#elif defined(OS_LINUX) || defined(OS_UNIX)
|
||||
|
||||
#include <cstring>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PATH_DELIMITER '/'
|
||||
#endif
|
||||
|
||||
#ifdef OS_UNIX
|
||||
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
|
||||
namespace easypr {
|
||||
|
||||
long Utils::getTimestamp() {
|
||||
#ifdef OS_WINDOWS
|
||||
return static_cast<long>(cv::getTickCount());
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return (ts.tv_sec * 1e3 + ts.tv_nsec / 1e6);
|
||||
#endif
|
||||
|
||||
#ifdef OS_UNIX
|
||||
// there is no function provided by osx to get system tick count.
|
||||
// but considering the purpose by using this function,
|
||||
// we can simply return a millisecond since 1970/1/1 to calc the time elapse.
|
||||
struct timeb tb;
|
||||
ftime(&tb);
|
||||
return long(tb.time * 1e3 + tb.millitm);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Utils::getFileName(const std::string &path,
|
||||
const bool postfix /* = false */) {
|
||||
if (!path.empty()) {
|
||||
size_t last_slash = utils::get_last_slash(path);
|
||||
size_t last_dot = path.find_last_of('.');
|
||||
|
||||
if (last_dot < last_slash || last_dot == std::string::npos) {
|
||||
// not found the right dot of the postfix,
|
||||
// return the file name directly
|
||||
return path.substr(last_slash + 1);
|
||||
} else {
|
||||
// the path has a postfix
|
||||
if (postfix) {
|
||||
// return the file name including postfix
|
||||
return path.substr(last_slash + 1);
|
||||
}
|
||||
// without postfix
|
||||
return path.substr(last_slash + 1, last_dot - last_slash - 1);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::string> Utils::splitString(const std::string &str,
|
||||
const char delimiter) {
|
||||
std::vector<std::string> splited;
|
||||
std::string s(str);
|
||||
size_t pos;
|
||||
|
||||
while ((pos = s.find(delimiter)) != std::string::npos) {
|
||||
std::string sec = s.substr(0, pos);
|
||||
|
||||
if (!sec.empty()) {
|
||||
splited.push_back(s.substr(0, pos));
|
||||
}
|
||||
|
||||
s = s.substr(pos + 1);
|
||||
}
|
||||
|
||||
splited.push_back(s);
|
||||
|
||||
return splited;
|
||||
}
|
||||
|
||||
std::vector<std::string> Utils::getFiles(const std::string &folder,
|
||||
const bool all /* = true */) {
|
||||
std::vector<std::string> files;
|
||||
std::list<std::string> subfolders;
|
||||
subfolders.push_back(folder);
|
||||
#ifdef OS_WINDOWS
|
||||
while (!subfolders.empty()) {
|
||||
std::string current_folder(subfolders.back());
|
||||
|
||||
if (*(current_folder.end() - 1) != '/') {
|
||||
current_folder.append("/*");
|
||||
} else {
|
||||
current_folder.append("*");
|
||||
}
|
||||
|
||||
subfolders.pop_back();
|
||||
|
||||
struct _finddata_t file_info;
|
||||
auto file_handler = _findfirst(current_folder.c_str(), &file_info);
|
||||
|
||||
while (file_handler != -1) {
|
||||
if (all &&
|
||||
(!strcmp(file_info.name, ".") || !strcmp(file_info.name, ".."))) {
|
||||
if (_findnext(file_handler, &file_info) != 0) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_info.attrib & _A_SUBDIR) {
|
||||
// it's a sub folder
|
||||
if (all) {
|
||||
// will search sub folder
|
||||
std::string folder(current_folder);
|
||||
folder.pop_back();
|
||||
folder.append(file_info.name);
|
||||
|
||||
subfolders.push_back(folder.c_str());
|
||||
}
|
||||
} else {
|
||||
// it's a file
|
||||
std::string file_path;
|
||||
// current_folder.pop_back();
|
||||
file_path.assign(current_folder.c_str()).pop_back();
|
||||
file_path.append(file_info.name);
|
||||
|
||||
files.push_back(file_path);
|
||||
}
|
||||
|
||||
if (_findnext(file_handler, &file_info) != 0) break;
|
||||
} // while
|
||||
_findclose(file_handler);
|
||||
}
|
||||
#elif defined(OS_LINUX) || defined(OS_UNIX)
|
||||
while (!subfolders.empty()) {
|
||||
std::string current_folder(subfolders.back());
|
||||
|
||||
if (*(current_folder.end() - 1) != '/') {
|
||||
current_folder.push_back('/');
|
||||
}
|
||||
|
||||
DIR* pdir = opendir(current_folder.c_str());
|
||||
|
||||
subfolders.pop_back();
|
||||
|
||||
if (!pdir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dirent* dir = NULL;
|
||||
|
||||
while ((dir = readdir(pdir)) != NULL) {
|
||||
// iterates the current folder, search file & sub folder
|
||||
struct stat st;
|
||||
|
||||
if (all && (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))) {
|
||||
// must ignore . & ..
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(dir->d_name, ".DS_Store")) {
|
||||
// in OSX, 'finder' will create .DS_Store
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string file_path;
|
||||
|
||||
file_path.append(current_folder.c_str());
|
||||
file_path.append(dir->d_name);
|
||||
|
||||
if (lstat(file_path.c_str(), &st) < 0) {
|
||||
// perror("lstat");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// it's a sub folder
|
||||
if (all) {
|
||||
// will search sub folder
|
||||
std::string subfolder(current_folder);
|
||||
subfolder.append(dir->d_name);
|
||||
|
||||
subfolders.push_back(subfolder.c_str());
|
||||
}
|
||||
} else {
|
||||
// it's a file
|
||||
files.push_back(file_path);
|
||||
}
|
||||
} // while
|
||||
closedir(pdir);
|
||||
}
|
||||
#endif
|
||||
return files;
|
||||
}
|
||||
|
||||
bool Utils::mkdir(const std::string folder) {
|
||||
std::string folder_builder;
|
||||
std::string sub;
|
||||
sub.reserve(folder.size());
|
||||
for (auto it = folder.begin(); it != folder.end(); ++it) {
|
||||
const char c = *it;
|
||||
sub.push_back(c);
|
||||
if (c == PATH_DELIMITER || it == folder.end() - 1) {
|
||||
folder_builder.append(sub);
|
||||
#ifdef OS_WINDOWS
|
||||
if (0 != ::_access(folder_builder.c_str(), 0)) {
|
||||
#else
|
||||
if (0 != ::access(folder_builder.c_str(), 0)) {
|
||||
#endif
|
||||
// this folder not exist
|
||||
#ifdef OS_WINDOWS
|
||||
if (0 != ::_mkdir(folder_builder.c_str())) {
|
||||
#else
|
||||
if (0 != ::mkdir(folder_builder.c_str(), S_IRWXU)) {
|
||||
#endif
|
||||
// create failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
sub.clear();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Utils::imwrite(const std::string &file, const cv::Mat &image) {
|
||||
auto folder = file.substr(0, utils::get_last_slash(file));
|
||||
Utils::mkdir(folder);
|
||||
return cv::imwrite(file, image);
|
||||
}
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
std::string Utils::utf8_to_gbk(const char* utf8) {
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
|
||||
wchar_t* wszGBK = new wchar_t[len + 1];
|
||||
memset(wszGBK, 0, len * 2 + 2);
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wszGBK, len);
|
||||
len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
|
||||
char* szGBK = new char[len + 1];
|
||||
memset(szGBK, 0, len + 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
|
||||
std::string strTemp(szGBK);
|
||||
if (wszGBK)
|
||||
delete[] wszGBK;
|
||||
if (szGBK)
|
||||
delete[] szGBK;
|
||||
return strTemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::size_t Utils::get_last_slash(const std::string &path) {
|
||||
#ifdef OS_WINDOWS
|
||||
size_t last_slash_1 = path.find_last_of("\\");
|
||||
size_t last_slash_2 = path.find_last_of("/");
|
||||
size_t last_slash;
|
||||
|
||||
if (last_slash_1 != std::string::npos && last_slash_2 != std::string::npos) {
|
||||
// C:/path\\to/file.postfix
|
||||
last_slash = std::max(last_slash_1, last_slash_2);
|
||||
} else {
|
||||
// C:\\path\\to\\file.postfix
|
||||
// C:/path/to/file.postfix
|
||||
last_slash =
|
||||
(last_slash_1 == std::string::npos) ? last_slash_2 : last_slash_1;
|
||||
}
|
||||
#else
|
||||
size_t last_slash = path.find_last_of('/');
|
||||
#endif
|
||||
return last_slash;
|
||||
}
|
||||
|
||||
} // namespace easypr
|
Loading…
Reference in new issue