MultiStateTarget と getState の罠

sikuli という大変便利なライブラリがあります。なんか画像認識とかしてくれます。java 用の api もあります、やったね。

「複数の Target のうち何かひとつ見つかればいいな」みたいなときには MultiStateTarget を使います。何が見つかったかは通常 getState で判別するわけなんですが。

getState の実装は、その名前からは全くかけ離れたものになっていて、「見つかった ScreenRegion の中で、複数の Target をもう一度 find しなおして、一番 score の良かった Target の状態を返す」という実装になっています。get とかいいつつなんかするのはよせ…。

そりゃまあ名前のとおり ScreenRegion はただの範囲であって、ある瞬間の状態をあらわすものじゃないわけですから、順当といえば順当です。
しかし MultiStateTarget を使っている時は、基本的には find した結果何が見つかったのか、が知りたいわけです。find してから getState するまでの間に、ScreenRegion 内に動きがあると、find したときに最も score の良かった Target とは違う Target の score が良くなってるかもしれない。厳密に何が見つかったかを判別してやるには、ある瞬間の状態を表すものとして、ScreenRegion の capture を呼び出してやる必要があるわけです。はて…

一番の問題は「見つかった ScreenRegion の中で find し直す」という部分です。登録されている、たとえば ImageTarget の画像サイズがそれぞれ異なっており、運悪く小さい画像が見つかった場合には、小さい ScreenRegion の中で大きな ImageTarget を見つけようとして、「opencv のレイヤで」ROI がおかしいぞ、としかられます。酷い。上述のリンク先のコードが動いているのは、たまたま大きいほうの画像が見つかったからで、locked.jpg が見つかっていたら getState の呼び出しで例外が飛ばされるはずです。酷すぎ。これは Target に大きさを持たないものがあることによる実装上の都合…ではなく、普通にバグだと思います。

追記:見つかった ROI を少し大きくする dirty な処理が書いてあったので、locked.jpg の場合は見つかってても動くかもしれません…なんにせよ酷い話ですね。

素直に ScreenRegion と一緒に、見つかった(score の良かった)Target の状態も返すような API があればとも思うのですが、ないです。残念でしたね。

addStateChangeEventListener 使えということなのかなあ。実行コンテキストの不明確なコールバックだらけのコードなんて書きたくないんですけど(現在の実装だと別スレッドでなんか一元管理されてます)…というか find, wait 使ってたコード回り全部書き直しになっちゃうしなあ…

まともな wrapper を書きたいものですが、病人なので我慢します。疲れました。