今までjit.genはGPUを使ってくれているものと思い込んでいた…。
GLSLを使うにはjit.gl.pixを使わなくちゃいけなかったのね。
jit.genでブラーを実装したときの重さで気付くべきだったなぁ。
ということでjit.gl.pixで改めてブラーを実装しました。
速さが段違い。
今までjit.genはGPUを使ってくれているものと思い込んでいた…。
GLSLを使うにはjit.gl.pixを使わなくちゃいけなかったのね。
jit.genでブラーを実装したときの重さで気付くべきだったなぁ。
ということでjit.gl.pixで改めてブラーを実装しました。
速さが段違い。
複数のjit.pwindowにそれぞれ矩形や楕円を描画し、
それらを加算乗算によって合成する。
おまけとして、そうやってできたテクスチャをビデオのマスクにする。
そろそろ実用に耐えるものができてきた、かな?
今日はちょっと軽めに。
jit.pwindowのクリックした位置に楕円を描きます。
jit.genでcodebox非使用(パッチングで頑張る)の練習のつもりで作りました。
今まではコーディングに頼りすぎてて、じゃあなんでMaxなのってなりそうだったので。
今回は操作用のjit.pwindowで出力(jit.windowへの入力)をモニタするようにしてます。
むしろなぜ今までそうしてなかったのか。
ビデオエフェクトの定番(?)ブラーフィルタです。
にじみ度合いをuv幅で指定します。
内部では、描画する点を中心にuv*2の範囲にある画素の平均色を描画する処理をしてます。
かなーりCPU食うのね…。まあしょうがないか…。
若干のがっかりを隠せませんが、とりあえず公開します。
ふたつのflonumは0.01くらいまでにしといた方が良いと思います。
片方が0だとある程度攻められますが。
選択した範囲にだけエフェクトをかけるとかそういう用途に使えるかなーと思いまして。
操作用のjit.pwindow上でのマウスイベントを取得して、
ボタンを押したときの座標を基準にxyそれぞれ0~1に正規化して矩形範囲を計算します。
(押した瞬間の座標だけ特別扱いする処理をchangeとgateでやっているのだけど、もっときれいな方法がある気がする)
で、jit.genに矩形の情報を渡して描画して、
ついでにjit.addで元動画と重ねて完成。
ボタンを放した瞬間も何かトリガーとってやったほうが使いやすいかも。
実際使うときにたぶんやる。
よく使うアプリケーションをできるだけ少ない手順で実行するために。
僕はこれで、例えばchでGoogle Chromeが、ssでスクリーンショットツールが、colでカラーピッカーが起動するようにしてます。
手順は以下。
ほんとこれだけなんですが、以下Tips。
レッツ華麗ライフ!
Genがかなり強力そうなので、5,6年ぶりにMaxを再開してみる。
練習がてらいくつか簡単なパッチを作ってアップするのを始めてみる。
一日ひとつとはいかないまでも、続いたらいいな…。
最近個人的には音より映像の方をよく触るので、まずはjitterからはじめていこうと思う。
とりあえずテクスチャの矩形切り出しをやってみた。
video_uv
UV分割数と使用インデックスで指定する方法と、
UVオフセットと幅で指定する方法とを用意した。(サブパッチみてね)
入力がビデオでも静止画でも同じように書けることに地味に感動しつつ。
昔と勝手が違うところがいろいろあって最初は手間取ったけど、
ちょっと触ればやっぱり簡単に作れてすごいソフトだなーと改めて思う。
プログラマな僕としてはGenの導入がかなりMaxをパワーアップさせてくれた感があるので
作品制作に使えるレベルまで継続したい。と今は思っている。
昨日は日曜日だったのでこんなものを作りました。
ビデオカメラからの入力画像をリアルタイム解析して、ある程度丸いものが見つかったらパカっと開けて中でふにふに動いてくれます。
以下技術的な話です。
やってることは至って単純で、OpenCvでカメラ入力画像から輪郭抽出をして、その中で円形に近い領域に対して上半分のピクセルを上にずらして、空いたところにアニメーションを表示しているものです。
ある領域が円形に近いかどうかの判定は以下の2つの条件で行いました。
誤検知を狙って作られたようなひねくれオブジェクトでなければ、大体これで丸いものを見つけられるようです。
ただ、みかんが意外と横に長かったり、環境とスレッショルドの兼ね合いによっては検出される輪郭が大きかったり小さかったりするようで、結構パラメータには余裕をもたせた方が良いかも知れません。
僕の身の周りのものだと、縦横比は3:2くらいまで、面積比は0.675から0.875くらいまであたりの設定で良い感じに検出されてくれました。
例えば電球とか、瓶や缶とかだと理想的なので検出されやすいと思います。
ところで、こういう値のリアルタイム調整にはofxParamEditが便利なんで使ってください。(宣伝)
以下ソースです。(抜粋)
(ofxOpenCvを使ってます)
// 輪郭抽出時のパラメータ(適当に初期化してください)
float area_min_;
float area_max_;
int area_count_max_;
int gray_threshold_;
// 丸いかどうかの判定に使用するパラメータ(適当に初期化してください)
struct BlobDetect {
float aspect_ratio_;
float area_ratio_min_;
float area_ratio_max_;
} blob_detect_;
ofPixels buf_; // カメラ入力画像データ
ofxCvColorImage cv_color_;
ofxCvGrayscaleImage cv_gray_;
ofxCvContourFinder contour_;
ofxCvContourFinder contour_inv_;
vector<ofxCvBlob*> valid_blobs_; // 有効な領域だけ保持しとく
Animation anime_; // ふにふにアニメーション
void update()
{
// カメラ画像をcvImageに読み込み
cv_color_.setFromPixels(buf_.getPixels(), buf_.getWidth(), buf_.getHeight());
// グレースケール変換
cv_gray_ = cv_color_;
// 二値化
cv_gray_.threshold(gray_threshold_);
// 輪郭抽出
contour_.findContours(cv_gray_, area_min_, area_max_, area_count_max_, false);
// 逆に二値化して再度チェック
cv_gray_ = cv_color_;
cv_gray_.threshold(gray_threshold_, true);
contour_inv_.findContours(cv_gray_, area_min_, area_max_, area_count_max_, false);
// 丸い輪郭だけ抽出
valid_blobs_.clear();
for(int i = 0; i < contour_.blobs.size(); ++i) {
if(isValidBlob(contour_.blobs[i])) {
valid_blobs_.push_back(&contour_.blobs[i]);
}
}
for(int i = 0; i < contour_inv_.blobs.size(); ++i) {
if(isValidBlob(contour_inv_.blobs[i])) {
valid_blobs_.push_back(&contour_inv_.blobs[i]);
}
}
// パカってする
ofPixelsRef pix = cv_color_.getPixelsRef();
for(vector<ofxCvBlob*>::iterator it = valid_blobs_.begin(); it != valid_blobs_.end(); ++it) {
const ofRectangle& rect = (*it)->boundingRect;
for(int x = rect.x; x < rect.x+rect.width; ++x) {
for(int y = rect.y; y < rect.y+rect.height/2; ++y) {
if(y-rect.height/2 < 0) {
continue;
}
pix.setColor(x,y-rect.height/2, pix.getColor(x,y));
pix.setColor(x,y, ofColor(0,0,0,0));
}
}
}
// テクスチャ更新
cv_color_.updateTexture();
}
// その領域が丸いかどうかの判定
bool isValidBlob(ofxCvBlob& blob)
{
// 縦横比
const ofRectangle& rect = blob.boundingRect;
if(rect.width < rect.height) {
if(rect.height/rect.width > blob_detect_.aspect_ratio_) {
return false;
}
}
else {
if(rect.width/rect.height > blob_detect_.aspect_ratio_) {
return false;
}
}
// 面積
float area_ratio = blob.area/(rect.width*rect.height);
if(area_ratio > blob_detect_.area_ratio_max_ || area_ratio < blob_detect_.area_ratio_min_) {
return false;
}
return true;
}
void draw()
{
ofPushMatrix();
ofPushStyle();
cv_color_.draw(0,0);
for(vector<ofxCvBlob*>::iterator it = valid_blobs_.begin(); it != valid_blobs_.end(); ++it) {
const ofRectangle& rect = (*it)->boundingRect;
anime_.draw(rect.x, rect.y, rect.width, rect.height/2);
}
ofPopStyle();
ofPopMatrix();
}
ちなみに、画像をパカっと切り取るところはシェーダーを使うとフレームバッファを消費することになるので、可搬性を考慮してCPUでやりましたが、あんまり気持ちよくはないのでうまい方法があれば教えてください。
Kinectとかのハードウェアからとってきた値を使うような処理をしていると、値の細かなブレが歓迎されない状況が多いので、ブレを吸収するためのテンプレートクラスを作ってみました。
組み上げたアプリへの導入が簡単になるよう、=で代入して右辺値で取り出すっていうシンプルな使い方にしていますので、目的の値の宣言をLazy<>で囲むだけで導入できます。
実際、すでに作った処理に対して組み込む作業を自分でもやってますが、かなり楽ちんです。
例えばfloatならLazy<float>、oFでofPointならLazy<ofPoint>にすればOK。
コンストラクタの第一引数か、もしくはsetSize関数でバッファの大きさを指定してやれば、それだけでブレ補正してくれるはずです。この値が大きいほど補正が強くなります。
以下軽く注意。
ちなみに、細かい設定はできませんがアニメーションにも使用できます。
以下のコードは0から1までの値を10分割補間して出力します。
※whlieの条件が環境によってはヤバそうですが、サンプルってことでご容赦を。
Lazy<float> value(10);
value = 0;
while(value < 1.0f) {
printf("%f\n", value);
value = 1.0f;
}
AmazonEC2 上の linux で Tower.js を試してみました。
Node.js を動かすところまでは検索でいろんな情報が見つかるんですが、Tower.jsで検索すると見つからなかったもので記録しておきます。
(別に特殊なことはないんですけどね・・)

npm list を見ると kue というパッケージで unmet dependencies があったので npm install kue して解決。
無事インストールできました。
(node_modules 内の node_modules へのインストールが危ういっぽい?)
ぱちぱちぱちぱち。