[DEV] separates algos to simplify codes

This commit is contained in:
Edouard DUPIN 2016-08-29 21:53:51 +02:00
parent d984faf27f
commit a6f9f546a4
19 changed files with 1144 additions and 703 deletions

View File

@ -9,290 +9,25 @@
#include <dollar/Rectangle.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <math.h>
#include <algorithm>
#define MAX_FLOAT std::numeric_limits<float>::max()
// simple decree to radian convertion
#define DEG_2_RAD(ddd) (ddd*M_PI/180.0)
#include <dollar/EngineN.h>
#include <dollar/EngineP.h>
#include <dollar/EnginePPlus.h>
// Test is done on the square of 1.0f * 1.0f ==> the result depend on the diagonal size
#define DIAGONAL (1.414213562)
#define HALF_DIAGONAL (0.707106781)
// an other magic number ratio of starting vetor pos... ==> must be reworked ==> nbElementInVector/RATIO_START_VECTOR
#define RATIO_START_VECTOR (8)
// angle precision of the detecting the way of rotation
#define ANGLE_ROTATION (2.0f)
// Angle to start processing of a start vector comparaison
#define ANGLE_THRESHOLD_START_PROCESSING DEG_2_RAD(30.0)
// A magic number: 0.5 * (-1.0 + sqrt(5.0))
#define MAGIC_RATIO (0.618033989)
static float angleBetweenUnitVectors(const vec2& _vect1, const vec2& _vect2) {
float n = _vect1.dot(_vect2);
// TODO : No needs
if (n < -1.0 || n > +1.0){
n = round(n*100000.0f)/100000.0f;
}
return std::acos(n); // arc cosine of the vector dot product
}
static float pathDistance(const std::vector<vec2>& _path1, const std::vector<vec2>& _path2) {
// assumes pts1.size == pts2.size
float distance = 0.0;
if (_path1.size() != _path2.size()) {
DOLLAR_ERROR("Path have not the same size: " << _path1.size() << " != " << _path2.size());
return MAX_FLOAT;
}
for (size_t iii=0; iii<_path1.size(); ++iii) {
distance += (_path2[iii]-_path1[iii]).length();
}
DOLLAR_VERBOSE("distance: " << distance << " size= " << _path1.size());
return (distance / _path1.size());
}
dollar::Engine::Engine():
m_PPlusDistance(0.10f),
m_PPlusExcludeDistance(0.2*0.2),
m_scaleKeepRatio(false) {
m_numPointsInGesture = 128;
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
setRotationInvariance(false);
}
void dollar::Engine::setNumberPointInGesture(size_t _value) {
if (_value == m_numPointsInGesture) {
return;
}
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, m_scaleKeepRatio);
}
}
size_t dollar::Engine::getNumberPointInGesture() {
return m_numPointsInGesture;
}
void dollar::Engine::setPPlusDistance(float _value) {
if (_value*_value == m_PPlusDistance*) {
return;
}
m_PPlusDistance = _value*_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 std::sqrt(m_PPlusDistance);
}
void dollar::Engine::setPPlusExcludeDistance(float _value) {
if (_value == m_PPlusExcludeDistance) {
return;
}
m_PPlusExcludeDistance = _value;
}
float dollar::Engine::getPPlusExcludeDistance() {
return m_PPlusExcludeDistance;
}
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;
float x1 = MAGIC_RATIO * startRange + (1.0 - MAGIC_RATIO) * endRange;
float f1 = pathDistance(dollar::rotateBy(_points, x1), _reference);
float x2 = (1.0 - MAGIC_RATIO) * startRange + MAGIC_RATIO * endRange;
float f2 = pathDistance(dollar::rotateBy(_points, x2), _reference);
DOLLAR_VERBOSE("init: startRange=" << startRange << " endRange=" << endRange << " MAGIC_RATIO=" << MAGIC_RATIO << " x1=" << x1 << " f1=" << f1 << " x2=" << x2 << " f2=" << f2);
while (fabs(endRange - startRange) > ANGLE_ROTATION) {
if (f1 < f2) {
endRange = x2;
x2 = x1;
f2 = f1;
x1 = MAGIC_RATIO * startRange + (1.0f - MAGIC_RATIO) * endRange;
f1 = pathDistance(dollar::rotateBy(_points, x1), _reference);
} else {
startRange = x1;
x1 = x2;
f1 = f2;
x2 = (1.0f - MAGIC_RATIO) * startRange + MAGIC_RATIO * endRange;
f2 = pathDistance(dollar::rotateBy(_points, x2), _reference);
}
}
return std::min(f1, f2);
}
static float cloudDistance(const std::vector<vec2>& _points1, const std::vector<vec2>& _points2, size_t _start) {
std::vector<bool> matched;
matched.resize(_points1.size(), false);
float out = 0;
size_t iii = _start;
do {
float min = MAX_FLOAT;
size_t index = 0;
for (size_t jjj=0; jjj<matched.size(); ++jjj) {
if (matched[jjj] == true) {
continue;
}
float distance = (_points1[iii] - _points2[jjj]).length();
if (distance < min) {
min = distance;
index = jjj;
}
}
matched[index] = true;
float weight = 1.0 - ((iii - _start + _points1.size()) % _points1.size())/_points1.size();
out = out + weight* min;
iii = (iii + 1) % _points1.size();
} while(iii != _start);
return out;
}
//Greedy-Cloud-Match
static float calculateBestDistance(const std::vector<vec2>& _points, const std::vector<vec2>& _reference) {
float out = MAX_FLOAT;
float si = 0.5f;
float step = pow(_points.size(), si-1);
if (step < 1) {
// DOLLAR_ERROR(" step is too small ... " << step);
step = 1.0f;
}
for (size_t iii=0; iii<_points.size(); iii+=int32_t(step)) {
float d1 = cloudDistance(_points, _reference, iii);
float d2 = cloudDistance(_reference, _points, iii);
out = std::min(out, std::min(d1,d2));
}
return out; // Distance to the nearest point must be < 2.0 (maximum distance visible)
}
static float calculatePPlusDistance(const std::vector<vec2>& _points,
const std::vector<vec2>& _reference,
std::vector<std::pair<int32_t, int32_t>>& _dataDebug) {
std::vector<float> distance; // note: use square distance (faster, we does not use std::sqrt())
distance.resize(_points.size(), MAX_FLOAT);
// point Id that is link on the reference.
std::vector<int32_t> usedId;
usedId.resize(_reference.size(), -1);
for (int32_t iii=0; iii<_points.size(); iii++) {
if (distance[iii] < 100.0) {
continue;
}
float bestDistance = MAX_FLOAT;
int32_t kkkBest = -1;
for (int32_t kkk=0; kkk<_reference.size(); ++kkk) {
float dist = (_points[iii]-_reference[kkk]).length2();
if (usedId[kkk] != -1) {
if (dist < distance[usedId[kkk]]) {
if (dist < bestDistance) {
bestDistance = dist;
kkkBest = kkk;
}
}
} else {
if (dist < bestDistance) {
bestDistance = dist;
kkkBest = kkk;
}
}
}
if (kkkBest != -1) {
// reject the distance ... if too big ...
if (bestDistance <= m_PPlusExcludeDistance) {
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;
}
}
}
}
double fullDistance = 0;
int32_t nbTestNotUsed = 0;
int32_t nbReferenceNotUsed = 0;
// now we count the full distance use and the number of local gesture not use
for (auto &it : distance) {
if (it < 100.0) {
fullDistance += it;
} else {
nbTestNotUsed++;
}
}
// we count the number of point in the gesture reference not used:
for (auto &it : usedId) {
if (it == -1) {
nbReferenceNotUsed++;
}
}
// now we add panality:
fullDistance += float(nbTestNotUsed)* 0.1f;
fullDistance += float(nbReferenceNotUsed)* 0.1f;
m_nbResult(5) {
for (int32_t kkk=0; kkk<usedId.size(); ++kkk) {
if (usedId[kkk] != -1) {
_dataDebug.push_back(std::make_pair(usedId[kkk], kkk));
}
}
DOLLAR_DEBUG("test distance : " << fullDistance << " nbTestNotUsed=" << nbTestNotUsed << " nbReferenceNotUsed=" << nbReferenceNotUsed);
return fullDistance;
}
float dollar::Engine::optimalCosineDistance(const std::vector<vec2>& _vect1, const std::vector<vec2>& _vect2) {
if (_vect1.size() != _vect2.size()) {
DOLLAR_ERROR("Vector have not the same size: " << _vect1.size() << " != " << _vect2.size());
return M_PI;
}
double somDot = 0;
double somCross = 0;
for (size_t iii=0; iii<_vect1.size(); ++iii) {
somDot += _vect1[iii].dot(_vect2[iii]);
somCross += _vect1[iii].cross(_vect2[iii]);
}
if (somDot == 0.0f) {
DOLLAR_ERROR("devide by 0");
return M_PI;
}
float angle = std::atan(somCross / somDot);
return std::acos(somDot * std::cos(angle) + somCross * std::sin(angle));
void dollar::Engine::setNumberResult(size_t _value) {
m_nbResult = _value;
}
void dollar::Engine::setRotationInvariance(bool _ignoreRotation) {
m_paramterIgnoreRotation = _ignoreRotation;
if (m_paramterIgnoreRotation == true) {
m_angleRange = 45.0;
} else {
m_angleRange = 15.0;
}
size_t dollar::Engine::getNumberResult() {
return m_nbResult;
}
bool dollar::Engine::loadPath(const std::string& _path) {
DOLLAR_INFO("Load Path: " << _path);
etk::FSNode path(_path);
@ -305,313 +40,29 @@ bool dollar::Engine::loadPath(const std::string& _path) {
return true;
}
bool dollar::Engine::loadGesture(const std::string& _filename) {
dollar::Gesture ref;
DOLLAR_DEBUG("Load Gesture: " << _filename);
if (ref.load(_filename) == true) {
addGesture(std::move(ref));
return true;
}
return false;
}
void dollar::Engine::addGesture(Gesture _gesture) {
_gesture.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance, m_scaleKeepRatio);
m_gestures.push_back(std::move(_gesture));
}
dollar::Results dollar::Engine::recognize(const std::vector<vec2>& _points, const std::string& _method) {
dollar::Results dollar::Engine::recognize(const std::vector<vec2>& _points) {
std::vector<std::vector<vec2>> tmp;
tmp.push_back(_points);
return recognize(tmp, _method);
return recognize2(tmp);
}
dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>& _points) {
return recognize2(_points);
}
#define MAX_RESULT_NUMBER (5)
dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>& _strokes, const std::string& _method) {
// Check if we have gestures...
if (m_gestures.empty()) {
DOLLAR_WARNING("No templates loaded so no symbols to match.");
return Results();
}
if ( _method == "$N-protractor"
|| _method == "$N"
ememory::SharedPtr<dollar::Engine> dollar::createEngine(const std::string& _method) {
if ( _method == "$N"
|| _method == "$1") {
return recognizeN(_strokes, _method);
} else if (_method == "$P") {
return recognizeP(_strokes);
} else if (_method == "$P+") {
return recognizePPlus(_strokes);
return ememory::makeShared<dollar::EngineN>(false);
}
DOLLAR_WARNING("Un-recognise methode ... '" << _method << "' supported: [$1,$N,$N-protractor,$P,$P+]" );
return Results();
if (_method == "$N-protractor") {
return ememory::makeShared<dollar::EngineN>(true);
}
if (_method == "$P") {
return ememory::makeShared<dollar::EngineP>();
}
if (_method == "$P+") {
return ememory::makeShared<dollar::EnginePPlus>();
}
return nullptr;
}
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, m_scaleKeepRatio);
vec2 startv = dollar::getStartVector(points, m_numPointsInGesture/RATIO_START_VECTOR);
std::vector<vec2> vector = normalyse(points);
// Keep maximum 5 results ...
float bestDistance[MAX_RESULT_NUMBER];
int32_t indexOfBestMatch[MAX_RESULT_NUMBER];
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii].getName() << "'");
Gesture gesture = m_gestures[iii];
for (size_t jjj=0; jjj<gesture.getEngineSize(); ++jjj) {
if (gesture.getEnginePath(jjj).size() == 0) {
DOLLAR_ERROR("Reference path with no Value");
continue;
}
// strokes start in the same direction
if(angleBetweenUnitVectors(startv, gesture.getEngineStartVector(jjj)) > ANGLE_THRESHOLD_START_PROCESSING) {
continue;
}
float distance = MAX_FLOAT;
// for Protractor
if (_method=="$p-protractor") {
distance = optimalCosineDistance(vector, gesture.getEngineVector(jjj));
} else {
// Golden Section Search (original $N)
distance = distanceAtBestAngle(points, gesture.getEnginePath(jjj));
}
for (size_t kkk=0; kkk<MAX_RESULT_NUMBER; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=MAX_RESULT_NUMBER-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
// transform distance in a % range
if (_method == "$p-protractor") {
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
if (-1 != indexOfBestMatch[0]) {
float score = MAX_FLOAT;;
if (bestDistance[iii] != 0.0) {
score = 1.0f / bestDistance[iii];
}
res.addValue(m_gestures[indexOfBestMatch[iii]].getName(), score);
}
}
} else {
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
if (-1 != indexOfBestMatch[0]) {
float score = 1.0 - (bestDistance[iii] / HALF_DIAGONAL);
res.addValue(m_gestures[indexOfBestMatch[iii]].getName(), score);
}
}
}
return res;
}
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, m_scaleKeepRatio);
// Keep maximum 5 results ...
float bestDistance[MAX_RESULT_NUMBER];
int32_t indexOfBestMatch[MAX_RESULT_NUMBER];
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii].getName() << "'");
Gesture gesture = m_gestures[iii];
if (gesture.getEngineSize() == 0) {
// Only need the first gesture ...
continue;
}
size_t jjj = 0;
if (gesture.getEnginePath(jjj).size() == 0) {
DOLLAR_ERROR("Reference path with no Value");
continue;
}
float distance = MAX_FLOAT;
distance = calculateBestDistance(points, gesture.getEnginePath(jjj));
for (size_t kkk=0; kkk<MAX_RESULT_NUMBER; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=MAX_RESULT_NUMBER-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
if (-1 != indexOfBestMatch[iii]) {
//float score = std::max((2.0 - bestDistance[iii])/2.0, 0.0);
float score = bestDistance[iii];
res.addValue(m_gestures[indexOfBestMatch[iii]].getName(), score);
}
}
return res;
}
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,
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()) {
data += " <polyline fill=\"none\" stroke=\"black\" stroke-opacity=\"0.8\" stroke-width=\"2\"\n";
data += " points=\"";
bool first = true;
for (auto& itPoints : itLines) {
if (first == false) {
data += " ";
}
first = false;
data += etk::to_string(itPoints.x()*100.0f) + "," + etk::to_string((1.0-itPoints.y())*100.0f);
}
data += "\"\n";
data += " />\n";
}
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;
for (auto& itPoints : itLines) {
if (first == false) {
data += " ";
}
first = false;
data += etk::to_string(itPoints.x()*100.0f) + "," + etk::to_string((1.0-itPoints.y())*100.0f);
}
data += "\"\n";
data += " />\n";
}
std::vector<vec2> refListPoint = gesture.getEnginePoints();
for (auto &it : refListPoint) {
data += " <circle fill=\"red\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string((1.0-it.y())*100.0f) + "\" r=\"0.6\"/>\n";
}
std::vector<vec2> testListPoint = _points;
for (auto &it : testListPoint) {
data += " <circle fill=\"orange\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string((1.0-it.y())*100.0f) + "\" r=\"0.6\"/>\n";
}
for (auto &it : _links) {
data += " <polyline fill=\"none\" stroke=\"blue\" stroke-opacity=\"0.8\" stroke-width=\"0.5\"\n";
data += " points=\"";
data += etk::to_string(refListPoint[it.second].x()*100.0f) + "," + etk::to_string((1.0-refListPoint[it.second].y())*100.0f);
data += " ";
data += etk::to_string(testListPoint[it.first].x()*100.0f) + "," + etk::to_string((1.0-testListPoint[it.first].y())*100.0f);
data += "\"\n";
data += " />\n";
}
data += "</svg>\n";
etk::FSNodeWriteAllData(_fileName, data);
}
dollar::Results dollar::Engine::recognizePPlus(const std::vector<std::vector<vec2>>& _strokes) {
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];
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii].getName() << "'");
Gesture gesture = m_gestures[iii];
if (gesture.getEngineSize() == 0) {
// Only need the first gesture ...
continue;
}
if (gesture.getEnginePoints().size() == 0) {
//DOLLAR_ERROR("Reference path with no Value");
continue;
}
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, m_scaleKeepRatio);
for (size_t kkk=0; kkk<MAX_RESULT_NUMBER; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=MAX_RESULT_NUMBER-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
for (size_t iii=0; iii<MAX_RESULT_NUMBER; ++iii) {
if (-1 != indexOfBestMatch[iii]) {
//float score = std::max((2.0 - bestDistance[iii])/2.0, 0.0);
float score = bestDistance[iii];
res.addValue(m_gestures[indexOfBestMatch[iii]].getName(), score);
}
}
return res;
}

