openFrameworks覚え書き

Mastering openFrameworks: Creative Coding Demystified」という本でopenFrameworksを勉強しています。
link: Amazon書籍版/Kindle版
日本語書籍でもoFの本が数冊出版されていますが、こちらの本もopenFrameworksの導入から始まりますが、さらにもう少しつっこんだ内容まで書かれているので、多少Flash/ActionScript等でビジュアルのコーディングをやってきた人にもちょうどいい内容だと思います。
さらに出版されたのが2013年9月末と、oF書籍の中では新しく、oFのバージョン0.8.0ベースで書かれています。

読んでみると、サンプルの内容も面白く、かつ、「このテクニックを使えば今流行のああいう作品やこういう作品を作れます」とか「こういう演出を実現したい場合に、AとBとCというやり方があるけど、Aはこういう特徴があって、Bは…、Cは…で、Aが一番高速に処理できるからおすすめです」みたいな、実際自分の作品を作っていく時に迷わないような現場的な解説が多くてありがたいです。

kindle paperwhiteで読んでますが、紙だと分厚いけどもkindleならどこでも読めて、コードを写経するときは立てかけたりできるので、kindleなおかげで英語の本だけども読み進められているように思います。復習するときは紙の本が欲しくなるけど。

あと、書籍の中でOpenGLを使う箇所は少しだけなのですが、openFrameworksって最終的にOpenGLシェーダーを書ける人が負荷にとらわれずにやりたい放題できるのではないかという印象を受けるのですがどうなのでしょう?oFでOpenGLで最後のアウトプット演出する人っていうのはFlashにおけるPixelBenderユーザーぐらいの割合なのでしょうか?それともほとんどのoFユーザーがシェーダー書いてるみたいな状態なのかなぁ?という温度感がよくわからないけども、まぁおいおい。

ちなみに当方、2014年1月時点、openFrameworks0.8.0、Xcode5.2、MacOS10.9(Marvelics)という環境です。

以下、気ままにメモしている内容。たぶん読んだ人にしか分からないし、最悪の場合僕が読み間違えてるかもしれないけども。

・制作する各プロジェクトはappsフォルダー内に置く。
・コンパイルの際、Debugモードではブレイクポイントを使える。Releaseモードの方がパフォーマンスは良いので、動作チェックはReleaseモードを使う。
・bin/dataの中には画像やビデオ、音声、XML、テキストといった、ロード&セーブに関わるファイルを置く。
・srcにソースコードを入れる
・プロジェクトフォルダ内の.sinはVisualStudio、.xcodeprojはXcode、.workspaceはCode::Blocksのプロジェクトファイルなので、開発環境に応じてこれらのファイルを開くことになる。

main()関数はopenFrameworkアプリケーションのエントリーポイントとなる重要な関数で、main.cpp内で定義されている。

ofSetupOpenGL( &window, 1024, 768, OF_WINDOW);

のようにすれば所定のサイズでユーザーが動かせるスタイルのアプリケーションウィンドウを作成できる。フルスクリーンのアプリを作りたい場合は第3引数をOF_FULLSCREENとする。
通常はmain.cppは修正変更せず、スクリーンサイズの変更に関してもtestApp.cppで行う。
※main.cpp内ではofImageなどのopenFrameworksのオブジェクト機能はパスや変数がこの時点では未設定なのできちんと動作しない。これらもtestApp.cpp以降で実装する。

.h(ヘッダーファイル)ではなく.cpp(実装ファイル)内でオブジェクト定義をすることもできるが、ofEasyCamやofThreadやofTCPServerを使う場合等は、クラスオブジェクト自体が生成されるまえにそれらを実行してしまうことでクラッシュの原因となることがある。

ESCキーでアプリ終了できるが、アプリからアプリ自身を終了させたいときはOF_EXIT_APP(val)関数を使う。

setup()関数ではofSetFrameRace(rate)でフレームレート設定できるが、デフォルトで0と鳴っており、これはマシンスペックに応じた最高パフォーマンスのfpsを出そうとするので注意すること。

