[DEV] better recognition

This commit is contained in:
Edouard DUPIN 2016-08-26 21:46:41 +02:00
parent 1b1992a301
commit aaceff37cb
8 changed files with 135 additions and 54 deletions

View File

@ -54,7 +54,8 @@ static float pathDistance(const std::vector<vec2>& _path1, const std::vector<vec
}
dollar::Engine::Engine():
m_PPlusDistance(0.10f) {
m_PPlusDistance(0.10f),
m_scaleKeepRatio(false) {
m_numPointsInGesture = 128;
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
setRotationInvariance(false);
@ -68,7 +69,7 @@ void dollar::Engine::setNumberPointInGesture(size_t _value) {
m_numPointsInGesture = _value;
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
for (auto &it: m_gestures) {
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance);
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance, m_scaleKeepRatio);
}
}
@ -76,6 +77,34 @@ size_t dollar::Engine::getNumberPointInGesture() {
return m_numPointsInGesture;
}
void dollar::Engine::setPPlusDistance(float _value) {
if (_value == m_PPlusDistance) {
return;
}
m_PPlusDistance = _value;
for (auto &it: m_gestures) {
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance, m_scaleKeepRatio);
}
}
float dollar::Engine::getPPlusDistance() {
return m_PPlusDistance;
}
void dollar::Engine::setScaleKeepRatio(bool _value) {
if (_value == m_scaleKeepRatio) {
return;
}
m_scaleKeepRatio = _value;
for (auto &it: m_gestures) {
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance, m_scaleKeepRatio);
}
}
bool dollar::Engine::getScaleKeepRatio() {
return m_scaleKeepRatio;
}
float dollar::Engine::distanceAtBestAngle(const std::vector<vec2>& _points, const std::vector<vec2>& _reference) {
float startRange = -m_angleRange;
float endRange = m_angleRange;
@ -177,17 +206,17 @@ static float calculatePPlusDistance(const std::vector<vec2>& _points,
}
}
if (kkkBest != -1) {
if (bestDistance > 0.2*0.2) {
// reject the distance ...
}
int32_t previous = usedId[kkkBest];
usedId[kkkBest] = iii;
distance[iii] = bestDistance;
//DOLLAR_INFO("set new link: " << iii << " with " << kkkBest << " d=" << bestDistance);
if (previous != -1) {
//DOLLAR_INFO(" Reject : " << previous);
distance[previous] = MAX_FLOAT;
iii = previous-1;
// reject the distance ... if too big ...
if (bestDistance <= 0.2*0.2) {
int32_t previous = usedId[kkkBest];
usedId[kkkBest] = iii;
distance[iii] = bestDistance;
//DOLLAR_INFO("set new link: " << iii << " with " << kkkBest << " d=" << bestDistance);
if (previous != -1) {
//DOLLAR_INFO(" Reject : " << previous);
distance[previous] = MAX_FLOAT;
iii = previous-1;
}
}
}
}
@ -276,7 +305,7 @@ bool dollar::Engine::loadGesture(const std::string& _filename) {
}
void dollar::Engine::addGesture(Gesture _gesture) {
_gesture.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance);
_gesture.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance, m_scaleKeepRatio);
m_gestures.push_back(std::move(_gesture));
}
@ -309,7 +338,7 @@ dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>&
dollar::Results dollar::Engine::recognizeN(const std::vector<std::vector<vec2>>& _strokes, const std::string& _method) {
std::vector<vec2> points = dollar::combineStrokes(_strokes);
points = dollar::normalizePath(points, m_numPointsInGesture, m_paramterIgnoreRotation);
points = dollar::normalizePath(points, m_numPointsInGesture, m_paramterIgnoreRotation, m_scaleKeepRatio);
vec2 startv = dollar::getStartVector(points, m_numPointsInGesture/RATIO_START_VECTOR);
std::vector<vec2> vector = normalyse(points);
// Keep maximum 5 results ...
@ -393,7 +422,7 @@ dollar::Results dollar::Engine::recognizeN(const std::vector<std::vector<vec2>>&
dollar::Results dollar::Engine::recognizeP(const std::vector<std::vector<vec2>>& _strokes) {
std::vector<vec2> points = dollar::combineStrokes(_strokes);
points = dollar::normalizePath(points, m_numPointsInGesture, m_paramterIgnoreRotation);
points = dollar::normalizePath(points, m_numPointsInGesture, m_paramterIgnoreRotation, m_scaleKeepRatio);
// Keep maximum 5 results ...
float bestDistance[MAX_RESULT_NUMBER];
int32_t indexOfBestMatch[MAX_RESULT_NUMBER];
@ -456,10 +485,11 @@ dollar::Results dollar::Engine::recognizeP(const std::vector<std::vector<vec2>>&
static void storeSVG(const std::string& _fileName,
const dollar::Gesture& gesture,
const std::vector<std::vector<vec2>>& _strokes,
const std::vector<vec2>& _points,
std::vector<std::pair<int32_t, int32_t>> _links) {
const dollar::Gesture& gesture,
const std::vector<std::vector<vec2>>& _strokes,
const std::vector<vec2>& _points,
std::vector<std::pair<int32_t, int32_t>> _links,
bool _keepAspectRatio) {
std::string data("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
data += "<svg height=\"100\" width=\"100\">\n";
for (auto &itLines : gesture.getPath()) {
@ -476,7 +506,7 @@ static void storeSVG(const std::string& _fileName,
data += "\"\n";
data += " />\n";
}
for (auto &itLines : dollar::scaleToOne(_strokes)) {
for (auto &itLines : dollar::scaleToOne(_strokes, _keepAspectRatio)) {
data += " <polyline fill=\"none\" stroke=\"purple\" stroke-opacity=\"0.8\" stroke-width=\"2\"\n";
data += " points=\"";
bool first = true;
@ -513,7 +543,7 @@ static void storeSVG(const std::string& _fileName,
dollar::Results dollar::Engine::recognizePPlus(const std::vector<std::vector<vec2>>& _strokes) {
std::vector<vec2> points = dollar::normalizePathToPoints(_strokes, m_PPlusDistance);
std::vector<vec2> points = dollar::normalizePathToPoints(_strokes, m_PPlusDistance, m_scaleKeepRatio);
// Keep maximum 5 results ...
float bestDistance[MAX_RESULT_NUMBER];
int32_t indexOfBestMatch[MAX_RESULT_NUMBER];
@ -536,7 +566,7 @@ dollar::Results dollar::Engine::recognizePPlus(const std::vector<std::vector<vec
float distance = MAX_FLOAT;
std::vector<std::pair<int32_t, int32_t>> dataPair;
distance = calculatePPlusDistance(points, gesture.getEnginePoints(), dataPair);
storeSVG("out_dollar/lib/recognizePPlus/" + gesture.getName() + "_" + etk::to_string(gesture.getId()) + ".svg", gesture, _strokes, points, dataPair);
//storeSVG("out_dollar/lib/recognizePPlus/" + gesture.getName() + "_" + etk::to_string(gesture.getId()) + ".svg", gesture, _strokes, points, dataPair, m_scaleKeepRatio);
for (size_t kkk=0; kkk<MAX_RESULT_NUMBER; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {

View File

@ -17,6 +17,15 @@ namespace dollar {
class Engine {
protected:
float m_PPlusDistance;
public:
void setPPlusDistance(float _value);
float getPPlusDistance();
protected:
bool m_scaleKeepRatio; // when rescale the path, keep the aspect ration for processing
public:
void setScaleKeepRatio(bool _value);
bool getScaleKeepRatio();
protected:
float m_angleRange;
bool m_paramterIgnoreRotation; //!< Ignore the start rotation of the gesture
public:

View File

@ -90,7 +90,6 @@ bool dollar::Gesture::loadJSON(const std::string& _fileName) {
m_name = doc["value"].toString().get();
m_subId = doc["sub-id"].toNumber().getU64(),
m_path = loadPointsJson(doc);
m_path = dollar::scaleToOne(m_path);
DOLLAR_DEBUG("Load gesture : " << m_name << " id=" << m_subId << " nb_elem=" << m_path.size());
return true;
}
@ -110,7 +109,6 @@ bool dollar::Gesture::loadSVG(const std::string& _fileName) {
it2.setY(it2.y()*-1);
}
}
m_path = dollar::scaleToOne(m_path);
DOLLAR_DEBUG("Load gesture : " << m_name << " id=" << m_subId << " nb_elem=" << m_path.size());
return true;
}
@ -151,7 +149,7 @@ void dollar::Gesture::storeJSON(const std::string& _fileName) {
}
void dollar::Gesture::storeSVG(const std::string& _fileName, bool _storeDot) {
std::vector<std::vector<vec2>> strokes = dollar::scaleToOne(m_path);
std::vector<std::vector<vec2>> strokes = dollar::scaleToOne(m_path, true);
std::string data("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
data += "<svg height=\"100\" width=\"100\">\n";
for (auto &itLines : strokes) {
@ -185,23 +183,25 @@ void dollar::Gesture::set(const std::string& _name, uint32_t _subId, std::vector
m_engineVector.clear();
m_engineStartV.clear();
m_enginePoints.clear();
m_path2.clear();
}
void dollar::Gesture::configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance) {
void dollar::Gesture::configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance, bool _keepAspectRatio) {
m_enginePath.clear();
m_engineVector.clear();
m_engineStartV.clear();
m_enginePoints.clear();
m_path2 = dollar::scaleToOne(m_path, _keepAspectRatio);
// Generates dots:
m_enginePoints = dollar::normalizePathToPoints(m_path, _distance);
m_enginePoints = dollar::normalizePathToPoints(m_path, _distance, _keepAspectRatio);
DOLLAR_VERBOSE("create " << m_enginePoints.size() << " points");
// for debug only
storeSVG("out_dollar/lib/gestures/" + m_name + "_" + etk::to_string(m_subId) + ".svg", true);
//storeSVG("out_dollar/lib/gestures/" + m_name + "_" + etk::to_string(m_subId) + ".svg", true);
// Simplyfy paths
std::vector<std::vector<vec2>> uniPath = dollar::makeReferenceStrokes(m_path);
// normalize paths
for (auto &it : uniPath) {
std::vector<vec2> val = dollar::normalizePath(it, _nbSample, _ignoreRotation);
std::vector<vec2> val = dollar::normalizePath(it, _nbSample, _ignoreRotation, _keepAspectRatio);
m_enginePath.push_back(val);
// calculate start vector:
vec2 startv = dollar::getStartVector(val, _startAngleIndex);

View File

@ -14,6 +14,7 @@ namespace dollar {
std::string m_name;
uint32_t m_subId;
std::vector<std::vector<vec2>> m_path;
std::vector<std::vector<vec2>> m_path2;
public:
Gesture();
bool load(const std::string& _filename);
@ -32,10 +33,10 @@ namespace dollar {
return m_subId;
}
const std::vector<std::vector<vec2>>& getPath() const {
return m_path;
return m_path2;
}
std::vector<std::vector<vec2>>& getPath() {
return m_path;
return m_path2;
}
protected:
std::vector<std::vector<vec2>> m_enginePath; // Singulized path with every conbinaison
@ -44,7 +45,7 @@ namespace dollar {
std::vector<vec2> m_enginePoints;
public:
// Configure the reference gesture for recognition...
void configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance);
void configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance, bool _keepAspectRatio);
size_t getEngineSize() const {
return m_enginePath.size();
}

View File

@ -52,18 +52,23 @@ std::vector<vec2> dollar::scaleToOne(const std::vector<vec2>& _points, bool _kee
dollar::Rectangle box(_points);
std::vector<vec2> out;
vec2 scale(1.0f/box.getSize().x(), 1.0f/box.getSize().y());
vec2 offset(0,0);
if (_keepAspectRation == true) {
float val = 1;
if (box.getSize().x() > 0) {
offset = box.getSize() * val;
if (box.getSize().x() > box.getSize().y()) {
val = 1.0f/box.getSize().x();
offset = vec2(0.0f, (1.0f-offset.y())*0.5f);
} else {
val = 1.0f/box.getSize().y();
offset = vec2((1.0f-offset.x())*0.5f, 0.0f);
}
scale = vec2(val,val);
}
for (auto &it : _points) {
vec2 tmp = it - box.getPos();
tmp *= scale;
//tmp += offset;
out.push_back(tmp);
}
return out;
@ -73,12 +78,16 @@ std::vector<std::vector<vec2>> dollar::scaleToOne(const std::vector<std::vector<
dollar::Rectangle box(_points);
std::vector<std::vector<vec2>> out;
vec2 scale(1.0f/box.getSize().x(), 1.0f/box.getSize().y());
vec2 offset(0,0);
if (_keepAspectRation == true) {
float val = 1;
if (box.getSize().x() > 0) {
offset = box.getSize() * val;
if (box.getSize().x() > box.getSize().y()) {
val = 1.0f/box.getSize().x();
offset = vec2(0.0f, (1.0f-offset.y())*0.5f);
} else {
val = 1.0f/box.getSize().y();
offset = vec2((1.0f-offset.x())*0.5f, 0.0f);
}
scale = vec2(val,val);
}
@ -87,6 +96,7 @@ std::vector<std::vector<vec2>> dollar::scaleToOne(const std::vector<std::vector<
for (auto &itPoint : it) {
vec2 tmp = itPoint - box.getPos();
tmp *= scale;
//tmp += offset;
stroke.push_back(tmp);
}
out.push_back(stroke);
@ -204,19 +214,19 @@ std::vector<vec2> dollar::normalyse(const std::vector<vec2>& _points) {
}
std::vector<vec2> dollar::normalizePath(std::vector<vec2> _points, size_t _nbSample, bool _ignoreRotation) {
std::vector<vec2> dollar::normalizePath(std::vector<vec2> _points, size_t _nbSample, bool _ignoreRotation, bool _keepAspectRatio) {
_points = dollar::resample(_points, _nbSample);
if (_ignoreRotation == true) {
_points = rotateToZero(_points);
}
_points = scaleToOne(_points);
_points = scaleToOne(_points, _keepAspectRatio);
return translateBariCenterToZero(_points);
}
std::vector<vec2> dollar::normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance) {
std::vector<vec2> dollar::normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance, bool _keepAspectRatio) {
// Scale point to (0.0,0.0) position and (1.0,1.0) size
_points = dollar::scaleToOne(_points);
_points = dollar::scaleToOne(_points, _keepAspectRatio);
std::vector<vec2> out;
for (auto &it : _points) {
if (it.size() == 0) {

View File

@ -47,7 +47,7 @@ namespace dollar {
* @param[in] _keepAspectRation Keep the aspect ratio of the scaling
* @return modify points
*/
std::vector<vec2> scaleToOne(const std::vector<vec2>& _points, bool _keepAspectRation=false);
std::vector<vec2> scaleToOne(const std::vector<vec2>& _points, bool _keepAspectRation=true);
/**
* @brief Scale the list of point in a 1.0*1.0 box started at 0.0*0.0
* @param[in] _points input path
@ -98,18 +98,20 @@ namespace dollar {
* @param[in] _points List of points in the path
* @param[in] _nbSample Number of element to resample
* @param[in] _ignoreRotation Ignore start rotation of the algorithm
* @param[in] _keepAspectRatio Keep Aspect ratio when scaling to the correct size (1.0,1.0) (it will be centered)
* @return new list of points
*/
std::vector<vec2> normalizePath(std::vector<vec2> _points, size_t _nbSample, bool _ignoreRotation);
std::vector<vec2> normalizePath(std::vector<vec2> _points, size_t _nbSample, bool _ignoreRotation, bool _keepAspectRatio);
/**
* @brief Transform the path to be comparable, resample the path with a specific number of sample, and limit size at 1.0 square center around 0
* @note The difference with @ref normalizePath is thet we do not combinethe path together, that permit to not have unneded point between strokes...
* @param[in] _points List of points in the path
* @param[in] _distance Distance between points
* @param[in] _keepAspectRatio Keep Aspect ratio when scaling to the correct size (1.0,1.0) (it will be centered)
* @return new list of points
*/
std::vector<vec2> normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance);
std::vector<vec2> normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance, bool _keepAspectRatio);
}

View File

@ -135,6 +135,7 @@ void annalyseResult(std::map<std::string, std::vector<std::pair<dollar::Results,
bool testCorpus(const std::string& _srcGesture, const std::string& _srcCorpus) {
// declare a Gesture (internal API)
dollar::Engine reco;
reco.setScaleKeepRatio(false);
TEST_PRINT("---------------------------------------------------------------------------");
TEST_PRINT("-- Load Gestures: " << _srcGesture);
TEST_PRINT("---------------------------------------------------------------------------");
@ -153,6 +154,7 @@ bool testCorpus(const std::string& _srcGesture, const std::string& _srcCorpus) {
// "label_type" ==> list of (result, file test name)
std::map<std::string, std::vector<std::pair<dollar::Results, std::string>>> agregateResults;
int32_t nbRecognise = 0;
int32_t nbRecognise2 = 0;
int32_t nbtested = 0;
for (auto &it : files) {
std::string label;
@ -180,15 +182,20 @@ bool testCorpus(const std::string& _srcGesture, const std::string& _srcCorpus) {
}
#else
if (res.getName() == label) {
nbRecognise ++;
nbRecognise++;
nbRecognise2++;
TEST_INFO(" " << res.getName() << " score=" << res.getConfidence());
} else {
} else if (etk::toupper(res.getName()) == etk::toupper(label)) {
nbRecognise2++;
TEST_WARNING(" " << res.getName() << " score=" << res.getConfidence());
}else {
TEST_ERROR(" " << res.getName() << " score=" << res.getConfidence());
}
#endif
}
annalyseResult(agregateResults);
TEST_PRINT("Recognise: " << nbRecognise << " / " << nbtested << " ==> " << (float(nbRecognise) / float(nbtested) * 100.0f) << " %");
TEST_PRINT("Recognise: " << nbRecognise << " / " << nbtested << " ==> " << (float(nbRecognise) / float(nbtested) * 100.0f) << " %");
TEST_PRINT("Recognise (a==A): " << nbRecognise2 << " / " << nbtested << " ==> " << (float(nbRecognise2) / float(nbtested) * 100.0f) << " %");
// All is done corectly
return 0;
}

View File

@ -119,6 +119,11 @@ bool testCorpus(const std::string& _srcCorpus) {
}
//listOfElementInCorpus.clear();
//listOfElementInCorpus.push_back("z");
// Value to stop grouping in the same element ...
float groupSize = 1.0;
groupSize = 1.0;
bool keepAspectRatio = false;
TEST_PRINT(" will done for: " << listOfElementInCorpus);
for (auto &itTypeOfCorpus : listOfElementInCorpus) {
TEST_PRINT("---------------------------------------------------------------------------");
@ -141,19 +146,28 @@ bool testCorpus(const std::string& _srcCorpus) {
it.resize(fileFiltered.size(), OUT_OF_RANGE);
}
// Generate Full Files:
std::string itTypeOfCorpusFileName = itTypeOfCorpus;
if (itTypeOfCorpusFileName == "/") {
itTypeOfCorpusFileName = "slash";
} else if (itTypeOfCorpusFileName == "\\") {
itTypeOfCorpusFileName = "back-slash";
} else if (itTypeOfCorpusFileName == "?") {
itTypeOfCorpusFileName = "question";
}
{
std::vector<std::string> listPath;
for (size_t iii=0; iii<fileFiltered.size(); ++iii) {
listPath.push_back(fileFiltered[iii]);
}
generateFile("out_dollar/generate-form/pre_generate/" + itTypeOfCorpus + "_FULL.svg", listPath, "");
generateFile("out_dollar/generate-form/pre_generate/" + itTypeOfCorpusFileName + "_FULL.svg", listPath, "");
}
for (size_t iii=0; iii<fileFiltered.size(); ++iii) {
dollar::Gesture gest;
std::vector<std::vector<vec2>> listPoints = dollar::loadPoints(fileFiltered[iii]);
gest.set(itTypeOfCorpus, 0, listPoints);
gest.configure(10, 8, false, 0.1);
gest.configure(10, 8, false, 0.1, keepAspectRatio);
dollar::Engine reco;
reco.setScaleKeepRatio(keepAspectRatio);
reco.addGesture(gest);
std::vector<std::string> path = etk::split(fileFiltered[iii], '/');
std::string filename = path[path.size()-1];
@ -177,9 +191,7 @@ bool testCorpus(const std::string& _srcCorpus) {
TEST_PRINT("---------------------------------------------------------------------------");
int32_t residualValues = fileFiltered.size();
int32_t subId = 1;
// Value to stop grouping in the same element ...
float groupSize = 1.0;
groupSize = 1.0;
while (residualValues > 0) {
std::vector<int32_t> countMinimum;
countMinimum.resize(fileFiltered.size(), 0);
@ -201,6 +213,10 @@ bool testCorpus(const std::string& _srcCorpus) {
}
if (bestId == -1) {
TEST_ERROR("No more elements ... residualValues=" << residualValues);
// TODO : Add the rest of the elements ...
//==> Exit loop
residualValues = 0;
continue;
@ -279,22 +295,28 @@ bool testCorpus(const std::string& _srcCorpus) {
results[jjj][linkIds[iii]] = OUT_OF_RANGE;
}
}
if (linkIds.size() <= 3) {
TEST_ERROR("Group is too small ... residualValues=" << residualValues);
// TODO : Add the rest of the elements ...
//==> Exit loop
residualValues = 0;
continue;
}
residualValues -= (linkIds.size() +1);
TEST_DEBUG("Generate output files (SVG with all added path in one file)");
// Generate Files:
std::vector<std::string> listPath;
for (size_t iii=0; iii<linkIds.size(); ++iii) {
listPath.push_back(fileFiltered[linkIds[iii]]);
}
generateFile("out_dollar/generate-form/pre_generate/" + itTypeOfCorpus + "_" + etk::to_string(subId) + ".svg", listPath, fileFiltered[bestId]);
generateFile("out_dollar/generate-form/pre_generate/" + itTypeOfCorpusFileName + "_" + etk::to_string(subId) + ".svg", listPath, fileFiltered[bestId]);
TEST_DEBUG("Generate output file (corpus ...)");
// declare a Gesture (internal API)
dollar::Gesture ref;
ref.set(itTypeOfCorpus, subId, dollar::loadPoints(fileFiltered[bestId]));
// Store gesture with his extention type:
ref.store("out_dollar/generate-form/corpus/" + itTypeOfCorpus + "_" + etk::to_string(subId) + ".json");
ref.store("out_dollar/generate-form/corpus_svg/" + itTypeOfCorpus + "_" + etk::to_string(subId) + ".svg");
ref.store("out_dollar/generate-form/corpus/" + itTypeOfCorpusFileName + "_" + etk::to_string(subId) + ".json");
ref.store("out_dollar/generate-form/corpus_svg/" + itTypeOfCorpusFileName + "_" + etk::to_string(subId) + ".svg");
// Increment subId...