[DEV] separates algos to simplify codes
This commit is contained in:
parent
d984faf27f
commit
a6f9f546a4
@ -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;
|
||||
}
|
@ -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
240
dollar/EngineN.cpp
Normal 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
45
dollar/EngineN.h
Normal 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
178
dollar/EngineP.cpp
Normal 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
39
dollar/EngineP.h
Normal 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
282
dollar/EnginePPlus.cpp
Normal 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
48
dollar/EnginePPlus.h
Normal 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);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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
39
dollar/GestureN.cpp
Normal 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
46
dollar/GestureN.h
Normal 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
25
dollar/GestureP.cpp
Normal 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
31
dollar/GestureP.h
Normal 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
24
dollar/GesturePPlus.cpp
Normal 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
31
dollar/GesturePPlus.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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 ...");
|
||||
|
@ -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 ...
|
||||
|
Loading…
x
Reference in New Issue
Block a user