キー押下時のキーコードはlibs/openFrameworks/utils/ofConstants.hを参照すること。

便利な関数
ofMap(v,v0,v1,out0,out1):float vが[vo, v1]の範囲の値を取る時、その範囲を[out0, out1]の範囲になるように変換する
ofClamp(v,v0,v1):float vがv0からv1までの値をとり得る。return min(max(v, v0), v1)と同義。
ofRandom(a, b):aからbまでの値をとる。b未満となることに注意
ofNoise(x),ofNoise(x,y),ofNoise(x,y,z),ofNoise(x,y,z,w):ペリンノイズを返す
ofToString(v):vというint,float値をstringに型変換する
ofToInt(s),ofToFloat(s):文字列sをいんちゃfloat値に型変換する
ofGetWidth(),ofGetHeight():現在のスクリーンの幅と高さのピクセル値を取得できる
ofGetElapsedTimef():プロジェクト開始からの時間をfloat型のミリ秒で返す。123.4であれば123秒+400ミリ秒を表す。
ofShowCursor(),ofHideCursor():マウスカーソルの表示/非表示

draw部分で毎フレームごとに背景から描画しなおすのが通例だけども、描画を積み重ねていきたいときもあり、そういうときはofSetBackgroundAuto(false)を使う。これを使うときは背景を描画し直す必要はなくなるが、この機能にはいくつかの問題があり、
・OSXではスクリーンがjitterになる
・Windowsではscreen grabbingが機能しない。
そのため、画面上にオブジェクトを重ねて描画していきたいときはFBOバッファーを使ったほうがよい。

FBO(Frame Buffer Object)はスクリーン外にラスターをバッファーしておく機能です。バッファーに描画しておいてから、実際のスクリーンにそれを描画するといった使い方ができます。バッファー上の描画はdraw()ごとにクリアされることはないので、画面上にオブジェクトを重ねていきたいときはバッファーを活用するとよいです。
fboにはofTextureという、現状の絵を保持しておく機能がある。fbo.getTextureReference()を記述することで利用できる。
FBOではアンチエリアシングが効かない場合があり、対応としては2倍のサイズでFBOに描画し、スケールを半分にするという方法がある。

image.grabScreenで画面キャプチャする時、pngは圧縮時間がかかるので、即座にキャプチャしたいときはsaveImage(“hoge.bmp”)の方がよい。

Particle
・パーティクル粒子を別クラスで定義するとき、本来は別のファイル(.h,.cpp)を作成するほうが通例ではあるが、既存のクラスファイルに追記する形でクラス定義することもできる。

extern宣言することで、外部クラスからもアクセスできるグローバル変数/メソッドを定義できる。なお、むやみにグローバル変数を作らないように心がけるべきなのは、他の言語と同様。
Euler method(オイラーの定理)とVerlet integration(ベレ法)はちゃんと復習しておいたほうが良い。

fbo.allocate(w,h,GL_RGB32F_ARB);と記述することで、カラーコンポーネントをfloat値として保持できるようになり、このほうがデフォルトよりも色の精度が高い(デフォルトはunsigned charなので)。通常の描画ではデフォルトでも問題ないが、バッファコンテンツを徐々に消すといった場合は、float値の精度が望ましい。
ただしfloat値のfboは通常の4倍のビデオメモリを消費し、古いビデオカードでは対応しきれない。その場合はデフォルトのfboを使うしかない。

ofClamp()関数は値の変化量を制限する機能。

パーティクル実装などで、アクティブでない要素を消去して回すような実装の場合、要素をvectorよりもdequeクラスに格納するほうが最適。

Marching cubes(マーチングキューブ法)も要チェック。めたボールなどに使われる。

4.Image
画像はPNG,JPG,BMP,TIFFが使える。PNGはデコードが速い。

image.draw(p)でポイント指定、image.draw(rect)で矩形エリアで描画ができる。

image.width/heightで画像のオリジナルのサイズが取得できる。