View File

@ -12,50 +12,29 @@
#include <dollar/Results.h>
#include <string>
#include <dollar/Gesture.h>
#include <ememory/memory.h>
namespace dollar {
class Engine {
protected:
float m_PPlusDistance;
size_t m_nbResult; // Number of result in the recognition parsing
public:
void setPPlusDistance(float _value);
float getPPlusDistance();
protected:
float m_PPlusExcludeDistance;
public:
void setPPlusExcludeDistance(float _value);
float setPPlusExcludeDistance();
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:
void setRotationInvariance(bool _ignoreRotation);
protected:
size_t m_numPointsInGesture; //!< Number of point in a gesture to recognise patern ...
public:
void setNumberPointInGesture(size_t _value);
size_t getNumberPointInGesture();
protected:
std::vector<Gesture> m_gestures; //!< List of all loaded gesture in the engine
void setNumberResult(size_t _value);
size_t getNumberResult();
public:
Engine();
float distanceAtBestAngle(const std::vector<vec2>& _points, const std::vector<vec2>& _reference);
Results recognize(const std::vector<std::vector<vec2>>& _paths, const std::string& _method="$N");
Results recognize(const std::vector<vec2>& _points, const std::string& _method="$N");
float optimalCosineDistance(const std::vector<vec2>& _vect1, const std::vector<vec2>& _vect2);
bool loadPath(const std::string& _path);
bool loadGesture(const std::string& _filename);
void addGesture(Gesture _gesture);
private:
Results recognizeN(const std::vector<std::vector<vec2>>& _paths, const std::string& _method="$N");
Results recognizeP(const std::vector<std::vector<vec2>>& _paths);
Results recognizePPlus(const std::vector<std::vector<vec2>>& _paths);
virtual ~Engine() = default;
dollar::Results recognize(const std::vector<vec2>& _paths);
dollar::Results recognize(const std::vector<std::vector<vec2>>& _paths);
protected:
virtual dollar::Results recognize2(const std::vector<std::vector<vec2>>& _paths) = 0;
public:
virtual bool loadPath(const std::string& _path);
virtual bool loadGesture(const std::string& _filename) = 0;
virtual void addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) = 0;
};
ememory::SharedPtr<dollar::Engine> createEngine(const std::string& _method="$N");
}

