Cinder+OpenCVでドロネー三角形分割(1)
先日、4時前に空が明るくなっててびっくりしました。
夏がまたやって来ますね。マサラ・ムーチョことムーチョです。こんばんは。
さてさて、Flash界では流行ともいえるドロネー図を
Cinder+OpenCVで逆輸入してみました。
つたないソースですが、ソースコードも公開します。
座標群をドロネー三角形分割化し、出力するわけですが、
出力で必要なのは線だとつながる2点、面だと3点が必要になります。
今回は線の出力です。

おおおつながってるよ! 今回座標の数は2000。60fpsでもギュンギュン。
OpenCVでドロネー図を作る際、以下のような流れになります。
1.空のドロネー空間を作成
2.座標をドロネー空間に追加
3.線の座標を取得
4.描画
以下抜粋ソースです。
[code lang="cpp"]
// 座標群
for(int i = 0; i < points.size(); i++){
float posx = Rand::randFloat()*Rand::randFloat(-1,1)*getWindowWidth()*0.5+getWindowWidth()*0.5;
float posy = Rand::randFloat(-1,1)*Rand::randFloat(-1,1)*getWindowHeight()*0.5+getWindowHeight()*0.5;
points[i]=Vec2f(posx,posy);
}
// 1.空のドロネー空間を作成
CvRect rect = { 0, 0, getWindowWidth(), getWindowHeight() };
storage = cvCreateMemStorage(0);
subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*subdiv),
sizeof(CvSubdiv2DPoint),
sizeof(CvQuadEdge2D),
storage );
cvInitSubdivDelaunay2D( subdiv, rect );
// 2.座標をドロネー空間に追加
CvSubdiv2DPoint *p;
for(int i = 0; i < points.size(); i++){
p = cvSubdivDelaunay2DInsert( subdiv, toOcv(points[i]) );
p->flags = i; //元座標とチェックするためにint追加
}
// 3.線の座標を取得
list lines;
int i, org_id, dst_id;
int total = subdiv->edges->total;
int elem_size = subdiv->edges->elem_size;
CvSubdiv2DPoint* org_pt;
CvSubdiv2DPoint* dst_pt;
Vec2f pos_org, pos_dst;
CvPoint2D32f org, dst;
CvSeqReader reader;
cvStartReadSeq( (CvSeq*)(subdiv->edges), &reader, 0 );
for( i = 0; i < total; i++ ){
CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
if( CV_IS_SET_ELEM( edge )){
org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
if( org_pt && dst_pt ){
org = org_pt->pt;
dst = dst_pt->pt;
pos_org = Vec2f(org.x, org.y); // 線の始点
pos_dst = Vec2f(dst.x, dst.y); // 線の終点
org_id = org_pt->flags;
dst_id = dst_pt->flags;
// 元座標と返された座標が一致するかチェック
if(points[org_id] == pos_org && points[dst_id] == pos_dst){
lines.push_back(LinePos(points[org_id], points[dst_id]));
}
}
}
CV_NEXT_SEQ_ELEM( elem_size, reader );
}
// 4.描画
for( list::iterator it = lines.begin(); it != lines.end(); ++it ){
gl::drawLine(it->first, it->second);
}
[/code]
ちょっとした問題があって、というか私のやりたいことと
OpenCVの考えるドロネー分割にはちょっとした違いがありました。
私は座標同士だけを結ぶ線が欲しかったのですが、
OpenCVでは生成した空間を分割しようとします。
場合によってはそっちの方が正解かもしれませんが、
今回はその線を取り除くためにやっつけなひと手間を入れました。
そこを省略できるともっと高速になると思います。
ソースがこちらになります。
DelaunayLineApp.cpp
CinderとCinder-OpenCVが動く環境ならそのまま動くと思います。
あ、includeとlibへのパスは追加してください。
これが何気にめんどくさいんですが。
どうにかなりませんかね?このパスを通す作業って。