image.drawでもいろいろ変形できるが、ofTranstlate()やofScale()で処理するほうがオススメ。

setAnchorPercent(xScale,yScale)とsetAnchorPoint(x,y)がある。

ofSetColor(r,g,b)で画像の色を変更できるが、ofSetColor(255,255,255)で元の画像なので、そこから減らすことで暗くはできる。ofColorは動作が遅いので、フラグメントシェーダーを使う方がオススメである。
ofSetColorはそれ以降の画像全てに影響するので、画像表示の際は毎回ofSetColor(255,255,255)を設定しておくとよい。

ブレンドモードを検証する際はで白背景は避けること。

より詳細なブレンド演出がやりたいときはフラグメントシェーダーを使うとよい。

ofDisableAlphaBlending()でオフできるが、これはFBOで使われることが多い。

ofImageの他に、より精度の高いofFloatImageや、デプスカメラに最適なofShortImageクラスもある。

image.bpp/8で画像が1:グレースケール、3:24ビットカラー、4:32ビットアルファ画像かを判定できる。

getPixelsでピクセル情報から色を取得しても良いけども、getColor(x,y)でその座標のピクセルの色をofColorオブジェクトとして取得できるので使い勝手が良い。
setColor(x,y,color)で着色できる。image.update()を記述しておかないと反映されないので注意。

getColor,setColor系はgetPixelsやsetFromPixelsよりも多少パフォーマンスは落ちるので留意しておくこと。

RAM側にはimage.getPixels()でアクセスできるピクセル配列があって、同時に
そのクローンがビデオメモリ側にあり、imagegetTextureReference()で使われる。この2重構造のために、imageの配列をいじったあとにimage.update()で適応させる必要があるわけです。
それが2度手間だと思う人もいるでしょうし、image.setUseTexture(false)とすることでピクセル配列を直接描画するオプションもありますが、これはピクセル配列をビデオカードにロードする必要が発生し、高速なのですがそうはいっても時間のかかる操作なのです。だから、画像を変更しない場合や何度も画面に描画する場合は、テクスチャを使った方がいいのです。

ビデオをプロジェクションマッピングするツールとしてはMadMapperが有名でoFで利用する為にofxSyphonアドオンというものがある。

texture.draw()では三角ポリゴン2つで構成されているので対角線付近が予期しない描画になる。よりスムーズな結果を得る為には画像のメッシュを50トライアングル以上にしたほうがよい。そういう場合はofMeshクラスを使う。

画像をパレットとして、ある1点のピクセルの色を取得して着彩に利用したりすることがある。
http://vimeo.com/68852188のパーティクルはカラーパレットを使っている。パーティクル生成時はパレット画像の下端を取得し、時間ごとに上方向のピクセルで着色するように実装している

Video
oFではAVI,MP4,MOV全てに対応している。oFは動画再生にAppleQuickTimeSDKを使っているのでMacでもWinでもMOV形式の動画を使うのが推奨。
※windows環境の人がoFでビデオを利用するときはQTをインストールしておくこと。

スピード調整やαチャンネルを使わない、通常のビデオであれば、MPEG4コーデック(H.264)がファイルサイズも小さくなるのでオススメ。HD動画であればこれ1択。

MPEG4はフレームのデコードに前のフレームを活用したりするので、ビデオの再生方向やスピードを変化させる場合は負の影響が大きい。

VJのようにビデオクリップを活用する場合は(Apple)Motion-JPEGが良い。

アルファチャンネル入りのビデオ映像はApple-PNGフォーマットを使う。数百万色+や32-bitカラーモード設定すること。サイズは一番大きくなるが、品質をパフォーマンスは良い。

ofVideoPlayerはタイムデータに基づく再生スピードになる。=oFのフレームレートとは無関係。oFが60FPS設定でupdateしていてもビデオが30FPSなら30FPSで再生される。その際、updateで映像がロード(変更)されたかはvideo.isFrameNew()のbooleanで判定できる。

