Logo Search packages:      
Sourcecode: zinnia version File versions

feature.cpp

//
//  Zinnia: Online hand recognition system with machine learning
//
//  $Id: feature.cpp 9 2008-09-20 12:11:02Z taku-ku $;
//
//  Copyright(C) 2008 Taku Kudo <taku@chasen.org>
//
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include "zinnia.h"
#include "feature.h"

namespace zinnia {

  namespace {

    static const size_t kMaxCharacterSize = 50;

    struct FeatureNodeCmp {
      bool operator()(const FeatureNode& x1, const FeatureNode &x2) {
        return x1.index < x2.index;
      }
    };

    float distance(const Node *n1, const Node *n2) {
      const float x = n1->x - n2->x;
      const float y = n1->y - n2->y;
      return std::sqrt(x * x + y * y);
    }

    float distance2(const Node *n1) {
      const float x = n1->x - 0.5;
      const float y = n1->y - 0.5;
      return std::sqrt(x * x + y * y);
    }

    float minimum_distance(const Node *first, const Node *last,
                           Node **best) {
      if (first == last) return 0.0;

      const float a = last->x - first->x;
      const float b = last->y - first->y;
      const float c = last->y * first->x - last->x * first->y;

      float max = -1.0;
      for (const Node *n = first; n != last; ++n) {
        const float dist = std::fabs((a * n->y) -(b * n->x) + c);
        if (dist > max) {
          max = dist;
          *best = const_cast<Node *>(n);
        }
      }

      return max * max /(a * a + b * b);
    }
  }

  bool Features::read(const Character &character) {
    features_.clear();
    const Node *prev = 0;

    // bias term
    {
      FeatureNode f;
      f.index = 0;
      f.value = 1.0;
      features_.push_back(f);
    }

    std::vector<std::vector<Node> > nodes(character.strokes_size());

    {
      const size_t height = character.height();
      const size_t width  = character.width();
      if (height == 0 || width == 0) return false;
      if (character.strokes_size() == 0) return false;
      for (size_t i = 0; i < character.strokes_size(); ++i) {
        const size_t ssize = character.stroke_size(i);
        if (ssize == 0) {
          return false;
        }
        nodes[i].resize(ssize);
        for (size_t j = 0; j < ssize; ++j) {
          nodes[i][j].x = 1.0 * character.x(i, j) / width;
          nodes[i][j].y = 1.0 * character.y(i, j) / height;
        }
      }
    }

    for (size_t sid = 0; sid < nodes.size(); ++sid) {
      std::vector<NodePair> node_pairs;
      const Node *first = &nodes[sid][0];
      const Node *last  = &nodes[sid][nodes[sid].size()-1];
      getVertex(first, last, 0, &node_pairs);
      makeVertexFeature(sid, &node_pairs);
      if (prev) {
        makeMoveFeature(sid, prev, first);
      }
      prev = last;
    }

    addFeature(2000000,  nodes.size());
    addFeature(2000000 + nodes.size(), 10);

    std::sort(features_.begin(), features_.end(), FeatureNodeCmp());

    {
      FeatureNode f;
      f.index = -1;
      f.value = 0.0;
      features_.push_back(f);
    }

    return true;
  }

  void Features::addFeature(int index, float value) {
    FeatureNode f;
    f.index = index;
    f.value = value;
    features_.push_back(f);
  }

  void Features::makeBasicFeature(int offset,
                                  const Node *first,
                                  const Node *last) {
    // distance
    addFeature(offset + 1 , 10 * distance(first, last));

    // degree
    addFeature(offset + 2 ,
               std::atan2(last->y - first->y, last->x - first->x));

    // absolute position
    addFeature(offset + 3, 10 * (first->x - 0.5));
    addFeature(offset + 4, 10 * (first->y - 0.5));
    addFeature(offset + 5, 10 * (last->x - 0.5));
    addFeature(offset + 6, 10 * (last->y - 0.5));

    // absolute degree
    addFeature(offset + 7, std::atan2(first->y - 0.5, first->x - 0.5));
    addFeature(offset + 8, std::atan2(last->y - 0.5,  last->x - 0.5));

    // absolute distance
    addFeature(offset + 9,  10 * distance2(first));
    addFeature(offset + 10, 10 * distance2(last));

    // diff
    addFeature(offset + 11, 5 * (last->x - first->x));
    addFeature(offset + 12, 5 * (last->y - first->y));
  }

  void Features::makeMoveFeature(int sid,
                                 const Node *first,
                                 const Node *last) {
    const int offset = 100000 + sid * 1000;
    makeBasicFeature(offset, first, last);
  }

  void Features::makeVertexFeature(int sid,
                                   std::vector<NodePair> *node_pairs) {
    for (size_t i = 0; i < node_pairs->size(); ++i) {
      if (i >  kMaxCharacterSize) {
        break;
      }
      const Node *first = (*node_pairs)[i].first;
      const Node *last  = (*node_pairs)[i].last;
      if (!first) {
        continue;
      }
      const int offset = sid * 1000 + 20 * i;
      makeBasicFeature(offset, first, last);
    }
  }


  void Features::getVertex(const Node *first, const Node *last,
                           int id,
                           std::vector<NodePair> *node_pairs) const {
    if (node_pairs->size() <= static_cast<size_t>(id))
      node_pairs->resize(id + 1);

    (*node_pairs)[id].first = first;
    (*node_pairs)[id].last = last;

    Node *best = 0;
    const float dist = minimum_distance(first, last, &best);

    static const float error = 0.001;
    if (dist > error) {
      getVertex(first, best, id * 2 + 1, node_pairs);
      getVertex(best,  last, id * 2 + 2, node_pairs);
    }
  }
}

Generated by  Doxygen 1.6.0   Back to index