240
dollar/EngineN.cpp Normal file
View File

@ -0,0 +1,240 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/EngineN.h>
#include <dollar/debug.h>
#include <dollar/Rectangle.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <math.h>
#include <algorithm>
#define MAX_FLOAT std::numeric_limits<float>::max()
// simple decree to radian convertion
#define DEG_2_RAD(ddd) (ddd*M_PI/180.0)
// Test is done on the square of 1.0f * 1.0f ==> the result depend on the diagonal size
#define DIAGONAL (1.414213562)
#define HALF_DIAGONAL (0.707106781)
// an other magic number ratio of starting vetor pos... ==> must be reworked ==> nbElementInVector/RATIO_START_VECTOR
#define RATIO_START_VECTOR (8)
// angle precision of the detecting the way of rotation
#define ANGLE_ROTATION (2.0f)
// Angle to start processing of a start vector comparaison
#define ANGLE_THRESHOLD_START_PROCESSING DEG_2_RAD(30.0)
// A magic number: 0.5 * (-1.0 + sqrt(5.0))
#define MAGIC_RATIO (0.618033989)
static float angleBetweenUnitVectors(const vec2& _vect1, const vec2& _vect2) {
float n = _vect1.dot(_vect2);
// TODO : No needs
if (n < -1.0 || n > +1.0){
n = round(n*100000.0f)/100000.0f;
}
return std::acos(n); // arc cosine of the vector dot product
}
static float pathDistance(const std::vector<vec2>& _path1, const std::vector<vec2>& _path2) {
// assumes pts1.size == pts2.size
float distance = 0.0;
if (_path1.size() != _path2.size()) {
DOLLAR_ERROR("Path have not the same size: " << _path1.size() << " != " << _path2.size());
return MAX_FLOAT;
}
for (size_t iii=0; iii<_path1.size(); ++iii) {
distance += (_path2[iii]-_path1[iii]).length();
}
DOLLAR_VERBOSE("distance: " << distance << " size= " << _path1.size());
return (distance / _path1.size());
}
dollar::EngineN::EngineN(bool _protractor):
m_protractor(_protractor),
m_numPointsInGesture(128) {
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
setRotationInvariance(false);
}
void dollar::EngineN::setNumberPointInGesture(size_t _value) {
if (_value == m_numPointsInGesture) {
return;
}
m_numPointsInGesture = _value;
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
for (auto &it: m_gestures) {
if (it == nullptr) {
continue;
}
it->configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation);
}
}
size_t dollar::EngineN::getNumberPointInGesture() {
return m_numPointsInGesture;
}
float dollar::EngineN::distanceAtBestAngle(const std::vector<vec2>& _points, const std::vector<vec2>& _reference) {
float startRange = -m_angleRange;
float endRange = m_angleRange;
float x1 = MAGIC_RATIO * startRange + (1.0 - MAGIC_RATIO) * endRange;
float f1 = pathDistance(dollar::rotateBy(_points, x1), _reference);
float x2 = (1.0 - MAGIC_RATIO) * startRange + MAGIC_RATIO * endRange;
float f2 = pathDistance(dollar::rotateBy(_points, x2), _reference);
DOLLAR_VERBOSE("init: startRange=" << startRange << " endRange=" << endRange << " MAGIC_RATIO=" << MAGIC_RATIO << " x1=" << x1 << " f1=" << f1 << " x2=" << x2 << " f2=" << f2);
while (fabs(endRange - startRange) > ANGLE_ROTATION) {
if (f1 < f2) {
endRange = x2;
x2 = x1;
f2 = f1;
x1 = MAGIC_RATIO * startRange + (1.0f - MAGIC_RATIO) * endRange;
f1 = pathDistance(dollar::rotateBy(_points, x1), _reference);
} else {
startRange = x1;
x1 = x2;
f1 = f2;
x2 = (1.0f - MAGIC_RATIO) * startRange + MAGIC_RATIO * endRange;
f2 = pathDistance(dollar::rotateBy(_points, x2), _reference);
}
}
return std::min(f1, f2);
}
float dollar::EngineN::optimalCosineDistance(const std::vector<vec2>& _vect1, const std::vector<vec2>& _vect2) {
if (_vect1.size() != _vect2.size()) {
DOLLAR_ERROR("Vector have not the same size: " << _vect1.size() << " != " << _vect2.size());
return M_PI;
}
double somDot = 0;
double somCross = 0;
for (size_t iii=0; iii<_vect1.size(); ++iii) {
somDot += _vect1[iii].dot(_vect2[iii]);
somCross += _vect1[iii].cross(_vect2[iii]);
}
if (somDot == 0.0f) {
DOLLAR_ERROR("devide by 0");
return M_PI;
}
float angle = std::atan(somCross / somDot);
return std::acos(somDot * std::cos(angle) + somCross * std::sin(angle));
}
void dollar::EngineN::setRotationInvariance(bool _ignoreRotation) {
m_paramterIgnoreRotation = _ignoreRotation;
if (m_paramterIgnoreRotation == true) {
m_angleRange = 45.0;
} else {
m_angleRange = 15.0;
}
}
bool dollar::EngineN::loadGesture(const std::string& _filename) {
ememory::SharedPtr<dollar::Gesture> ref = ememory::makeShared<dollar::GestureN>();
DOLLAR_DEBUG("Load Gesture: " << _filename);
if (ref->load(_filename) == true) {
addGesture(ref);
return true;
}
return false;
}
void dollar::EngineN::addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) {
ememory::SharedPtr<dollar::GestureN> gest = ememory::dynamicPointerCast<dollar::GestureN>(_gesture);
if (gest != nullptr) {
gest->configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation);
m_gestures.push_back(gest);
}
}
dollar::Results dollar::EngineN::recognize2(const std::vector<std::vector<vec2>>& _strokes) {
std::vector<vec2> points = dollar::combineStrokes(_strokes);
points = dollar::normalizePath(points, m_numPointsInGesture, m_paramterIgnoreRotation, false);
vec2 startv = dollar::getStartVector(points, m_numPointsInGesture/RATIO_START_VECTOR);
std::vector<vec2> vector = normalyse(points);
// Keep maximum 5 results ...
float bestDistance[m_nbResult];
int32_t indexOfBestMatch[m_nbResult];
for (size_t iii=0; iii<m_nbResult; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii]->getName() << "'");
ememory::SharedPtr<GestureN> gesture = m_gestures[iii];
for (size_t jjj=0; jjj<gesture->getEngineSize(); ++jjj) {
if (gesture->getEnginePath(jjj).size() == 0) {
DOLLAR_ERROR("Reference path with no Value");
continue;
}
// strokes start in the same direction
if(angleBetweenUnitVectors(startv, gesture->getEngineStartVector(jjj)) > ANGLE_THRESHOLD_START_PROCESSING) {
continue;
}
float distance = MAX_FLOAT;
// for Protractor
if (m_protractor == true) {
distance = optimalCosineDistance(vector, gesture->getEngineVector(jjj));
} else {
// Golden Section Search (original $N)
distance = distanceAtBestAngle(points, gesture->getEnginePath(jjj));
}
for (size_t kkk=0; kkk<m_nbResult; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=m_nbResult-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "," << jjj << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
// transform distance in a % range
if (m_protractor == true) {
for (size_t iii=0; iii<m_nbResult; ++iii) {
if (-1 != indexOfBestMatch[0]) {
float score = MAX_FLOAT;;
if (bestDistance[iii] != 0.0) {
score = 1.0f / bestDistance[iii];
}
res.addValue(m_gestures[indexOfBestMatch[iii]]->getName(), score);
}
}
} else {
for (size_t iii=0; iii<m_nbResult; ++iii) {
if (-1 != indexOfBestMatch[0]) {
float score = 1.0 - (bestDistance[iii] / HALF_DIAGONAL);
res.addValue(m_gestures[indexOfBestMatch[iii]]->getName(), score);
}
}
}
return res;
}

45
dollar/EngineN.h Normal file
View File

@ -0,0 +1,45 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <etk/math/Vector2D.h>
#include <limits>
#include <iostream>
#include <dollar/Results.h>
#include <string>
#include <dollar/Gesture.h>
#include <dollar/Engine.h>
#include <dollar/GestureN.h>
namespace dollar {
class EngineN : public dollar::Engine {
private:
bool m_protractor;
protected:
float m_angleRange;
bool m_paramterIgnoreRotation; //!< Ignore the start rotation of the gesture
public:
void setRotationInvariance(bool _ignoreRotation);
protected:
size_t m_numPointsInGesture; //!< Number of point in a gesture to recognise patern ...
public:
void setNumberPointInGesture(size_t _value);
size_t getNumberPointInGesture();
protected:
std::vector<ememory::SharedPtr<dollar::GestureN>> m_gestures; //!< List of all loaded gesture in the engine
public:
EngineN(bool _protractor);
dollar::Results recognize2(const std::vector<std::vector<vec2>>& _points) override;
bool loadGesture(const std::string& _filename) override;
void addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) override;
protected:
float distanceAtBestAngle(const std::vector<vec2>& _points, const std::vector<vec2>& _reference);
float optimalCosineDistance(const std::vector<vec2>& _vect1, const std::vector<vec2>& _vect2);
};
}