setLoopStateでビデオのループ設定(ループ無し、有り、往復再生)も可能。再生スピードを途中で変更することも可能。

ofPixels &pixels = video.getPixelRef();における&シンボルは、データ配列を参照するだけで、データ自体はコピーしませんよということを意味する。ポインタと同様の機能。これによって処理が高速化する。

dequeクラスはどんなクラスのコンテナにもなる。vectorクラスとも似ており、vectorの方がやや高速なのだけど、要素の追加&削除の処理はdequeの方が高速。

カメラからの映像の取得はofVideoGrabberクラスで行う。grabber.listDevices()関数は、grabber.setDeviceID()でカメラIDを指定する際に便利。

連番画像の連続再生における制限
・画像の読み込みは時間がかかる処理なので、通常は全ての画像をsetup()時に読み込み終えておくとよい。
・ロードする画像数はビデオメモリのサイズによって制限される。n枚ならn*w*h*4バイトのメモリを使う。そういった場合はRAMを占領しないようにofImageではなくofTextureを使うと良い。

fmod(a,b)はaをbで割ったときの余りを返してくれる。

ofNoise()関数は0~1の間の値を返す。

Sound
setMultiPlay(multi)関数はコピーしたサンプル音を同時に鳴らせるかどうかをboolean設定をするための大事な関数。

unloadSound()はPCではそれほどケアする必要はないが、モバイル端末では重要。またloadSound()で次のサンプル音源をロードすれば、自動的に前のサンプル音源はアンロードされる仕様なので、PCでunloadSoundを使う必要はないです。

ofSetBackgroundAuto(false)で、draw()のたびにスクリーンをクリアしなくできる。

ニュートンの第2法則(運動の第2法則)もおさらいしておく。

要素数の決まった配列であれば、ofSoundPlayer sound[6]よりもvector soundで定義したほうが望ましいが、現時点でoF+MacOSXではvictorとofSoundPlayer間の不具合で1個の音しか鳴らない不具合がある。

audioOut()関数はupdateやdrawのコールとは別(独立した)で呼び出される。サウンドカードがバッファ状況に応じて呼び出す関数。

逆にサウンドカードによってはバッファが間に合わずに音の途切れが聞こえる場合があるが、その場合は
soundStream.setup(this, 2, 0, 44100, 512, 4);
512の個所はバッファサイズなので、ここを1024とかにすると安定する。逆にあたいを小さくするとレスポンスは良くなる。バッファを満たすことと再生のラグが少ないからだ。

音や画像等のデータをデバイス等の他の装置の入力として使うことをトランスコーディングと呼ぶ。

音声録音の際によく使われるDC-ofset removalはバッファの平均値をゼロに等しくなるようにシフトすること。そういう機能があるわけではなく、forでバッファの平均を出して次のforでその平均値を減算するという実装をする。

マイクからの音の入力は、出力と同様にofSoundStreamインスタンスを作って、audioOutではなくaudioReceived(float *input, int bufferSize, int nChannels);する

PCMはsoundStreamで音の振幅を扱うが、ofSoundPlayerでは音のスペクトラムを扱う。
スペクトラムをaudioOutで出力したい場合は、FastFourierTransform(高速フーリエ変換)を自分で実装する必要がある。

spectrumのRadとbandRad、VelとbandVelの関係について要調査。特定のミュージックトラックを特定して選択できる。ベースドラムとスネアドラムを抽出するといった風に。

3D
z-buffer(depth buffer)は標準でオフになっているので、必要な場合はofEnableDepthTest();でONにする

3D表現ではofMeshクラスを積極的に使った方がよい。ほぼ全ての状況においてofTriangleでポリゴンを作成するよりも良い。2Dで使っても良い。

メッシュの頂点が変化しない場合はofVBOMeshを使った方が明らかにパフォーマンスがよくなる。

ofMeshでは、addVertex()で頂点を追加していく。ある頂点が複数の三角ポリゴンの頂点となる場合は1度だけ設定する仕様になっている。頂点移動の際、1つだけ動かせば良いので扱いやすい。

