Noise01Test その1 簡単なノイズフィルタ
今回のネタは「ノイズフィルタ」ノイズは簡単に作成出来る割に、奥の深いエフェクト処理です。簡単にアルゴリズムを説明すると、ピクセルの値にランダムの数値を加算するもので、RGBチャンネルに同じ数値ならばモノクロノイズ、個別の数値ならばカラーノイズになります。SDK_noiseが参考になります。
まず作るノイズフィルタは、量と強さと二つのパラメータを持つシンプルなものから作成して、少しづつ機能を追加していきます。
パラメータは以下の意味になります。
- value(%)
ランダムノイズを発生させる確率(%)です。0でなし。100で必ずランダムの数値を加算します。具体的にはピクセルごとに乱数を発生させ、いのパラメータより乱数が小さい場合にのみピクセルにランダムな数値を加算します。
- “strong(%)
ノイズの強さをパーセントで指定します。100%にしても数値自体はランダムなので最大値に鳴らない可能性があり、数値の範囲を200%まで指定できます。加算するランダムな数値の値をこのパラメータで強弱をつけます。
- “used_color_noise
ノイズをカラーにするかモノクロにするか指定します。
とりあえずソースです。
Noise01Test.zipのダウンロード
前回まで画像のコピーをAfterEffectsのコールバック関数で行っていましたが、ちょっと気まぐれで自前のコピー関数を作成してあります。
ただの画像のコピーですが、画像データ処理の最も基本的なコーディングになっています。縦・横方向の2重ループでピクセルへアクセスしinput画像をoutput画像へコピーしています。アドレス計算は1ピクセルごとの加算、ラインごとに横幅の補正を行っています。ほとんどのエフェクト処理も同様なコーディングになります。
以下のコードがノイズ処理のメインとなっています。既にoutputに画像が入っている前提のコーディングで、先に説明したノイズのアルゴリズム実装したものです。
補足として、87行目のF_SRAND関数は、Macとの互換性をとるためにマクロ定義したもので、実体は標準関数のsrandです。
RAND_MAXの値がWindowsとMacと異なり、移植の際に苦労したので、マクロで再定義してあります。Windowsオンリならば標準関数そのまま使用してこのマクロを使う必要はありません。
srandには、カレントフレームの値をいれてランダムパターンをフレームごとに同じになるようにしています。
90行目の無駄っぽいループは、何故かランダム関数が一定パターンの繰り返しになってノイズというか格子状のパターンが出てしまったので、対処するために入れてあります。標準のrand関数は、かなり品質が悪いのでいつも悩みます。
110行目に計算が終わった値の範囲がunsigned charに収まるよう処理しています。ピクセルの値の計算時は必ず範囲のチェックを行う必要があります。
Noise01_Test 問題点 out_flagsの設定
とりあえず、Noise01Test.aexをリビルドして作成し、動作確認を行います。ノイズは問題なく表示されますが、静止フッテージ画像に対してこのプラグインを適応させラムプレビューをしてみてください。ラムプレビューを行うと分かりますが、ノイズが動きません。
フレーム番号でランダム関数の初期化を行ってるはずなので、これはおかしな結果です。
これはコーディングの問題ではなく、パラメータの変化がないため、AfterEffectsはキャシュされた画像を描画に使用してプラグインを呼び出さないのが原因です。
パラメータの変化がなくてもプラグインを毎フレームごとに呼び出すようにするには、out_flagsのフラグ値を設定する必要があります。
設定はすでにソースコードにコメントとしてあります。Noise01Test.cppの以下の部分を修正し、それに応じてNoise01TestPiPL.rの修正を行ってください。
Noise01Test.cppのGlobalSetup
Noise01TestPiPL.rのAE_Effect_Global_OutFlags
コメントアウトされているところを入れ替えリビルドを行ったものであれば、期待通りにラムプレビューでノイズが動いてくれるようになります。
このようにホスト(After Effects)の動作を設定する為にout_flagsの値を変更する必要があります。
out_flagsの値は、AfterEffect.hで定義されていて、コメントで説明されていますので、一度目を通しておいた方が良いです。
まぁ、通常のエフェクトプラグインならば今回用意した二つの値のどちらかで対応できます。
しかし、作ったプラグインの動作がおかしい場合はout_flagsの値を確認してみると良いです。
AE_out_flags.exe
out_flagsの値は、GlobalSetup関数とリソース定義ファイルの2箇所に記述しますが、リソース定義ファイルには、マクロ定数では無く値そのものを記述しなければいけないのが非常に面倒です。いちいち計算するのが馬鹿らしいので専用ツールを作成しました。
AE_out_flags.zipのダウンロード
ソースは、VC# 2008 express editionで作成ビルドしてあります。
AE_Effect_Version.exe
ヘッダーで定義しているプラグイン自体のバージョンも、out_flagsと同様に記述が必要なので、こちら用にも専用ツールを作成しました。AE_Effect_Version.zipのダウンロード
ソースは、同様にVC# 2008 express editionで作成ビルドしてあります。
プラグインの名前 Tips
ここで余談ですか、プラグインの名称について。メニューに表示されるプラグインの名称は、リソース定義ファイルの、Nameで設定できます。サブメニューの名前はCategoryになります。
(昔は、日本語表記にするだけでいろいろ障害がでましたが、最近では特に問題ないようです。まぁ、僕自身はもう日本語プラグイン名は懲り懲りなので似非英単語を採用してます)
唯一注意する点は、プラグイン名の末尾に半角数字をつけてはいけないことです。同じプラグインが登録された場合、末尾に数字をつけて区別できるように自動的にリネームされます。そのせいで元が半角数字で終わっているとその数字がただインクリメントされて、プラグインの名前が混乱してしまう事が多発します。
昔この為エライ苦労したことがります(市販のプラグインで一見半角数字で終わってるものがあったりしますが、こっそり末尾にピリオド入ってたりとか苦労の跡が見えて笑ってしまうこともあります)
バージョン管理為に、表示名にバージョン入れるとかすると結構はまります。最近では僕は、表示名ではなくカテゴリ名(サブメニュー名)に情報を入れて管理してます。
Noise02Iterate PF_ITERATE
Noise01Teast.aexでは、アドレスの計算を自前のコードで行ってますが、AdobeのSDKではなるべく直接画像にアクセスしないで、Iterateというコールバック関数を介してピクセルにアクセスしなさいという記述があります。僕は好みで上記の方法で画像処理していますが、Iterateを使ったアクセス方法も覚えておいた方がいいのでここで説明します。
とりあえず、以下にNoise01Teastを単純にIterateを使ったものに変えたもの(Noise02Iterate)をあげます。
Noise02Iterate.zipのダウンロード
機能はNoise01Testとまったく同じです。
以上がIterateの使用サンプルです。一見するとよくわかりませんが、まぁ要するに適当なパラメータを与えて実行すれば全ピクセルへのアクセスを自動的にするって機能です。
ピクセル単位の処理は別に関数で実装し、その関数ポインタを引数の一つとしています。
関数ポインタはここでは説明しませんので、適当に調べてください。かなり便利な機能で覚えるとプラグインの高速化等で役に立ちます。
以上がIterateで呼び出される関数です。引数・引数の変数型は変更できません、
引数順 | 引数の型 | 引数の説明 |
---|---|---|
第1引数 | A_long refcon | A_longですが、実際はポインタ。 PF_ITERATEの第5引数で指定された変数の先頭アドレスになります。 |
第2引数 | A_long x | ターゲットピクセルのX座標 |
第3引数 | A_long y | ターゲットピクセルのY座標 |
第4引数 | PF_Pixel *inP | インプット画像のターゲットピクセルへのポインタ |
第5引数 | PF_Pixel *outP | アウトプット画像のターゲットピクセルへのポインタ |
refconに必要なパラメータを収納した変数(構造体)を設定すれば、動作に必要なパラメータをこの関数へ引き渡すことができます。
Iterateを使った処理はポインタを理解してないとただ面倒なだけですが、実際はかなり効率的でスマートな操作をユーザーに提供します。
アドレスの計算をやってくれる他にも、After Effectsのいろいろな機能に自動的に対応しますので、なるべく使ったほうがよいです。
まぁこの講座では、基本の説明ためIterateはほぼ使いません。
Noise03Lv
Noise01Testはあまりにも簡単なので、もう少し凝った機能を実装してみます。という事で、Noise03Lvは元画像の明るさに応じてノイズの強さを変えられるプラグインにします。
(昔、画面の明るいところと暗いところでノイズの強さを変えてくれというオーダーがあり、面倒なコンポジションを組んだ事を思い出したので)
とりあえず、ソース。
Noise03Lv.zipのダウンロード
パラメータは四つ。
- value(%)
これはNoise01Testのものと同じもの。
- hi(%)
明るい部分のノイズの強さ。
- mid(%)
中間部分のノイズの強さ。
- low(%)
暗い部分のノイズの強さ。
アルゴリズムは以下の様に。
- 画像の明るさを識別するために3個のテーブル用配列を確保。
- テーブルの初期化
- ピクセルの明るさを計算
- ピクセルの明るさをテーブル参照で、明るい部分の強度・中間部分の強度・暗い部分の強度を求める
- おのおのの強度とパラメータの数値を考慮して、そのピクセルのノイズの強さを計算
- ノイズを描画
- 3以後の処理を全ピクセルで実行
テーブルとは、ピクセルの強さから明暗を識別する計算をあらかじめ終わらせておいて、配列にまとめたものになります。通常は高速化のテクニックの一つですが今回はコーディングの量を減らすために使ってます。
MainRenderでは、テーブル用のメモリを動的に確保しています。メモリ確保に使うコールバック関数PF_NEW_HANDLEは、バイト数でメモリを確保して、確保されたメモリの先頭アドレスをハンドル(ポインタのポインタ)で返します。ハンドルはかなり難しい概念ですがでここではこんな感じに使うって覚えれば大丈夫です。
3個のテーブルを準備してますが、PF_NEW_HANDLEで三つ分まとめて確保して、先頭アドレスを修正して三個の変数に割り振るっています。
使い終わったらPF_DISPOSE_HANDLEでメモリを開放しています。JavaScriptのような便利な言語だとガベレージ機能で勝手に開放してくれますが、C/C++の場合はちゃんと自分で管理しないといけません。
テーブルの初期化では、以下の定数を設定しています。
- LV_LOW_OVR
完全に暗い明るさ。25%に設定。
- LV_MID_TOP
中間職の始まりの明るさ。40%に設定。
- LV_MID_BTM
中間職の終わりの明るさ。70%に設定。
- LV_HI_OVR
完全に明るい明るさ。80%に設定。
- LV_MAX
テーブルの数値の最大値1024に設定。
以上の数値を使いテーブルを初期化します。
テーブルの数値は、明るい部分の強度・中間部分の強度・くらい部分の強度をピクセルの明るさの数値全て(8bitの場合256)で計算したものです。
16bit化を考えて定数をパーセントとして設定していますので、8Bitの場合の数値に変換し、適当にリニア補完で計算しています。
以下がノイズ処理のメインです。
121行目でピクセルの明るさを計算しています。今回はYUV変換のYの値を明るさとしています。この辺のことはYUVを検索すれば詳細な解説があるHPを見つけられます。
124行目でランダムな数値を計算していて、数値は-255から255の間としています。
126行目で明るさの数値をテーブル参照・パラメータの数値から各レベルの強度を計算しています。130行目で判定を行っていて、一番強度を強いものを採用しています。
131行目でランダムの数値を130行目で求めた強度で補正しています。
132行目でランダム処理です。Noise01Testと同じ処理ですが、関数化してシンプルにしてます。
所々に入っているF_RAND関数は、条件分岐によって分岐されてもF_RANDの実行回数が変わらないようにするためのものです、。
上図がカラーカーブで作成したグラデーションに適応させた物になります。
Noise04Lv 改造
Noise03Lvは解説用に作ったとはいえ、かなりの問題があります。具体的には、以下の2点があげられます。
- 明るさの閾値が固定でかついい加減
明るさの識別処理はあまりにもシンプルすぎて、実際の使用には耐えられそうに無い。
- ノイズの品質が悪い
基本的に1ピクセルのノイズなので、パラメータを強くするとギザギザしたジャギっぽい質感になり、弱くすると最近のHD画像では消えてしまう可能性がある。
ここでは、ノイズの品質にのみ対処してみます。といっても、複雑なことはしないで、ノイズに軽くぼかし処理を加えるだけの簡単な処理してみます。
で、作成したものがこれになります。
Noise04Lv.zipのダウンロード
変更点は、手順を変えてノイズの数値だけを元画像と加算しないでとりあえずアウトプット画像に描画し、ノイズのみにぼかし処理を加え、その後に元画像と合成したことです。
まず、ノイズの値を全ピクセル分計算しておきます。
ノイズの値を一時退避としてアウトプット画面に代入しています。値の範囲は-255から255の間となるのでredとgreenの2チャンネル分使って保存しています。値がマイナスなら、redへプラスならgreenへ値を書き込んでおきます。
Noise3Lvでカラーノイズをなくしたのはこの処理を想定していたからです。RGB個別のノイズを行う場合にはもうメモリが足りないので、ノイズの値を小さくするか、作業バッファーを別に作成する必要があります。
本来なら、退避用のメモリを確保するところですが、メモリ節約のためこのような処理にしています。
ちなみに、アウトプット画面はこのような使い方をしても大丈夫ですが、インプット画面ではいろいろと制約があります。インプット画面はAfter Effectsのイメージキャッシュそのものなので、インプット画面に書き込みを行うと、描画がおかしくなります。一応、out_flagsでフラグを設定することで回避できますが、イメージキャッシュを毎回クリアされてしまうので、効率悪くなるので使うなとSDKでは明記されてます。
全画面分のノイズの値にぼかし処理を加えます。red/greenチャンネルの値を処理して計算結果をblue/aplhaのチャンネルに書きこみます。
ぼかしのアルゴリズムは、ピクセルの周囲9ピクセル分の平均を求める簡単なものですが、一応ピクセルの位置によって重み付けを変えて計算しています。
また、9ピクセル分ほとんど同じコードが並ぶことになるので、マクロでNOISE_BLUEを定義してコーディング量を減らしています。関数化しても同様な事は行えますが、繰り返しが多くなる画像処理ではオーバーヘッドによる速度低下を防ぐ意味でも、マクロ定義は有効です。
ノイズにぼかしを加えたら、元画像と合成して終了です。
After Effectsで動作を確認してみます。
単純なぼかしですが、見た目はかなり変わりました。Noise03Lvと比較してみると以下のようになります。
比べてみると、品質を上げたというより、単純にぼかしただけって印象になってますが(プログラムもそのとおり)、HDサイズで作業していて放送・販売時にSDサイズにダウンコンされる場合には、効果があるというかダウンコンによる印象の変化が少なくなると思います。
次回予告
次回は、画像の描画を詳しく説明しようと思ってます。「簡易スッパッタリング描画フィルタを作ってみよう!」です。
新着記事 : アニメ制作者のためのAfterEffectsプラグイン作成入門(第4回) オリジナルノイズフィルタを作ってみよう! http://bit.ly/9JENTD
RT @AEUSERS: 新着記事 : アニメ制作者のためのAfterEffectsプラグイン作成入門(第4回) オリジナルノイズフィルタを作ってみよう! http://bit.ly/9JENTD
RT @AEUSERS アニメ制作者のためのAfterEffectsプラグイン作成入門(第4回) オリジナルノイズフィルタを作ってみよう! http://bit.ly/dzPA6Z #aejp
RT @AEUSERS: 新着記事 : アニメ制作者のためのAfterEffectsプラグイン作成入門(第4回) オリジナルノイズフィルタを作ってみよう! http://bit.ly/9JENTD
Now browsing:アニメ制作者のためのAfterEffectsプラグイン作成入門(第4回) オリジナルノイズフィルタを作ってみよう! | AEP Project… http://bit.ly/ahcZV8