カラーコレクション

カラーコレクション 

今度は最低限のカラーコレクション(ColorCorrection)を実装します。 カラーコレクションを行うことも踏まえて,今までのフローをおさらいします。

f:id:hikita12312:20170913225016p:plain

カラーコレクションを行う場合は,トーンマッピング前後における,HDR空間({0\sim\infty})かSDR空間({0\sim1})でのどちらかで行うことになると思います。 HDR空間でカラーコレクションを行うと,1.0以上の高輝度成分もトーンマッピングで良い感じにサチって白飛びなどが目立たなくなりますが, 非線形なトーンマッピング変換によって,色変換が直感的ではなくなって色味が変わったものになります。 SDR空間でカラーコレクションを行えば直感的ですが,白飛びが起こりやすくなります。

本記事では,SDR空間でカラーコレクションを行うこととします。

明度変換

{ \displaystyle
b : \mathrm{brightness} \\
\begin{eqnarray}
\left(\begin{array}{c}r'\\g'\\ b'\\ 1\end{array}\right)=
\left(  
\begin{array}{cccc}
b & b & b & 0 \\
b & b & b & 0 \\
b & b & b & 0 \\
0 & 0 & 0 & 1
\end{array}
\right)
\left(\begin{array}{c}r\\ g\\ b\\ 1\end{array}\right)
\end{eqnarray}
}
f:id:hikita12312:20170914100150p:plain
{b = 0}
f:id:hikita12312:20170914100214p:plain
{b = 2.0}

色変換を行うときに4x4の行列を用いて線形変換を行う方法を利用します。 下のサイトの行列変換を参考にしました。

https://docs.rainmeter.net/tips/colormatrix-guide/

見たままですが,RGB空間に対して全ての成分を{b}倍にスケールすることで,明るさを変換できます。

彩度変換

{ \displaystyle
s : \mathrm{saturation} \\
{\bf W} = (0.298912, 0.586611, 0.114478) \\ 
{\bf S} = (1-s){\bf W} \\
\begin{eqnarray}
\left(\begin{array}{c}r'\\g'\\ b'\\ 1\end{array}\right)=
\left(  
\begin{array}{cccc}
S_r + s & S_g & S_b & 0 \\
S_r & S_g + s & S_b & 0 \\
S_r & S_g & S_b + s& 0 \\
0 & 0 & 0 & 1
\end{array}
\right)
\left(\begin{array}{c}r\\ g\\ b\\ 1\end{array}\right)
\end{eqnarray}
}
f:id:hikita12312:20170914100150p:plain:w300
{s = 0}
f:id:hikita12312:20170914100504p:plain:w300
{s = 2.0}

{\bf W}の重みは,輝度を算出するときに利用されるNTSC係数です。 https://ja.wikipedia.org/wiki/NTSC ネットで調べると,G成分が0.7ぐらいの値のものが出てくることがありますが,それはガンマ補正を考慮した結果の重みで, 今回はガンマ補正前なので,この重みで輝度を算出します。
s=0のときが,いわゆるグレースケールとなります。sでグレースケールと入力色の線形補間で彩度変換を表現しているようです。

コントラスト変換

{ \displaystyle
c : \mathrm{contrast} \\
t = (1-c)/2 \\
\begin{eqnarray}
\left(\begin{array}{c}r'\\g'\\ b'\\ 1\end{array}\right)=
\left(  
\begin{array}{cccc}
c & 0 & 0 & t \\
0 & c & 0 & t \\
0 & 0 & c & t \\
0 & 0 & 0 & 1
\end{array}
\right)
\left(\begin{array}{c}r\\ g\\ b\\ 1\end{array}\right)
\end{eqnarray}
}
f:id:hikita12312:20170914100150p:plain:w300
{c = 0}
f:id:hikita12312:20170914101003p:plain:w300
{c = 1.5}

cで灰色と入力色の線形補間をしているようです。

単色フィルタ

{ \displaystyle
{\bf C} : \mathrm{color} \\
s : \mathrm{scale} \\
{\bf W} = (0.298912, 0.586611, 0.114478) \\ 
\begin{eqnarray}
\left(\begin{array}{c}r'\\g'\\ b'\\ 1\end{array}\right)=
\left(  
\begin{array}{cccc}
(1-s)+sW_rC_r & sW_gC_r & sW_bC_r & 0 \\
sW_rC_g & (1-s)+sW_gC_g & sW_bC_g & 0 \\
sW_rC_b & sW_gC_b & (1-s)+sW_bC_b & 0 \\
0 & 0 & 0 & 1
\end{array}
\right)
\left(\begin{array}{c}r\\ g\\ b\\ 1\end{array}\right)
\end{eqnarray}
}
f:id:hikita12312:20170914100150p:plain:w300
{s = 0}
f:id:hikita12312:20170914101105p:plain:w300
セピア化

{\bf C}と,その色への変化度合い{s}が与えられたときの,単色への変換です。 {\bf C}が茶色っぽい色の場合は,いわゆるセピアトーンになります。 {s=0}のときは,単位行列になり,{s=1}のときは,マスク色{\bf C}と輝度の乗算となります。

色相変換

{ \displaystyle
\begin{eqnarray}
\left(\begin{array}{c}r'\\g'\\ b'\\ 1\end{array}\right)=
R(\theta)
\left(\begin{array}{c}r\\ g\\ b\\ 1\end{array}\right)
\end{eqnarray}
}
f:id:hikita12312:20170914100150p:plain:w300
{\theta = 0.0^\circ}
f:id:hikita12312:20170914101146p:plain:w300
{\theta = 180^\circ}

{R(\theta)}は軸{\frac{1}{\sqrt{3}}(1,1,1)}を中心とした回転行列です。
RGB空間を各軸の中心方向の軸に対して回転をさせれば,色相が変換できます。

LUT

今までの変換は,全て4x4の行列を利用した線形変換でした。 しかしながら,実際に色変換を行いたい場合は非線形の変換を多数行うことになるでしょう。 非線形変換を行う為にシェーダーに変数を追加したり,シェーダー自体を書き換えたりすれば,シェーダーバイナリは膨れ上がりますし,計算コストも増大していく一方です。

そこで,16x16x16のような立方体サイズの3次元テクスチャを用意して,その各軸を0から1までのRGBと捉え, SDR空間において入力色の座標から線形補間しながら出力色をフェッチするLUT(Look Up Table)を利用します。 事前に様々な変換を施したLUTを作成しておくことで,再現性が高く,高速に色変換が可能です。 もちろん,サイズ16ではなく,サイズ32,64などに増やしていけば精度が上がります。

本来はフォトショなどでLUTを作成しますが,UnrealEngineのドキュメントにテスト用LUTがあったので,拝借します。

https://docs.unrealengine.com/latest/JPN/Engine/Rendering/PostProcessEffects/ColorGrading/index.html

f:id:hikita12312:20170914102504p:plain

このテクスチャを3Dテクスチャに変換して,線形補間した結果です。

f:id:hikita12312:20170914100150p:plain:w300
変換前
f:id:hikita12312:20170914102607p:plain:w300
変換後