178
dollar/EngineP.cpp Normal file
View File

@ -0,0 +1,178 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/EngineP.h>
#include <dollar/debug.h>
#include <dollar/Rectangle.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <math.h>
#include <algorithm>
#define MAX_FLOAT std::numeric_limits<float>::max()
dollar::EngineP::EngineP():
m_scaleKeepRatio(false),
m_numPointsInGesture(128) {
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
}
void dollar::EngineP::setNumberPointInGesture(size_t _value) {
if (_value == m_numPointsInGesture) {
return;
}
m_numPointsInGesture = _value;
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
for (auto &it: m_gestures) {
if (it == nullptr) {
continue;
}
it->configure(m_numPointsInGesture);
}
}
size_t dollar::EngineP::getNumberPointInGesture() {
return m_numPointsInGesture;
}
void dollar::EngineP::setScaleKeepRatio(bool _value) {
if (_value == m_scaleKeepRatio) {
return;
}
m_scaleKeepRatio = _value;
for (auto &it: m_gestures) {
if (it == nullptr) {
continue;
}
it->configure(m_numPointsInGesture);
}
}
bool dollar::EngineP::getScaleKeepRatio() {
return m_scaleKeepRatio;
}
static float cloudDistance(const std::vector<vec2>& _points1, const std::vector<vec2>& _points2, size_t _start) {
std::vector<bool> matched;
matched.resize(_points1.size(), false);
float out = 0;
size_t iii = _start;
do {
float min = MAX_FLOAT;
size_t index = 0;
for (size_t jjj=0; jjj<matched.size(); ++jjj) {
if (matched[jjj] == true) {
continue;
}
float distance = (_points1[iii] - _points2[jjj]).length();
if (distance < min) {
min = distance;
index = jjj;
}
}
matched[index] = true;
float weight = 1.0 - ((iii - _start + _points1.size()) % _points1.size())/_points1.size();
out = out + weight* min;
iii = (iii + 1) % _points1.size();
} while(iii != _start);
return out;
}
//Greedy-Cloud-Match
static float calculateBestDistance(const std::vector<vec2>& _points, const std::vector<vec2>& _reference) {
float out = MAX_FLOAT;
float si = 0.5f;
float step = pow(_points.size(), si-1);
if (step < 1) {
// DOLLAR_ERROR(" step is too small ... " << step);
step = 1.0f;
}
for (size_t iii=0; iii<_points.size(); iii+=int32_t(step)) {
float d1 = cloudDistance(_points, _reference, iii);
float d2 = cloudDistance(_reference, _points, iii);
out = std::min(out, std::min(d1,d2));
}
return out; // Distance to the nearest point must be < 2.0 (maximum distance visible)
}
bool dollar::EngineP::loadGesture(const std::string& _filename) {
ememory::SharedPtr<dollar::Gesture> ref = ememory::makeShared<dollar::GestureP>();
DOLLAR_DEBUG("Load Gesture: " << _filename);
if (ref->load(_filename) == true) {
addGesture(ref);
return true;
}
return false;
}
void dollar::EngineP::addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) {
ememory::SharedPtr<dollar::GestureP> gest = ememory::dynamicPointerCast<dollar::GestureP>(_gesture);
if (gest != nullptr) {
gest->configure(m_numPointsInGesture);
m_gestures.push_back(gest);
}
}
dollar::Results dollar::EngineP::recognize2(const std::vector<std::vector<vec2>>& _strokes) {
std::vector<vec2> points = dollar::combineStrokes(_strokes);
points = dollar::normalizePath(points, m_numPointsInGesture, false, m_scaleKeepRatio);
// Keep maximum 5 results ...
float bestDistance[m_nbResult];
int32_t indexOfBestMatch[m_nbResult];
for (size_t iii=0; iii<m_nbResult; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii]->getName() << "'");
ememory::SharedPtr<GestureP> gesture = m_gestures[iii];
if (gesture->getPath().size() == 0) {
DOLLAR_ERROR("Reference path with no Value");
continue;
}
float distance = MAX_FLOAT;
distance = calculateBestDistance(points, gesture->getPath());
for (size_t kkk=0; kkk<m_nbResult; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=m_nbResult-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
for (size_t iii=0; iii<m_nbResult; ++iii) {
if (-1 != indexOfBestMatch[iii]) {
//float score = std::max((2.0 - bestDistance[iii])/2.0, 0.0);
float score = bestDistance[iii];
res.addValue(m_gestures[indexOfBestMatch[iii]]->getName(), score);
}
}
return res;
}

