Xで反響が大きかった、AfterEffects の「エクスプレッションTIPS#18 指定位置の色を取得」の解説記事を書いていきたいと思います。
今回説明するのは、以下のポストのエクスプレッションについてです。動画を再生すると指定した位置の色を取得する様子がわかると思います。
sampleImageとは?
エクスプレッション「sampleImage」は、指定レイヤーから色を取得するためのエクスプレスプレッションです。
例えば、同一コンポジション内に、movieという名前のレイヤーがある場合、以下のように記述すことで色を取得できます。
thisComp.layer("movie").sampleImage([960, 540]);
今回のXのポストでは、色を取得する位置と、色を取得する範囲しか使っていませんが、他にも出来ることがあるので順に説明していきたいと思います。
まず、以下が全てのパラメーターを設定する場合の書き方です。
thisComp.layer("movie").sampleImage([960, 540], [0.5, 0.5], true, time)
- 第1引数・・・色の取得位置
- 第2引数・・・色の取得範囲(デフォルト値:[0.5, 0.5])
- 第3引数・・・エフェクトやマスク適用前か後かを指定(デフォルト値:true)
- 第4引数・・・色の取得時刻(デフォルト値:time)
なお、第1引数以外は省略可能で、省略した場合は、デフォルト値になります。詳しくはこの後解説していきます。
本記事はadobe公式のエクスプレッション言語リファレンスに掲載してある仕様を基に、私が検証した内容を加え、解説したものになっています
第1引数
第1引数と第2引数はポストで説明してある通りですが、具体的にどのピクセルをどの範囲で取得するのか?について補足説明しておきます。
説明をわかりやすくするために、4px×4pxのコンポジションに、4px×4pxのmovieという名前のレイヤーを配置したという前提で説明していきます。また、エクスプレッションを記載するのは、カラープロパティなら何でも大丈夫です。
movieレイヤーは以下のように、1px毎に違う色になっているとします。位置は[0, 0]~[3, 3]となります。
sampleImageの場合は、AfterEffectsの通常の座標系とは少し異なり、色の取得位置はピクセルの中心となります。例えば、下記のように、色取得位置を[2, 2]、取得範囲を[0.5, 0.5]とした場合は、[2, 2]の1ピクセル分となります。
また、取得範囲を[1.5, 1.5]にした場合は[2, 2]のピクセルを中心とした9ピクセル分の範囲となり、その平均値となります。
※ 実際にこの9つの色を足して、9で割ると、このsampleImageの結果と同じになります
厳密にピクセル単位で色を取得したい場合は、上記のように範囲を0.5刻みにする必要がありますが、通常はxとyの値を2倍したものが実際の色取得範囲になるということだけ覚えておけば大丈夫です。
ちなみに、以下のように中途半端な範囲になったとしても、面積に応じて平均色を計算してくれます。
※ こちらも面積比率に応じて平均色を計算すると、このsampleImageの結果と同じになります
なお、以下のように第2引数を指定しない場合は、デフォルト値の[0.5, 0.5]になるので、1ピクセルのみを取得する動作になります。
thisComp.layer("movie").sampleImage([2, 2]);
第3引数
第3引数は、エフェクトやマスク適用前の色を取得するか、適用後の色を取得するかを指定できます。
エフェクトやマスクを適用した後の色を取得したい場合はtrue、適用する前の色を取得したい場合は、falseを指定します。
例えば、レンズフィルターエフェクトを適用して、以下のように色を変えても、第3引数をfalseに設定すれば、エフェクト適用前の色を取得できます。
そして、trueにすればエフェクト適用後の色を取得することができます。
ちなみに、以下のように第3引数を省略した場合、デフォルト値はtrueなので、エフェクト適用後の色を取得する動作になります。
thisComp.layer("movie").sampleImage([2, 2], [0.5, 0.5]);
第4引数
第4引数は、取得する時刻を指定できます。
例えば、movieレイヤーの5秒地点の色を取得したい場合は、5を指定すると、時間の経過に関係なく、常に5秒地点の色を取得するようになります。
thisComp.layer("movie").sampleImage([2, 2], [0.5, 0.5], true, 5);
以下のように第4引数を省略した場合、デフォルトはtimeなので、時間の経過とともに、その時刻の色を取得するようになっています。
thisComp.layer("movie").sampleImage([2, 2], [0.5, 0.5], true);
応用編
最後に、実はsampleImageを実行した結果は、RGBではなく、アルファチャンネル(透過情報)も含んだRGBAになっています。
※範囲を広げた場合は、アルファチャンネルもRGBと同様に平均値が計算されます
このRGBAで取得する値は0~1(0~255を255で割った数)となります。例えば、取得したRBGAが#BC60BBFFだった場合は、上記のように[0.737, 0.376, 0.733, 1.000]となります。この最初の3つの値がRGBで上記の取得結果の色となり、4番目の値1.000がアルファで、不透明度100%という意味になります。
ちなみに、アルファが0で、完全に透明な場合、色は取得されません。
また、movieレイヤーの全てのピクセルのアルファが0.5だった場合は、色の取得結果は変わらず、アルファの値だけが0.5になります。
そのため、sampleImageでアルファの値0~1を取得後、100倍し、0~100の値に変換すれば、不透明度プロパティの形式にすることができます。
以下がそのエクスプレッションになります。これを不透明度プロパティに書き込めばアルファも反映することができます。
thisComp.layer("movie").sampleImage([2, 2])[3] * 100;
※[3]でRGBAの4番目のアルファ要素を取得。
これにより、例えば、sampleImageで取得したアルファが0.5なら、不透明度が50%になります。
まとめ
エクスプレッションの中でも唯一無二の存在のsampleImageを紹介しました。
背景色の取得するだけでなく、全く別のカラーマップから色を取得しても、面白い表現ができるので色々試してみてください。
細かい仕様を忘れたらこの記事を見返してもらえればと思います。
ちなみに、だいぶ昔に作った動画ですが、sampleImageの全引数の動作を動画にまとめたものもあるので、興味のある方はご覧ください。
本記事の感想やコメントなどありましたら、Xのポストに返信などいただければと思います!