使用C ++设计事件驱动的头部跟踪库

时间:2011-09-23 18:56:36

标签: c++ api architecture opencv

我想为我的团队提供一个非常简单的C ++接口来进行OpenCV头部跟踪。

哪种架构最适合此应用程序?

我应该提供“事件驱动的”API,还是应该让客户端在想要头部位置(yuck)时查询该库? 我应该要求客户端应用程序定义回调函数吗?我可以做一些类似于[TUIO API in Processing] [1]的东西,你将类传递给TUIO类,然后TUIO事件调度程序在该类中查找你的回调方法(你的类成为委托)? C ++的方法是什么?

如果要求指向具有给定签名的静态方法的函数指针呢?


详细

该规范将遵循TUIO和Community Core Vision的领导。我们正在寻找带有Haar分类器的头部,而不是blob(触摸桌上的指尖)。每个新头都获得一个持久ID。然后我们只发送三个事件:addHeadObject(id, x, y)removeHeadObject(id)updateHeadObject(id, x, y)。可能有更多参数用于通信确定性等.updateHeadObject事件将以帧速率发生,最高可达每秒30次。通过增加一些轮询速率无法提高更新速率,因此来自摄像头设备的帧事件必须最终驱动API。

2 个答案:

答案 0 :(得分:0)

我将遵循TUIO C ++界面中使用的委托模式。 http://www.tuio.org/?cpp

MyHeadTrackerListener listener; // defines a listener
HeadTrackerClient client; // creates the client
client.addHeadTrackerListener(&listener); // registers the listener
client.connect(); // starts the client

正如TUIO文档中所述:

  

您的应用程序需要实现TuioListener接口,并且   必须添加到TuioClient才能接收消息。

     

TuioListener需要实现以下方法:

     

addTuioObject(TuioObject * tobj)当对象变为时调用它   可见removeTuioObject(TuioObject * tobj)从中删除了一个对象   表updateTuioObject(TuioObject * tobj)移动了一个对象   表面addTuioCursor(TuioCursor * tcur)这个时候调用   检测到新光标removeTuioCursor(TuioCursor * tcur)一个光标   从表updateTuioCursor(TuioCursor * tcur)中删除了一个游标   正在桌面上刷新(TuioTime bundleTime)这个   在每个包之后调用方法,使用它重新绘制屏幕   示例

我的客户端将需要实现一些头部跟踪事件回调方法。根据这个大纲看起来很简单,但我不知道在C ++中这样做是否正常。

答案 1 :(得分:0)

如果库正在控制程序流,那么回调可能是最简单的解决方案。如果您愿意,将其抽象为coroutine也可以很好地转换为多线程解决方案。我在实施和维护方面提供了这一点,而不是性能。

我过去使用boost::signal作为达到目的的手段取得了成功,值得一看。

您的公共界面可能是等效的,但可能更详细一些。我希望大多数这些类都有纯虚拟接口,但是对于演示,简单结构更容易阅读。

struct Head {
  int transient_id;
  int x, y;
};

struct Frame {
  std::vector<Head> heads;
};

struct HeadTracker {
  boost::signal<void(const Frame &)> updates;
};

我不确定你是否绝对需要在头部进入或离开相关性时发射事件。更新提供xy,因此相关性更改仅为transient_id。如果客户端不知道它有哪个transient_id,它可能只是请求详细信息。虽然简化处理可能会有所帮助。

我对您的标识符的确定度有一些担忧,因为它们理论上(或可能是常规的)会发生变化。我不清楚你是否在追踪特定人物的身份(transient_id#35523是Jane Doe),或者只是那个形状是否是头脑。

如果Jane走出门,然后回来,每个系统会有两个不同的人。这会简化一些事情,我建议不管怎样在幕后建模。

struct Head {
  int transient_id;
  int x, y;
  float is_head; // [0.0, 1.0]
};

将场景中的这个圆形形状识别为头部是一个有点二进制的问题。这应该完全独立于任何身份认同。如果你只是看到某个人的头脑后面,那个头部属于任何一个人的信念会随着时间的推移而发展。

您可以为每个框架上的每个人设置统计信息。这是一个数据表,而不是单个值。虽然你可能只想在任何特定时间对有限数量的人采取行动。

struct Person {
  int person_id;
  std::string name;
};

struct Head {
  int transient_id;
  int x, y;

  size_t getNumRanks();
  std::pair<Person, float> getPersonWithCertainty(size_t rank);
};

struct HeadTracker {
  boost::signal<void(const Frame &)> updates;
};

将排名置于Head可能会有些误导(无论多么方便),具体取决于系统行为。如果你有过去帧的历史,那么它可能是准确的。如果您在任何给定时间只有一个统计量,那么它可能属于HeadTracker

计算排名似乎对于实时系统而言非常强烈,但可能并非如此。我不确定你说的是什么,但考虑一下它当然很有趣。 :)