39
dollar/EngineP.h Normal file
View File

@ -0,0 +1,39 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <etk/math/Vector2D.h>
#include <limits>
#include <iostream>
#include <dollar/Results.h>
#include <string>
#include <dollar/Engine.h>
#include <dollar/GestureP.h>
namespace dollar {
class EngineP : public dollar::Engine {
protected:
bool m_scaleKeepRatio; // when rescale the path, keep the aspect ration for processing
public:
void setScaleKeepRatio(bool _value);
bool getScaleKeepRatio();
protected:
size_t m_numPointsInGesture; //!< Number of point in a gesture to recognise patern ...
public:
void setNumberPointInGesture(size_t _value);
size_t getNumberPointInGesture();
protected:
std::vector<ememory::SharedPtr<dollar::GestureP>> m_gestures; //!< List of all loaded gesture in the engine
public:
EngineP();
dollar::Results recognize2(const std::vector<std::vector<vec2>>& _paths) override;
bool loadGesture(const std::string& _filename) override;
void addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) override;
};
}

282
dollar/EnginePPlus.cpp Normal file
View File

@ -0,0 +1,282 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/EnginePPlus.h>
#include <dollar/debug.h>
#include <dollar/Rectangle.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <math.h>
#include <algorithm>
#define MAX_FLOAT std::numeric_limits<float>::max()
dollar::EnginePPlus::EnginePPlus():
m_PPlusDistance(0.10f),
m_PPlusExcludeDistance(0.2*0.2),
m_scaleKeepRatio(false) {
}
void dollar::EnginePPlus::setPPlusDistance(float _value) {
if (_value*_value == m_PPlusDistance) {
return;
}
m_PPlusDistance = _value*_value;
for (auto &it: m_gestures) {
if (it == nullptr) {
continue;
}
it->configure(m_PPlusDistance, m_scaleKeepRatio);
}
}
float dollar::EnginePPlus::getPPlusDistance() {
return std::sqrt(m_PPlusDistance);
}
void dollar::EnginePPlus::setPPlusExcludeDistance(float _value) {
if (_value == m_PPlusExcludeDistance) {
return;
}
m_PPlusExcludeDistance = _value;
}
float dollar::EnginePPlus::getPPlusExcludeDistance() {
return m_PPlusExcludeDistance;
}
void dollar::EnginePPlus::setScaleKeepRatio(bool _value) {
if (_value == m_scaleKeepRatio) {
return;
}
m_scaleKeepRatio = _value;
for (auto &it: m_gestures) {
if (it == nullptr) {
continue;
}
it->configure(m_PPlusDistance, m_scaleKeepRatio);
}
}
bool dollar::EnginePPlus::getScaleKeepRatio() {
return m_scaleKeepRatio;
}
float dollar::EnginePPlus::calculatePPlusDistance(const std::vector<vec2>& _points,
const std::vector<vec2>& _reference,
std::vector<std::pair<int32_t, int32_t>>& _dataDebug) {
std::vector<float> distance; // note: use square distance (faster, we does not use std::sqrt())
distance.resize(_points.size(), MAX_FLOAT);
// point Id that is link on the reference.
std::vector<int32_t> usedId;
usedId.resize(_reference.size(), -1);
for (int32_t iii=0; iii<int32_t(_points.size()); iii++) {
if (distance[iii] < 100.0) {
continue;
}
float bestDistance = MAX_FLOAT;
int32_t kkkBest = -1;
for (size_t kkk=0; kkk<_reference.size(); ++kkk) {
float dist = (_points[iii]-_reference[kkk]).length2();
if (usedId[kkk] != -1) {
if (dist < distance[usedId[kkk]]) {
if (dist < bestDistance) {
bestDistance = dist;
kkkBest = kkk;
}
}
} else {
if (dist < bestDistance) {
bestDistance = dist;
kkkBest = kkk;
}
}
}
if (kkkBest != -1) {
// reject the distance ... if too big ...
if (bestDistance <= m_PPlusExcludeDistance) {
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;
}
}
}
}
double fullDistance = 0;
int32_t nbTestNotUsed = 0;
int32_t nbReferenceNotUsed = 0;
// now we count the full distance use and the number of local gesture not use
for (auto &it : distance) {
if (it < 100.0) {
fullDistance += it;
} else {
nbTestNotUsed++;
}
}
// we count the number of point in the gesture reference not used:
for (auto &it : usedId) {
if (it == -1) {
nbReferenceNotUsed++;
}
}
// now we add panality:
fullDistance += float(nbTestNotUsed)* 0.1f;
fullDistance += float(nbReferenceNotUsed)* 0.1f;
for (size_t kkk=0; kkk<usedId.size(); ++kkk) {
if (usedId[kkk] != -1) {
_dataDebug.push_back(std::make_pair(usedId[kkk], kkk));
}
}
DOLLAR_DEBUG("test distance : " << fullDistance << " nbTestNotUsed=" << nbTestNotUsed << " nbReferenceNotUsed=" << nbReferenceNotUsed);
return fullDistance;
}
bool dollar::EnginePPlus::loadGesture(const std::string& _filename) {
ememory::SharedPtr<dollar::Gesture> ref = ememory::makeShared<dollar::GesturePPlus>();
DOLLAR_DEBUG("Load Gesture: " << _filename);
if (ref->load(_filename) == true) {
addGesture(ref);
return true;
}
return false;
}
void dollar::EnginePPlus::addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) {
ememory::SharedPtr<dollar::GesturePPlus> gest = ememory::dynamicPointerCast<dollar::GesturePPlus>(_gesture);
if (gest != nullptr) {
gest->configure(m_PPlusDistance, m_scaleKeepRatio);
m_gestures.push_back(gest);
}
}
static void storeSVG(const std::string& _fileName,
const ememory::SharedPtr<dollar::GesturePPlus>& _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 : dollar::scaleToOne(_gesture->getPath(), _keepAspectRatio)) {
data += " <polyline fill=\"none\" stroke=\"black\" stroke-opacity=\"0.8\" stroke-width=\"2\"\n";
data += " points=\"";
bool first = true;
for (auto& itPoints : itLines) {
if (first == false) {
data += " ";
}
first = false;
data += etk::to_string(itPoints.x()*100.0f) + "," + etk::to_string((1.0-itPoints.y())*100.0f);
}
data += "\"\n";
data += " />\n";
}
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;
for (auto& itPoints : itLines) {
if (first == false) {
data += " ";
}
first = false;
data += etk::to_string(itPoints.x()*100.0f) + "," + etk::to_string((1.0-itPoints.y())*100.0f);
}
data += "\"\n";
data += " />\n";
}
std::vector<vec2> refListPoint = _gesture->getEnginePoints();
for (auto &it : refListPoint) {
data += " <circle fill=\"red\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string((1.0-it.y())*100.0f) + "\" r=\"0.6\"/>\n";
}
std::vector<vec2> testListPoint = _points;
for (auto &it : testListPoint) {
data += " <circle fill=\"orange\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string((1.0-it.y())*100.0f) + "\" r=\"0.6\"/>\n";
}
for (auto &it : _links) {
data += " <polyline fill=\"none\" stroke=\"blue\" stroke-opacity=\"0.8\" stroke-width=\"0.5\"\n";
data += " points=\"";
data += etk::to_string(refListPoint[it.second].x()*100.0f) + "," + etk::to_string((1.0-refListPoint[it.second].y())*100.0f);
data += " ";
data += etk::to_string(testListPoint[it.first].x()*100.0f) + "," + etk::to_string((1.0-testListPoint[it.first].y())*100.0f);
data += "\"\n";
data += " />\n";
}
data += "</svg>\n";
etk::FSNodeWriteAllData(_fileName, data);
}
dollar::Results dollar::EnginePPlus::recognize2(const std::vector<std::vector<vec2>>& _strokes) {
std::vector<vec2> points = dollar::normalizePathToPoints(_strokes, m_PPlusDistance, m_scaleKeepRatio);
// Keep maximum 5 results ...
float bestDistance[m_nbResult];
int32_t indexOfBestMatch[m_nbResult];
for (size_t iii=0; iii<m_nbResult; ++iii) {
bestDistance[iii] = MAX_FLOAT;
indexOfBestMatch[iii] = -1;
}
// for each multistroke
for (size_t iii=0; iii<m_gestures.size(); ++iii) {
DOLLAR_DEBUG("[" << iii << "] '" << m_gestures[iii]->getName() << "'");
ememory::SharedPtr<GesturePPlus> gesture = m_gestures[iii];
if (gesture->getEnginePoints().size() == 0) {
//DOLLAR_ERROR("Reference path with no Value");
continue;
}
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, m_scaleKeepRatio);
for (size_t kkk=0; kkk<m_nbResult; ++kkk) {
if (distance < bestDistance[kkk]) {
if (kkk == 0) {
DOLLAR_DEBUG("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
if (indexOfBestMatch[kkk] != int64_t(iii)) {
for (int32_t rrr=m_nbResult-1; rrr>int32_t(kkk); --rrr) {
bestDistance[rrr] = bestDistance[rrr-1];
indexOfBestMatch[rrr] = indexOfBestMatch[rrr-1];
}
indexOfBestMatch[kkk] = iii;
}
bestDistance[kkk] = distance;
break;
} else {
if (kkk == 0) {
DOLLAR_VERBOSE("[" << iii << "] d=" << distance << " < bd=" << bestDistance << " ");
}
}
}
}
// Check if we have match ...
if (-1 == indexOfBestMatch[0]) {
DOLLAR_WARNING("Couldn't find a good match.");
return Results();
}
Results res;
for (size_t iii=0; iii<m_nbResult; ++iii) {
if (-1 != indexOfBestMatch[iii]) {
//float score = std::max((2.0 - bestDistance[iii])/2.0, 0.0);
float score = bestDistance[iii];
res.addValue(m_gestures[indexOfBestMatch[iii]]->getName(), score);
}
}
return res;
}

48
dollar/EnginePPlus.h Normal file
View File

@ -0,0 +1,48 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <etk/math/Vector2D.h>
#include <limits>
#include <iostream>
#include <dollar/Results.h>
#include <string>
#include <dollar/Engine.h>
#include <dollar/GesturePPlus.h>
namespace dollar {
class EnginePPlus : public dollar::Engine {
protected:
float m_PPlusDistance;
public:
void setPPlusDistance(float _value);
float getPPlusDistance();
protected:
float m_PPlusExcludeDistance;
public:
void setPPlusExcludeDistance(float _value);
float getPPlusExcludeDistance();
protected:
bool m_scaleKeepRatio; // when rescale the path, keep the aspect ration for processing
public:
void setScaleKeepRatio(bool _value);
bool getScaleKeepRatio();
protected:
std::vector<ememory::SharedPtr<dollar::GesturePPlus>> m_gestures; //!< List of all loaded gesture in the engine
public:
EnginePPlus();
dollar::Results recognize2(const std::vector<std::vector<vec2>>& _paths) override;
bool loadGesture(const std::string& _filename) override;
void addGesture(ememory::SharedPtr<dollar::Gesture> _gesture) override;
protected:
float calculatePPlusDistance(const std::vector<vec2>& _points,
const std::vector<vec2>& _reference,
std::vector<std::pair<int32_t, int32_t>>& _dataDebug);
};
}

View File

@ -167,9 +167,11 @@ void dollar::Gesture::storeSVG(const std::string& _fileName, bool _storeDot) {
data += " />\n";
}
if (_storeDot == true) {
/*
for (auto &it : m_enginePoints) {
data += " <circle fill=\"red\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string((1.0-it.y())*100.0f) + "\" r=\"0.6\"/>\n";
}
*/
}
data += "</svg>\n";
etk::FSNodeWriteAllData(_fileName, data);
@ -179,33 +181,4 @@ void dollar::Gesture::set(const std::string& _name, uint32_t _subId, std::vector
m_name = _name;
m_subId = _subId;
m_path = _path;
m_enginePath.clear();
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, 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, _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);
// 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, _keepAspectRatio);
m_enginePath.push_back(val);
// calculate start vector:
vec2 startv = dollar::getStartVector(val, _startAngleIndex);
m_engineStartV.push_back(startv);
m_engineVector.push_back(dollar::normalyse(val));
}
}

