[DEV] add algo (too slow) and try to develop an other algo named +
This commit is contained in:
parent
7f186ff963
commit
cce6474635
@ -53,7 +53,8 @@ static float pathDistance(const std::vector<vec2>& _path1, const std::vector<vec
|
||||
return (distance / _path1.size());
|
||||
}
|
||||
|
||||
dollar::Engine::Engine() {
|
||||
dollar::Engine::Engine():
|
||||
m_PPlusDistance(0.10f) {
|
||||
m_numPointsInGesture = 128;
|
||||
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
|
||||
setRotationInvariance(false);
|
||||
@ -67,7 +68,7 @@ void dollar::Engine::setNumberPointInGesture(size_t _value) {
|
||||
m_numPointsInGesture = _value;
|
||||
DOLLAR_ASSERT(m_numPointsInGesture>16, "NB element in a path must be > 16 ...");
|
||||
for (auto &it: m_gestures) {
|
||||
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation);
|
||||
it.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +102,121 @@ float dollar::Engine::distanceAtBestAngle(const std::vector<vec2>& _points, cons
|
||||
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) {
|
||||
int32_t previous = usedId[kkkBest];
|
||||
usedId[kkkBest] = iii;
|
||||
distance[iii] = bestDistance;
|
||||
if (previous != -1) {
|
||||
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 {
|
||||
// TODO : Only for debug
|
||||
nbTestNotUsed++;
|
||||
}
|
||||
}
|
||||
// TODO : Only for debug
|
||||
// we count the number of point in the gesture reference not used:
|
||||
for (auto &it : usedId) {
|
||||
if (it == -1) {
|
||||
nbReferenceNotUsed++;
|
||||
}
|
||||
}
|
||||
// TODO : Only for debug
|
||||
for (int32_t kkk=0; kkk<_reference.size(); ++kkk) {
|
||||
if (it != -1) {
|
||||
_dataDebug.push_back(std::make_pair(it, kkk));
|
||||
}
|
||||
}
|
||||
// TODO : Only for debug
|
||||
DOLLAR_INFO("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());
|
||||
@ -152,7 +268,7 @@ bool dollar::Engine::loadGesture(const std::string& _filename) {
|
||||
}
|
||||
|
||||
void dollar::Engine::addGesture(Gesture _gesture) {
|
||||
_gesture.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation);
|
||||
_gesture.configure(m_numPointsInGesture/RATIO_START_VECTOR, m_numPointsInGesture, m_paramterIgnoreRotation, m_PPlusDistance);
|
||||
m_gestures.push_back(std::move(_gesture));
|
||||
}
|
||||
|
||||
@ -164,13 +280,27 @@ dollar::Results dollar::Engine::recognize(const std::vector<vec2>& _points, cons
|
||||
#define MAX_RESULT_NUMBER (5)
|
||||
|
||||
dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>& _strokes, const std::string& _method) {
|
||||
std::vector<vec2> points = dollar::combineStrokes(_strokes);
|
||||
// Make sure we have some templates to compare this to
|
||||
// or else recognition will be impossible
|
||||
// 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"
|
||||
|| _method == "$1") {
|
||||
return recognizeN(_strokes, _method);
|
||||
} else if (_method == "$P") {
|
||||
return recognizeP(_strokes);
|
||||
} else if (_method == "$P+") {
|
||||
return recognizePPlus(_strokes);
|
||||
}
|
||||
DOLLAR_WARNING("Un-recognise methode ... '" << _method << "' supported: [$1,$N,$N-protractor,$P,$P+]" );
|
||||
return Results();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
vec2 startv = dollar::getStartVector(points, m_numPointsInGesture/RATIO_START_VECTOR);
|
||||
std::vector<vec2> vector = normalyse(points);
|
||||
@ -196,11 +326,11 @@ dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>&
|
||||
}
|
||||
float distance = MAX_FLOAT;
|
||||
// for Protractor
|
||||
if (_method=="protractor") {
|
||||
distance = Engine::optimalCosineDistance(vector, gesture.getEngineVector(jjj));
|
||||
if (_method=="$p-protractor") {
|
||||
distance = optimalCosineDistance(vector, gesture.getEngineVector(jjj));
|
||||
} else {
|
||||
// Golden Section Search (original $N)
|
||||
distance = Engine::distanceAtBestAngle(points, gesture.getEnginePath(jjj));
|
||||
distance = distanceAtBestAngle(points, gesture.getEnginePath(jjj));
|
||||
}
|
||||
for (size_t kkk=0; kkk<MAX_RESULT_NUMBER; ++kkk) {
|
||||
if (distance < bestDistance[kkk]) {
|
||||
@ -224,16 +354,14 @@ dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>&
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure we actually found a good match
|
||||
// Sometimes we don't, like when the user doesn't draw enough points
|
||||
// Check if we have match ...
|
||||
if (-1 == indexOfBestMatch[0]) {
|
||||
DOLLAR_WARNING("Couldn't find a good match.");
|
||||
return Results();
|
||||
}
|
||||
Results res;
|
||||
// Turn the distance into a percentage by dividing it by half the maximum possible distance (across the diagonal of the square we scaled everything too)
|
||||
// Distance = hwo different they are subtract that from 1 (100%) to get the similarity
|
||||
if (_method == "protractor") {
|
||||
// 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;;
|
||||
@ -254,3 +382,129 @@ dollar::Results dollar::Engine::recognize(const std::vector<std::vector<vec2>>&
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
dollar::Results dollar::Engine::recognizePPlus(const std::vector<std::vector<vec2>>& _strokes) {
|
||||
std::vector<vec2> points = dollar::normalizePathToPoints(_strokes, m_PPlusDistance);
|
||||
// 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;
|
||||
DOLLAR_INFO("[" << iii << "] " << m_gestures[iii].getName());
|
||||
std::vector<std::pair<int32_t, int32_t> dataPair;
|
||||
distance = calculatePPlusDistance(points, gesture.getEnginePoints(), dataPair);
|
||||
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;
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
namespace dollar {
|
||||
class Engine {
|
||||
protected:
|
||||
float m_PPlusDistance;
|
||||
float m_angleRange;
|
||||
bool m_paramterIgnoreRotation; //!< Ignore the start rotation of the gesture
|
||||
public:
|
||||
@ -30,12 +31,16 @@ namespace dollar {
|
||||
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="normal");
|
||||
Results recognize(const std::vector<vec2>& _points, const std::string& _method="normal");
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -124,16 +124,12 @@ void dollar::Gesture::storeJSON(const std::string& _fileName) {
|
||||
doc.store(_fileName);
|
||||
}
|
||||
|
||||
void dollar::Gesture::storeSVG(const std::string& _fileName) {
|
||||
void dollar::Gesture::storeSVG(const std::string& _fileName, bool _storeDot) {
|
||||
std::vector<std::vector<vec2>> strokes = dollar::scaleToOne(m_path);
|
||||
std::string data("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
|
||||
data += "<svg height=\"100\" width=\"100\">\n";
|
||||
for (auto &itLines : strokes) {
|
||||
data += " <polyline\n";
|
||||
data += " fill=\"none\"\n";
|
||||
data += " stroke=\"black\"\n";
|
||||
data += " stroke-opacity=\"1\"\n";
|
||||
data += " stroke-width=\"2\"\n";
|
||||
data += " <polyline fill=\"none\" stroke=\"black\" stroke-opacity=\"1\" stroke-width=\"2\"\n";
|
||||
data += " points=\"";
|
||||
bool first = true;
|
||||
for (auto& itPoints : itLines) {
|
||||
@ -146,15 +142,35 @@ void dollar::Gesture::storeSVG(const std::string& _fileName) {
|
||||
data += "\"\n";
|
||||
data += " />\n";
|
||||
}
|
||||
if (_storeDot == true) {
|
||||
for (auto &it : dollar::scaleToOne(m_enginePoints)) {
|
||||
data += " <circle fill=\"red\" cx=\"" + etk::to_string(it.x()*100.0f) + "\" cy=\"" + etk::to_string(it.y()*100.0f) + "\" r=\"0.6\"/>\n";
|
||||
}
|
||||
}
|
||||
data += "</svg>\n";
|
||||
etk::FSNodeWriteAllData(_fileName, data);
|
||||
}
|
||||
|
||||
|
||||
void dollar::Gesture::configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation) {
|
||||
void dollar::Gesture::set(const std::string& _name, uint32_t _subId, std::vector<std::vector<vec2>> _path) {
|
||||
m_name = _name;
|
||||
m_subId = _subId;
|
||||
m_path = _path;
|
||||
m_enginePath.clear();
|
||||
m_engineVector.clear();
|
||||
m_engineStartV.clear();
|
||||
m_enginePoints.clear();
|
||||
}
|
||||
|
||||
void dollar::Gesture::configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance) {
|
||||
m_enginePath.clear();
|
||||
m_engineVector.clear();
|
||||
m_engineStartV.clear();
|
||||
m_enginePoints.clear();
|
||||
// Generates dots:
|
||||
m_enginePoints = dollar::normalizePathToPoints(m_path, _distance);
|
||||
DOLLAR_INFO("create " << m_enginePoints.size() << " points");
|
||||
// for debug only
|
||||
storeSVG("out/zzz_" + m_name + "_" + etk::to_string(m_subId) + ".svg", true);
|
||||
// Simplyfy paths
|
||||
std::vector<std::vector<vec2>> uniPath = dollar::makeReferenceStrokes(m_path);
|
||||
// normalize paths
|
||||
|
@ -18,11 +18,12 @@ namespace dollar {
|
||||
Gesture();
|
||||
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);
|
||||
protected:
|
||||
bool loadJSON(const std::string& _filename);
|
||||
bool loadSVG(const std::string& _filename);
|
||||
void storeJSON(const std::string& _filename);
|
||||
void storeSVG(const std::string& _filename);
|
||||
void storeSVG(const std::string& _filename, bool _storeDot=false);
|
||||
public:
|
||||
const std::string& getName() {
|
||||
return m_name;
|
||||
@ -37,9 +38,10 @@ namespace dollar {
|
||||
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);
|
||||
void configure(float _startAngleIndex, size_t _nbSample, bool _ignoreRotation, float _distance);
|
||||
size_t getEngineSize() const {
|
||||
return m_enginePath.size();
|
||||
}
|
||||
@ -52,6 +54,9 @@ namespace dollar {
|
||||
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);
|
||||
}
|
||||
|
@ -103,21 +103,18 @@ std::vector<vec2> dollar::translateBariCenterToZero(std::vector<vec2> _points) {
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::vector<vec2> dollar::resample(std::vector<vec2> _points, int32_t _nbPoints) {
|
||||
static std::vector<vec2> discretize(std::vector<vec2> _points, float _interval) {
|
||||
std::vector<vec2> out;
|
||||
// calculate the interval between every points ...
|
||||
float interval = pathLength(_points) / (_nbPoints - 1);
|
||||
float distance = 0.0;
|
||||
// same first point ==> no change
|
||||
out.push_back(_points.front());
|
||||
float distance = 0.0f;
|
||||
// For all other point we have to resample elements
|
||||
for (size_t iii=1; iii<_points.size(); ++iii) {
|
||||
vec2 currentPoint = _points[iii];
|
||||
vec2 previousPoint = _points[iii-1];
|
||||
float tmpDist = (currentPoint-previousPoint).length();
|
||||
if ((distance + tmpDist) >= interval) {
|
||||
vec2 point = previousPoint + (currentPoint - previousPoint) * ((interval - distance) / tmpDist);
|
||||
if ((distance + tmpDist) >= _interval) {
|
||||
vec2 point = previousPoint + (currentPoint - previousPoint) * ((_interval - distance) / tmpDist);
|
||||
out.push_back(point);
|
||||
_points.insert(_points.begin() + iii, point);
|
||||
distance = 0.0;
|
||||
@ -125,6 +122,14 @@ std::vector<vec2> dollar::resample(std::vector<vec2> _points, int32_t _nbPoints)
|
||||
distance += tmpDist;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<vec2> dollar::resample(std::vector<vec2> _points, int32_t _nbPoints) {
|
||||
std::vector<vec2> out;
|
||||
// calculate the interval between every points ...
|
||||
float interval = pathLength(_points) / (_nbPoints - 1);
|
||||
out = discretize(_points, interval);
|
||||
// somtimes we fall a rounding-error short of adding the last point, so add it if so
|
||||
if (int64_t(out.size()) == (_nbPoints - 1)) {
|
||||
out.push_back(_points.back());
|
||||
@ -205,5 +210,29 @@ std::vector<vec2> dollar::normalizePath(std::vector<vec2> _points, size_t _nbSam
|
||||
_points = rotateToZero(_points);
|
||||
}
|
||||
_points = scaleToOne(_points);
|
||||
return translateBariCenterToZero(_points);;
|
||||
return translateBariCenterToZero(_points);
|
||||
}
|
||||
|
||||
|
||||
std::vector<vec2> dollar::normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance) {
|
||||
// Scale point to (0.0,0.0) position and (1.0,1.0) size
|
||||
_points = dollar::scaleToOne(_points);
|
||||
std::vector<vec2> out;
|
||||
for (auto &it : _points) {
|
||||
if (it.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (it.size() == 1) {
|
||||
out.push_back(it[0]);
|
||||
continue;
|
||||
}
|
||||
std::vector<vec2> tmp = discretize(it, _distance);
|
||||
for (auto &pointIt : tmp) {
|
||||
out.push_back(pointIt);
|
||||
}
|
||||
if (tmp[tmp.size()-1] != it[it.size()-1]) {
|
||||
out.push_back(it[it.size()-1]);
|
||||
}
|
||||
}
|
||||
return translateBariCenterToZero(out);
|
||||
}
|
@ -101,6 +101,15 @@ namespace dollar {
|
||||
* @return new list of points
|
||||
*/
|
||||
std::vector<vec2> normalizePath(std::vector<vec2> _points, size_t _nbSample, bool _ignoreRotation);
|
||||
|
||||
/**
|
||||
* @brief Transform the path to be comparable, resample the path with a specific number of sample, and limit size at 1.0 square center around 0
|
||||
* @note The difference with @ref normalizePath is thet we do not combinethe path together, that permit to not have unneded point between strokes...
|
||||
* @param[in] _points List of points in the path
|
||||
* @param[in] _distance Distance between points
|
||||
* @return new list of points
|
||||
*/
|
||||
std::vector<vec2> normalizePathToPoints(std::vector<std::vector<vec2>> _points, float _distance);
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,13 +20,19 @@ int main(int _argc, const char *_argv[]) {
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
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"), "normal");
|
||||
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/Arrow.json"), "$N");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
@ -42,7 +48,7 @@ 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"), "protractor");
|
||||
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/Arrow.json"), "$N-protractor");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
@ -62,7 +68,7 @@ TEST(TestAll, singleStroke_protractor) {
|
||||
TEST(TestAll, multiStroke_normal) {
|
||||
dollar::Engine reco;
|
||||
reco.loadPath("DATA:text");
|
||||
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "normal");
|
||||
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$N");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
@ -78,7 +84,43 @@ 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"), "protractor");
|
||||
dollar::Results res = reco.recognize(dollar::loadPoints("DATA:test/P.json"), "$N-protractor");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
return;
|
||||
}
|
||||
EXPECT_EQ(res.getName(), "P");
|
||||
TEST_INFO("Results");
|
||||
for (size_t iii=0; iii<res.getSize(); ++iii) {
|
||||
TEST_INFO(" - " << res.getName(iii) << " score=" << res.getConfidence(iii));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* $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");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
return;
|
||||
}
|
||||
EXPECT_EQ(res.getName(), "P");
|
||||
TEST_INFO("Results");
|
||||
for (size_t iii=0; iii<res.getSize(); ++iii) {
|
||||
TEST_INFO(" - " << res.getName(iii) << " score=" << res.getConfidence(iii));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* $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+");
|
||||
EXPECT_EQ(res.haveMath(), true);
|
||||
if (res.haveMath() == false) {
|
||||
TEST_INFO(" Recognise noting ...");
|
||||
|
Loading…
Reference in New Issue
Block a user