111

線形 RGB 空間の基本特性と非線形 RGB 空間の基本特性は何ですか? これらの 8 ビット (またはそれ以上) の各チャネル内の値について話すとき、何が変わるのでしょうか?

OpenGL では、色は 3+1 の値であり、これは RGB+alpha を意味し、各チャネルに 8 ビットが予約されています。これは私が明確に理解している部分です。

しかし、ガンマ補正に関しては、非線形 RGB 空間で作業した場合の効果がわかりません。

写真編集用のグラフィックソフトウェアで曲線を使用する方法を知っているので、私の説明は、線形RGB空間では、非線形の場合の代わりに、操作も数学関数も添付せずに値をそのまま取得するということです。チャネルは通常、古典的なべき乗関数の動作に従って進化します。

この説明を本当の説明として受け取ったとしても、実際の線形空間が何であるかはまだわかりません。計算後、すべての非線形 RGB 空間が線形になり、何よりも重要なのは、非-最終的にすべてのRGB空間が私が理解しているものに対して線形であるため、線形色空間は人間の目により適しています。

4

2 に答える 2

273

RGB カラーを扱っているとしましょう。各カラーは 3 つの強度または明るさで表されます。「リニア RGB」と「sRGB」のどちらかを選択する必要があります。ここでは、3 つの異なる強度を無視して単純化し、強度が 1 つだけであると仮定します。つまり、グレーの色合いだけを扱っているとします。

線形色空間では、格納する数値とそれらが表す強度との関係は線形です。実際には、これは数値を 2 倍にすると、強度 (グレーの明度) が 2 倍になることを意味します。2 つの強度を一緒に加算したい場合 (2 つの光源の寄与に基づいて強度を計算している、または不透明なオブジェクトの上に透明なオブジェクトを追加しているなどの理由で)、単純に2 つの数字を一緒に。あらゆる種類の 2D ブレンディングや 3D シェーディング、またはほぼすべての画像処理を行っている場合は、強度をリニア カラー スペースにする必要があります。、したがって、数値を加算、減算、乗算、および除算するだけで、強度に同じ効果を与えることができます. ほとんどの色処理およびレンダリング アルゴリズムでは、すべてに重みを追加しない限り、線形 RGB でのみ正しい結果が得られます。

それはとても簡単に聞こえますが、問題があります。光に対する人間の目の感度は、高強度よりも低強度の方が優れています。つまり、識別できるすべての強度のリストを作成すると、明るい強度よりも暗い強度のほうが多いということです。別の言い方をすれば、暗いグレーの色合いは、明るいグレーの色合いよりもよく区別できます。特に、強度を表すために 8 ビットを使用していて、これを線形色空間で行う場合、最終的に明るい色合いが多すぎて、暗い色合いが不十分になります。暗い領域ではバンディングが発生しますが、明るい領域では、ユーザーが区別できないさまざまな色合いの白に近いビットが無駄になっています。

この問題を回避し、これらの 8 ビットを最大限に活用するために、sRGBを使用する傾向があります。sRGB 標準は、色を非線形にするために使用する曲線を示します。曲線は下の方が浅いので濃いグレーが多く、上の方が急なので明るいグレーが少なくなります。数値を 2 倍にすると、強度が 2 倍以上になります。これは、sRGB カラーを一緒に追加すると、必要以上に明るい結果になることを意味します。最近では、ほとんどのモニターが入力カラーを sRGB として解釈します。そのため、画面に色を配置したり、チャネルごとに 8 ビットのテクスチャに色を保存したりする場合は、色を sRGB として保存して、 8 ビットを最大限に活用します。