View File

@ -7,6 +7,7 @@
#pragma once
#include <string>
#include <etk/math/Vector2D.h>
#include <ememory/memory.h>
namespace dollar {
class Gesture {
@ -14,9 +15,9 @@ 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();
virtual ~Gesture() = default;
bool load(const std::string& _filename);
bool store(const std::string& _filename);
void set(const std::string& _name, uint32_t _subId, std::vector<std::vector<vec2>> _path);
@ -32,35 +33,6 @@ namespace dollar {
const uint32_t& getId() {
return m_subId;
}
const std::vector<std::vector<vec2>>& getPath() const {
return m_path2;
}
std::vector<std::vector<vec2>>& getPath() {
return m_path2;
}
protected:
std::vector<std::vector<vec2>> m_enginePath; // Singulized path with every conbinaison
std::vector<std::vector<vec2>> m_engineVector;
std::vector<vec2> m_engineStartV;
std::vector<vec2> m_enginePoints;
public:
// Configure the reference gesture for recognition...
void configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance, bool _keepAspectRatio);
size_t getEngineSize() const {
return m_enginePath.size();
}
const std::vector<vec2>& getEnginePath(size_t _id) const {
return m_enginePath[_id];
}
const std::vector<vec2>& getEngineVector(size_t _id) const {
return m_engineVector[_id];
}
const vec2& getEngineStartVector(size_t _id) const {
return m_engineStartV[_id];
}
const std::vector<vec2>& getEnginePoints() const {
return m_enginePoints;
}
};
std::vector<std::vector<vec2>> loadPoints(const std::string& _fileName, std::string* _label=nullptr, std::string* _type=nullptr);
}