mesh.addColor()はmesh.addVertex()するたびに毎回呼び出さないと頂点着色できない

コード模写中、getNumVerticesとgetNumIndicesを書き間違っていたため、メッシュの一部分にしかnormalが適応されない現象が発生した。

draw()>ofPushMatrix内で
ofRotate(30, 1, 0, 0);
ofRotate(angle, 0, 0, 1);
カメラをx軸に30度傾けてから、時間経過(angle)ごとにz軸回転をし続けるコード

シェーダーは主に3種類ある
・フラグメントシェーダー

シェーダーにパラメーターを渡すことでマウス位置等に応じた動的な画像変化を演出できる。
パラメーターを渡す際はuniformキーワードを使う。
例)uniform float time;
oF側からは、shader.enable()を呼び出した後、
shader.setUniform1f(“time”,time);
と記述する。このときの1fはシェーダーにfloat値を渡すことを意味する。
第一引数の”time”はシェーダーで定義した変数名、第二引数はoF上の変数である。

配列を渡すときはシェーダー側は
#define N (256)
uniform float myArray[N];
とし、oF側から以下のように呼び出す
shader.setUniform1fv(“myArray”, myArray, 256);
これで256個の要素を持ったfloat配列であるmyArrayを渡せる

シェーダーでもPerlinノイズを使える。GLSL言語にもnoise1,noise2(),noise3(),noise4()というPerlinノイズが使えるビルドイン関数があるが、多くのビデオカードではこれらは0を返してしまうので自分で実装したほうがよい。幸いにもGLSLにはPerlin等のノイズを簡単に使える下地がある。

Ashima ArtsとStefan Gustavsonが作ったebgl-noiseライブラリ(https://github.com/ashima/webgl-noise)を活用する。その中のcnoise関数実装を自分が書いたシェーダーにコピペして流用できる。

※gl_TexCoord[0].xyとgl_TexCoord[0].stの違いが分からない。xyをstにしても問題なく動いたりする

マスク表現などではフラグメントシェーダーに画像を2つ以上読み込ませる場合がある。その場合はuniform sample2DRectパラメーターを追加すれば良い。

oFではshader.enableをコールした直後に画像をsetUniformTextureしてやれば良い。
例)shader.setUniformTexture(“texture1”,image2.getTextureReference(),1);
3番目の引数はOpenGL上のテクスチャIDで0以上で指定する。※前のサンプルで0番は使っているので、上ではIDを1にしている。前のサンプルでは0番テクスチャは画面全体を渡しているのでsetUniformTextureしていないことにも注意。

最後のサンプルでは音を画像化してシェーダーに渡すことで音と画像をシェーダー上で連動された表現を作っている。注意点としては、スペクトラムをfloat配列としてシェーダーに渡すのではなく、画像化してから渡している点で、この理由としては

頂点をシェーダーでゆがめる場合、gl_Vertexやgl_PositionはVec4形式、x,y,z,scaleが入っている。
座標変換だけならVec3で取り扱い、最後に
vec4 posHomog = vec4(v, 1.0);
という感じで4つ目のスケールを追加してVec4として座標変換に活用すればよい。

ジオメトリシェーダー
処理経路において、vertex→geometry→fragmentの順で処理される。

OpenCV
CPUとGPUどちらでも使える。CPUでの動作はピクセルごとに画像処理するよりも高速に処理できるが、フルHD解像度の処理に対応できるかというとそうでもない。そういう場合はGPUを使う。
ビデオエフェクトであればシェーダーを使う方がシンプルで汎用性もあるのでそちらを使う方が良い。
oF用のofxOpenCVというアドオンがある。最新版の最新機能を使いたい場合はオリジナルのOpenCVライブラリを使ってもよい。