ここで問題があることに気付くでしょう。色を線形空間で処理したいのですが、sRGB に保存したいのです。つまり、読み取り時に sRGB から線形への変換を行い、書き込み時に線形から sRGB への変換を行うことになります。線形の 8 ビット強度には十分な暗さがないと既に述べたように、これは問題を引き起こすため、もう 1 つの実用的なルールがあります。回避できる場合は、8 ビットの線形カラーを使用しないでください。8 ビット カラーは常に sRGB であるという規則に従うのが慣例になりつつあるため、強度を 8 ビットから 16 ビットに、または整数から浮動小数点に拡大すると同時に、sRGB から線形への変換を行います。同様に、浮動小数点処理が終了したら、sRGB への変換と同時に 8 ビットに絞り込みます。これらのルールに従えば、

sRGB 画像を読み取っていて、線形強度が必要な場合は、次の式を各強度に適用します。

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

逆に、画像を sRGB として書き込みたい場合は、次の式を各線形強度に適用します。

float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )

どちらの場合も、浮動小数点の値の範囲は 0 から 1 であるため、8 ビット整数を読み取る場合は最初に 255 で割り、8 ビット整数を書き込む場合は 255 を掛けます。最後に、通常と同じ方法で。sRGB を使用するために知っておく必要があるのはこれだけです。

これまでは 1 つの強度だけを扱ってきましたが、色にはもっと賢い方法があります。人間の目は、さまざまな色合いよりもさまざまな明るさを区別することができます (より技術的には、クロミナンスよりも輝度解像度の方が優れています)。そのため、色合いとは別に明るさを保存することで、24 ビットをさらに有効に活用できます。これは、YUV、YCrCb などの表現がやろうとしていることです。Y チャネルは色の全体的な明るさであり、他の 2 つのチャネルよりも多くのビットを使用します (または空間解像度が高くなります)。この方法では、RGB 強度の場合のように曲線を適用する必要は (常に) ありません。YUV は線形色空間であるため、Y チャネルの数値を 2 倍にすると、色の明度が 2 倍になりますが、RGB 色のように YUV 色を加算または乗算することはできません。

これであなたの質問に答えられると思いますので、簡単な歴史的なメモで終わります。sRGB が登場する前は、古い CRT には非線形性が組み込まれていました。ピクセルの電圧を 2 倍にすると、強度は 2 倍以上になります。モニターごとにどれだけ異なるか、このパラメーターはガンマと呼ばれていました。この動作は、明るい部分よりも暗い部分をより多く取得できることを意味するため便利でしたが、最初に調整しない限り、ユーザーの CRT で色がどの程度明るいかがわからないことも意味していました。ガンマ補正は、最初の色 (おそらく線形) を変換し、ユーザーの CRT のガンマに合わせて変換することを意味します。OpenGL はこの時代に生まれたため、その sRGB の動作が少し混乱することがあります。しかし、GPU ベンダーは現在、上で説明した規則に従って作業する傾向があります。テクスチャまたはフレームバッファーに 8 ビットの強度を格納する場合は sRGB であり、色を処理する場合はリニアです。たとえば、OpenGL ES 3.0 では、各フレームバッファとテクスチャに「sRGB フラグ」があり、オンにすると、読み取りおよび書き込み時に自動変換を有効にできます。sRGB 変換やガンマ補正を明示的に行う必要はまったくありません。

于 2012-10-15T10:50:50.380 に答える
1

私は「人間の色検出の専門家」ではありませんが、YUV->RGB 変換で同様のことを経験しました。R/G/B チャンネルには異なる重みがあるため、ソース カラーを x で変更すると、RGB 値は異なる量を変更します。

前述のように、私は専門家ではありませんが、とにかく、色を正しく変換したい場合は、YUV 空間で変換してから RGB に変換する必要があります (または RGB で数学的に同等の操作を行うことに注意してください)。のデータ損失)。また、YUV が色の最適なネイティブ表現であるかどうかはわかりませんが、ビデオ カメラはその形式を提供しており、そこで問題に遭遇しました。

これは、秘密の数字が含まれた魔法の YUV->RGB 式です: http://www.fourcc.org/fccyvrgb.php

于 2012-10-15T09:58:51.593 に答える