39
dollar/GestureN.cpp Normal file
View File

@ -0,0 +1,39 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/debug.h>
#include <dollar/GestureN.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <ejson/ejson.h>
#include <esvg/esvg.h>
#include <etk/stdTools.h>
dollar::GestureN::GestureN() {
}
void dollar::GestureN::configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, bool _keepAspectRatio) {
m_enginePath.clear();
m_engineVector.clear();
m_engineStartV.clear();
m_path2 = dollar::scaleToOne(m_path, _keepAspectRatio);
// for debug only
//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, _keepAspectRatio);
m_enginePath.push_back(val);
// calculate start vector:
vec2 startv = dollar::getStartVector(val, _startAngleIndex);
m_engineStartV.push_back(startv);
m_engineVector.push_back(dollar::normalyse(val));
}
}

46
dollar/GestureN.h Normal file
View File

@ -0,0 +1,46 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <string>
#include <etk/math/Vector2D.h>
#include <dollar/Gesture.h>
namespace dollar {
class GestureN : public dollar::Gesture {
protected:
std::vector<std::vector<vec2>> m_path2;
public:
GestureN();
public:
const std::vector<std::vector<vec2>>& getPath() const {
return m_path2;
}
std::vector<std::vector<vec2>>& getPath() {
return m_path2;
}
protected:
std::vector<std::vector<vec2>> m_enginePath; // Singulized path with every conbinaison
std::vector<std::vector<vec2>> m_engineVector;
std::vector<vec2> m_engineStartV;
public:
// Configure the reference gesture for recognition...
void configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, bool _keepAspectRatio=false);
size_t getEngineSize() const {
return m_enginePath.size();
}
const std::vector<vec2>& getEnginePath(size_t _id) const {
return m_enginePath[_id];
}
const std::vector<vec2>& getEngineVector(size_t _id) const {
return m_engineVector[_id];
}
const vec2& getEngineStartVector(size_t _id) const {
return m_engineStartV[_id];
}
};
}

25
dollar/GestureP.cpp Normal file
View File

@ -0,0 +1,25 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/debug.h>
#include <dollar/GestureP.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <ejson/ejson.h>
#include <esvg/esvg.h>
#include <etk/stdTools.h>
dollar::GestureP::GestureP() {
}
void dollar::GestureP::configure(size_t _nbSample) {
m_enginePoints.clear();
// Generates dots:
std::vector<vec2> points = dollar::combineStrokes(m_path);
m_enginePoints = dollar::normalizePath(points, _nbSample, false, false);
}

31
dollar/GestureP.h Normal file
View File

@ -0,0 +1,31 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <string>
#include <etk/math/Vector2D.h>
#include <dollar/Gesture.h>
namespace dollar {
class GestureP : public dollar::Gesture {
public:
GestureP();
protected:
std::vector<vec2> m_enginePoints;
public:
// Configure the reference gesture for recognition...
void configure(size_t _nbSample);
const std::vector<vec2>& getEnginePoints() const {
return m_enginePoints;
}
const std::vector<vec2>& getPath() const {
return m_enginePoints;
}
std::vector<vec2>& getPath() {
return m_enginePoints;
}
};
}

24
dollar/GesturePPlus.cpp Normal file
View File

@ -0,0 +1,24 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <dollar/debug.h>
#include <dollar/GesturePPlus.h>
#include <dollar/tools.h>
#include <etk/os/FSNode.h>
#include <ejson/ejson.h>
#include <esvg/esvg.h>
#include <etk/stdTools.h>
dollar::GesturePPlus::GesturePPlus() {
}
void dollar::GesturePPlus::configure(float _distance, bool _keepAspectRatio) {
m_enginePoints.clear();
// Generates dots:
m_enginePoints = dollar::normalizePathToPoints(m_path, _distance, _keepAspectRatio);
DOLLAR_VERBOSE("create " << m_enginePoints.size() << " points");
}

31
dollar/GesturePPlus.h Normal file
View File

@ -0,0 +1,31 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#pragma once
#include <string>
#include <etk/math/Vector2D.h>
#include <dollar/Gesture.h>
namespace dollar {
class GesturePPlus : public dollar::Gesture {
public:
GesturePPlus();
protected:
std::vector<vec2> m_enginePoints;
public:
// Configure the reference gesture for recognition...
void configure(float _distance, bool _keepAspectRatio);
const std::vector<vec2>& getEnginePoints() const {
return m_enginePoints;
}
const std::vector<std::vector<vec2>>& getPath() const {
return m_path;
}
std::vector<std::vector<vec2>>& getPath() {
return m_path;
}
};
}

View File

@ -31,7 +31,13 @@ def create(target, module_name):
my_module.add_src_file([
'dollar/debug.cpp',
'dollar/Engine.cpp',
'dollar/EngineN.cpp',
'dollar/EngineP.cpp',
'dollar/EnginePPlus.cpp',
'dollar/Gesture.cpp',
'dollar/GestureN.cpp',
'dollar/GestureP.cpp',
'dollar/GesturePPlus.cpp',
'dollar/Results.cpp',
'dollar/tools.cpp',
'dollar/Rectangle.cpp'
@ -51,6 +57,7 @@ def create(target, module_name):
'etk',
'ejson',
'esvg',
'ememory',
])
return my_module

View File