OpenCVにはイメージクラス群とアルゴリズムクラス群がある(参考:http://blog.n1n9.jp/how2/of-opencv.php)
ImageClasses
ofxCvColorImage:rgbの3チャンネルunsigned char、カメラからの映像等
ofxCvGrayscaleImage:1チャンネルのグレースケール、unsigned char。輪郭やコーナー検出処理につかわれる。0か255の2値であるバイナリイメージ用のクラスとしても用いられる。
ofxCvFloatImage:1チャンネルグレースケール。float値。スムーズフィルタやフーリエ変換等の正確な計算の際に用いられる
ofxCvShartImage:1チャンネルグレースケール。unsigned short int。0〜65535。デプスカメラ等で用いられる。
ofxCvImage:基底クラス。このクラスを直接使うことはできない。エラーになる。
※4チャンネルカラー画像は用意されていないので、つまりα画像は使えない

Algorithm Classes
ofxCvContourFinder:インプットのバイナリイメージから白い塊を探す。オブジェクト検出に使われる。
ofxCvHaarFinder:Haar-like機能を使って画像からオブジェクトを見つける。主に顔認識に使われる。

ofxCv image群を使う際のポイント
・最初に初期化する。allocate(w,h)
・=オペレーターで画像をコピーできる。異なるイメージクラスの画像にもコピーできる。=でコピーする場合は初期化処理は不要。コピー元と同じサイズで生成される。異なるイメージクラスへのコピーはその取り得る値に応じて変換されることに注意。
・setFromPixels(data, w, h)。image.setFromPixels(grabber.getPixelsRef());としてカメラ映像を処理できる。

・イメージが初期化されたかどうかはbAllocatedでブーリアン判定できる。

初期化関連で2つの関数が用意されている
・set( value )関数は画像ピクセルを設定できる。カラー画像用にはset( valueRed, valueGreen,valueBlue )という関数もある。
・clear()関数はメモリに割り当てられたimageを全てクリアする。通常は画像のデストラクタは自動的に呼び出されるので、この関数を使う必要はない。

アルゴリズム群
*=では負の値は0に切り捨てられるので、中間処理段階でマイナス値を必要とする処理の場合はこ使わないこと。floatImage *= valueを使わずに、multiplyByScalar(floatImage,value)関数を使うこと。関数の実装は書籍に記載。
ofxCvFloatImage以外は値が0〜255に切り詰められる可能性があるので、ofxCvFloatImageに変換した上で処理を書けるとよい。

OpneCVのgetPixelsRef()はofImageクラスのgetPixelRef()と関数名が微妙に違うので注意。
OpenCV画像やIplImageオブジェクトを直接変更した場合はflagImageChanged()でofxOpenCvアドオンにたいして画像が変更したことを通知する必要がある。

モーション判定
映像判定は前後のフレームの差異を比較する。この場合はgrayImage.absDiff(grayImage2)関数を使う。これを使って、カメラ前で動いた時に、動作の多い場所をパーティクルのエミッターにすることができる。
サンプル実装
drawでは4分割で各計算した画像を表示させるが、パーティクルエミッター等の算出はupdate()内でできていることに留意。→draw内の実装は確認用。
diffFloat *= 5.0というピクセル値の増幅の値を減らすと検出量が少なく、増やすと検出量は多くなる。
bufferFloat *= 0.85というバッファダンプの減衰を0.95に増やすと検出している時間が長くなる。
※影の動きも差異で検出するので、インスタレーションやるときは正面からライト当てるとよさげ。

フィルタ
・blur(winSize)
・blurGaussian(winSize)ーblurよりも正確だけどもCPU負荷は高い。blurと比較するときはblurGaussianの方が同じwinSizeだとぼけが強いのでblur(11)とblurGaussian(21)で同じぐらいで比較できる。
・erode()ーエロージョン:3×3のピクセルに最小化するフィルタで、ノイズの除去などに使われる。
・dilate()ーダイレージョン:穴埋めなどに使われる。参照→モルフォルジ処理(http://www.fjt.info.gifu-u.ac.jp/jyugyou/subject2011/index3.html)
・convertToRange(minValue, maxValue)ー画像の値を所定の範囲に変更できる。例えばfloatImage.convertToRange(0.5,1.5)とすれば元画像[0,1]の範囲が[0.5,1.5]になる。つまりvalue = 0.5+value*(1.5-0.5)と同義
・invert()ー反転。grayImage.convertToRange(255,0)でも同じことができるけども、invert()の方が高速に処理できる。
ofxCvGrayscaleImageクラスにはとても重要なthreshold(threshValue)メソッドがある。閾値以下のピクセルを0にできる。第2引数doInvertをtrueにすれば反転指定もできる。

フィルタ例
Gaussianスムーズは最も有用なフィルタで、ノイズを軽減させたり、文字通り画像をスムーズにしたり、バイナリ化するときにも便利に使える。

(メモ抜け)

image.warpPerspetive(ofPoint,ofPoint,ofPoint,ofPoint)
は各ポイントを頂点とする歪んだ四角形を画面サイズに合わせる(リストア)ためのメソッド。
変形前と変形後の四角形の縦横比率が変わることがあるので、その場合はwarpPerspective()ではなくwarpIntoMe()メソッドを使う。

オブジェクト検知

オブジェクト検知の種類としてはTemplate matching、Contour analysis(geometrical matching)、Algorithms based on machine learningがある。

ofxCvContourinderクラスはバイナリイメージ中の白い部分が広がっている範囲と境界を検出するためのクラス。この範囲はblobsと呼ばれる。
contourFinder.findCountours(mask, minArea, maxArea, maxNumber, findHoles)
maskはバイナリイメージ,minAreaとmaxAreaはどのサイズのblobsを除外するかを設定できる。maxNumberは最大何個のblobsを検出するかを設定でき、findHolesはfalseなら白いblobs中の黒いblobsを無視する、trueなら黒いblobsもblobsとカウントする。
その結果取得できるのは
contourFinder.blobs配列でblobsにアクセスでき、contourFinder.blobs.size()で個数を、contourFinder.blobs[i]にはそれぞれarea(blobの中に何ピクセルあるか)、length(blobの境界の外周の長さ)、boundingRect(blobの境界の矩形)、centroid(blobの重心)、hole(ブーリアン値で、自身が他のblobの穴である場合はtrue)、pts(blobの境界上にあるポイントが全て入った配列)、nPts(境界上のポイント数)
注意点としては、毎フレームごとに検出されるblob配列の順番は同じとは限らない(blob[5]に入っている領域が次のフレームでもblob[5]に格納されるとは限らない)ので、そこはblobの場所をもとに自分でIDを割り振って前後フレームで比較するような実装を自力実装する必要がある。

contourFinder.draw(x,y,w,h)でblobを描画できる
サンプル中、obj.resize(n);を記述するのは、毎フレームごとにblobの個数が違う可能性があるため。

ofxOpenCVに加え、OpenCVを使う
using namespace cv;でインポート
Mat imageCV;でCV画像imageCVを用意して
imageCV = Mat(image.getCvImage());でofxCvImageであるimageをimageCVにコピー。※通常の=コピーではないことに注意。また、imageCVとimageは同じメモリ領域を共有するため、imageCVは変更用ではなく参照用として扱うほうがよい。
Tip:Mat変換においてはsetNativeScale()は使わない方がよい
Mat同士のimageCVからimageCV2へのコピーはimageCV2 = imageCV.clone();で行う。
Mat型画像を表示したいときは、imshow(“image”,imageCV);で別のウィンドウでImageというタイトルで画像が表示できる。デバッグに役立つがCPU消費するのでデバッグできたらコメントアウトしましょう。
この関数を使うにはtest.cppの冒頭に#include “highgui.h”を記述すること。
OpenCV画像をもとのofxCv画像に戻すには
IplImage iplImage(imageCV2);
image = &iplImage;
と記述する。
現在はimageがallocateされていないとimage = &iplImageの部分がエラーになる。これを治すにはaddonのofxCvImage.cpp内のコードを書き直すことで対応できる。

(続く)


kindle版