@ -21,18 +21,20 @@ int main(int _argc, const char *_argv[]) {
}
TEST(TestAll, plop) {
/*
dollar::Gesture gest;
gest.set("test", 55, dollar::loadPoints("DATA:test/P.json"));
gest.configure(0.1, 64, false, 0.1f);
*/
}
/*
* single-stroke gesture recognition
*/
TEST(TestAll, singleStroke_normal) {
dollar::Engine reco;
reco.loadPath("DATA:figure");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/arrow.json"), "$N");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$N");
reco->loadPath("DATA:figure");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/arrow.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");
@ -46,9 +48,9 @@ TEST(TestAll, singleStroke_normal) {
}
TEST(TestAll, singleStroke_protractor) {
dollar::Engine reco;
reco.loadPath("DATA:figure");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/arrow.json"), "$N-protractor");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$N");
reco->loadPath("DATA:figure");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/arrow.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");
@ -66,9 +68,9 @@ TEST(TestAll, singleStroke_protractor) {
* multi-stroke gesture recognition
*/
TEST(TestAll, multiStroke_normal) {
dollar::Engine reco;
reco.loadPath("DATA:text");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$N");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$N");
reco->loadPath("DATA:text");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/P.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");
@ -82,9 +84,9 @@ TEST(TestAll, multiStroke_normal) {
}
TEST(TestAll, multiStroke_protractor) {
dollar::Engine reco;
reco.loadPath("DATA:text");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$N-protractor");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$N-protractor");
reco->loadPath("DATA:text");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/P.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");
@ -100,9 +102,9 @@ TEST(TestAll, multiStroke_protractor) {
* $P algorithms
*/
TEST(TestAll, multiStroke_point) {
dollar::Engine reco;
reco.loadPath("DATA:text");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$P");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$P");
reco->loadPath("DATA:text");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/P.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");
@ -118,9 +120,9 @@ TEST(TestAll, multiStroke_point) {
* $P+ algorithms
*/
TEST(TestAll, multiStroke_pointPlus) {
dollar::Engine reco;
reco.loadPath("DATA:text");
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$P+");
ememory::SharedPtr<dollar::Engine> reco = dollar::createEngine("$P+");
reco->loadPath("DATA:text");
dollar::Results res = reco->recognize(dollar::loadPoints("DATA:test/P.json"));
EXPECT_EQ(res.haveMath(), true);
if (res.haveMath() == false) {
TEST_INFO(" Recognise noting ...");

View File

@ -16,23 +16,36 @@
void usage(const std::string& _progName) {
TEST_PRINT("usage:");
TEST_PRINT(" " << _progName << " [option] corpus_path");
TEST_PRINT(" ");
TEST_PRINT(" [option]");
TEST_PRINT(" -h --help Display this help");
TEST_PRINT(" --keep_ratio Keep aspect ratio for the form recognition");
TEST_PRINT(" --dist-check=flaot distance between points in the system recognition");
TEST_PRINT(" --dist-excl=flaot distance to exclude points in the check algo");
TEST_PRINT(" --group-size=flaot size of the distance between point to stop grouping in one form");
TEST_PRINT(" --mode=string mode of reco");
TEST_PRINT(" ");
TEST_PRINT(" parameters (must be here)");
TEST_PRINT(" corpus_path Path of the corpus files");
TEST_PRINT(" ");
TEST_PRINT(" example:");
}
bool testCorpus(const std::string& _srcCorpus);
static bool keepAspectRatio = false;
static float distanceReference; // distance of the gesture reference [0.02, 0.3]
static float distanceCheck; // distance of the test points [0.02, 0.3]
static float distanceExclude; // distance of the exclusion point [0.1, 1.0]
static float distanceReference = 0.1; // distance of the gesture reference [0.02, 0.3]
static float distanceCheck = 0.1; // distance of the test points [0.02, 0.3]
static float distanceExclude = 0.2; // distance of the exclusion point [0.1, 1.0]
static float distanceGroupLimiting = 1.0; // square distance of the grouping genereation
static std::string modeReco = "P+";
setScaleKeepRatio(keepAspectRatio);
setScaleKeepRatio();
setPPlusDistance(distanceReference); // to generate reference gesture
setPPlusDistance(distanceCheck); // to generate test gesture
setPPlusExcludeDistance(distanceExclude);
int main(int _argc, const char *_argv[]) {
// init etk log system and file interface:
etk::init(_argc, _argv);
@ -52,16 +65,33 @@ int main(int _argc, const char *_argv[]) {
if (etk::start_with(arg,"--dist-ref=") == true) {
std::string val(&arg[11]);
distanceReference = etk::string_to_float(val);
TEST_PRINT("configure distanceReference=" << distanceReference);
continue;
}
// TODO: Must not be different ...
if (etk::start_with(arg,"--dist-check=") == true) {
std::string val(&arg[13]);
distanceCheck = etk::string_to_float(val);
distanceReference = distanceCheck;
TEST_PRINT("configure distanceCheck=" << distanceCheck);
continue;
}
if (etk::start_with(arg,"--dist-excl=") == true) {
std::string val(&arg[12]);
distanceExclude = etk::string_to_float(val);
TEST_PRINT("configure distanceExclude=" << distanceExclude);
continue;
}
if (etk::start_with(arg,"--group-size=") == true) {
std::string val(&arg[13]);
distanceGroupLimiting = etk::string_to_float(val);
TEST_PRINT("configure distanceGroupLimiting=" << distanceGroupLimiting);
continue;
}
if (etk::start_with(arg,"--mode=") == true) {
std::string val(&arg[7]);
modeReco = val;
TEST_PRINT("configure modeReco=" << modeReco);
continue;
}
if ( arg[0] == '-'
@ -148,10 +178,7 @@ bool testCorpus(const std::string& _srcCorpus) {
}
//listOfElementInCorpus.clear();
//listOfElementInCorpus.push_back("slash");
// Value to stop grouping in the same element ...
float groupSize = 1.0;
groupSize = 1.0;
bool keepAspectRatio = false;
TEST_PRINT(" will done for: " << listOfElementInCorpus);
int32_t nbElementGenerated = 0;
@ -195,9 +222,11 @@ bool keepAspectRatio = false;
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, keepAspectRatio);
gest.configure(10, 8, false, distanceReference, keepAspectRatio);
dollar::Engine reco;
reco.setScaleKeepRatio(keepAspectRatio);
reco.setPPlusDistance(distanceCheck);
reco.setPPlusExcludeDistance(distanceExclude);
reco.addGesture(gest);
std::vector<std::string> path = etk::split(fileFiltered[iii], '/');
std::string filename = path[path.size()-1];
@ -208,7 +237,7 @@ bool keepAspectRatio = false;
continue;
}
listPoints = dollar::loadPoints(fileFiltered[jjj]);
dollar::Results res = reco.recognize(listPoints, "$P+");
dollar::Results res = reco.recognize(listPoints, "$" + modeReco);
results[iii][jjj] = res.getConfidence();
results[jjj][iii] = res.getConfidence();
path = etk::split(fileFiltered[jjj], '/');
@ -227,7 +256,7 @@ bool keepAspectRatio = false;
countMinimum.resize(fileFiltered.size(), 0);
for (size_t iii=0; iii<fileFiltered.size(); ++iii) {
for (size_t jjj=0; jjj<fileFiltered.size(); ++jjj) {
if (results[iii][jjj] < groupSize) {
if (results[iii][jjj] < distanceGroupLimiting) {
countMinimum[iii] ++;
}
}
@ -256,7 +285,7 @@ bool keepAspectRatio = false;
// Order the result for the bestID ==> permit to show if it is possible to do a better case ...
std::vector<int32_t> linkIds;
for (size_t jjj=0; jjj<fileFiltered.size(); ++jjj) {
if (results[bestId][jjj] < groupSize) {
if (results[bestId][jjj] < distanceGroupLimiting) {
linkIds.push_back(jjj);
}
}
@ -287,7 +316,7 @@ bool keepAspectRatio = false;
float lastValue = 0.0;
linkIds.clear();
for (size_t jjj=0; jjj<ordered.size(); ++jjj) {
if (ordered[jjj].first < groupSize) {
if (ordered[jjj].first < distanceGroupLimiting) {
linkIds.push_back(ordered[jjj].second);
} else {
// auto find a separation in the group ...