rgb
GrayScale
ガンマ補正

グレースケール画像のうんちく

グレースケール変換する際の前提知識から実際の変換アルゴリズムまで解説します。
カラー画像を元に白黒印刷したいときや、最近だと機械学習の特徴抽出で輝度だけ使いたい等、意外とよくグレースケール化を実装する機会があります。
大抵はグラフィックエンジンのグレースケール化APIを使うだけですが、具体的にどういう処理をしているのか、知っておいて損はないです。

グレースケールとは

画像を色味のない明るさの度合いだけで表現するのがグレースケールです。
似た言葉にモノクロ画像がありますが、こちらは下のカラー以外3つを含む広い概念です。バイナリ画像(白と黒のどちらかだけで中間のグレーがない)を指す事もありますし、色味がつくモノトーンかもしれません。

カラー画像 グレースケール画像 バイナリ画像 セピア調モノクロ画像
image.png image.png image.png image.png
image.png image.png image.png image.png

カラー画像は1つのピクセルに複数の値が必要で、R,G,Bの3つの値を用いる事が多いのですが、グレースケール画像だと1つのピクセルに1つの値で十分です。
その1つの値に、モニタでは Y(輝度、CIE XYZ の Y軸)、L(Luminance)、印刷だと K(Key 又は blacK)、より抽象的には V(Value、色価)、Intensity(強度) といった文字や用語がよく用いられます。

color pixel grayscale pixel
4x4color.png 4x4grayscale.png

(OpenCV の imshow 関数を使った表示)

つまりグレースケール変換は、色成分RGB(又はCMYK等)が持つ複数の値から人の眼が感じる色の強さを推定して1つの値にまとめる作業と言えます。

前提知識

R,G,B は同じ値でも明るさが違う

R,G,B は3つ同じ値で加色混合すると無彩色(黒〜グレー〜白)になるよう輝度がバランス調整されています。一番右のように、同じ明るさで混ぜると青紫方向に色味がかります。

R=G=B=255 R=G=B=187(sRGBで輝度半分相当) R:165,G:121,B:255(明るさを揃える)
image.png image.png !image.png

つまり、同じ値でも R,G,B をバラバラに見ると、明るさが各々で異なります。
具体的にはG(緑)が明るく、R(赤)は暗く、B(青)はとても暗く見えます。

カラー画像 良いグレースケール (CIE XYZ) 悪いグレースケール (RGB平均)
image.png image.png image.png

グレースケール変換する時には、この色成分R,G,B毎の明るさ(輝度)の違いを考慮する必要があります。

ガンマ補正で平均が歪む

一般的な画像ファイルから取り出せる RGB 値はピクセルの物理的な輝度と素直に比例しません。昔のブラウン管テレビが入力信号の電圧に対して出力する輝度が比例しなかったので、信号側で補正する事にしたそうです。

image.png
(c) https://ja.wikipedia.org/wiki/ガンマ値

このグラフで言うと、入力信号に対して CRT gamma 曲線のように輝度が下に沈み込む特性を想定して、RGB値の方を gamma correction の点線のように釣り上げる補正をします。すると、CRT では何も補正せずとも輝度リニアに表示するという理屈です。

このガンマ補正された R,G,B の値のまま平均を計算すると、本物の輝度の平均にはなりません。

image.png

ガンマ補正を考慮せずにR,G,Bの輝度値の平均をとると、実際の輝度よりも暗くなります。

カラー画像 嘘 CIE XYZ(ガンマ処理無し) CIE XYZ(ガンマを考慮した版)
image.png image.png image.png

この真中の図のように特に彩度が高い色に影響が現れます。以下のようにガンマ値解除の手続きをする事で、右図のような結果に出来ます。

  • R,G,B値にかかっているガンマ補正を外して、物理的な輝度と比例の関係にする (linear-RGB 化)
  • やりたい輝度計算をする。(グレースケールだとR,G,Bを混ぜる計算)
  • その後またガンマ補正をかけ直して元に戻す

ちなみに、グレースケール以外でも回転やリサイズ等の座標変換で補間処理がある時にも同じ事が言えます。

RGB からグレースケールへ変換

R,G,B 3つの値をどうやって 1つの値にまとめるのか。幾つか代表的な方法を紹介します。

RGB平均

V = (R + G + B) / 3.0

処理は単純ですが、前提知識で述べた2つの問題があって、実感と合わない結果になりがちです。

元画像 平均
image.png image.png

右下の緑とか論外ですね。^^;

RGB平均 (ガンマ補正)

ガンマ補正を考慮した変換をしてみます。

(R',G',B') = 逆ガンマ補正(R,G,B)
V' = (R' + G' + B') / 3.0
V = ガンマ補正(V')
元画像 逆ガンマ補正 グレースケール変換 ガンマ補正
image.png image.png image.png image.png

これで大分よくなりますが、それでも色々と食い違っています。

CIE XYZ の Y

CIE XYZ は人の眼の視覚特性を等色実験で調べて CIE(国際照明委員会)がまとめたRGBとは別の軸を持つ色空間の規格です。
XYZ 軸の色空間で、Y 軸が輝度(明るさ)にあたります。

詳しくはこちらを参考にどうぞ

この CIE XYZ を使った方法だと、ガンマ補正を考慮した上、更に R,G,B を足す際に重み付けをする(加重平均)事で、前提知識にある問題を2つとも解決します。

(R',G',B') = 逆ガンマ補正(R,G,B)
V' = 0.2126*R' + 0.7152*G' + 0.0722*B'
V = ガンマ補正(V')
元画像 逆ガンマ補正 グレースケール変換 ガンマ補正
image.png image.png image.png image.png

人の眼の色の捉え方には個人差がありますが、おそらく多くの人はこのグレースケールに納得できるでしょう。
また、カラーマネジメントの観点でも sRGB 以外の P3 や AdobeRGB 、ProPhoto といった色域やガンマ値が異なる色空間でも XYZ への変換は ICC プロファイルで定義されているので、適切な CMM(カラーマネジメントモジュール) を使えば輝度は楽に取得できます。

デジタル放送の BT.709 も同じ加重平均ですし、sRGB (Webで流通している画像の殆どがこれ)も同じなので、これが決定版だ! …と言いたいところですが、そうとも言い切れないので記事の最後で少し解説しておきます。

ITU-R Rec BT.601

(R',G',B') = 逆ガンマ補正(R,G,B)
V' = 0.299*R' + 0.587*G' + 0.114*B'
V = ガンマ補正(V')

CIE XYZ と並んでメジャーな計算式を持つ BT.601 は SDTV の規格です。
ガンマ補正がかかったまま R,G,B を計算するのをよく見かけますが、仕様書を確認すると pre-corrected primary analogue signals なので、ガンマ補正を解くのが前提です。
あと JPEG の YCbCr の Y に用いられる式としても有名です。

ガンマ補正のかかったまま処理する規格もありそうなので、その場合と両方の結果を並べます。

元画像 BT.601(ガンマ補正のまま) BT.601(ガンマ補正解除して計算)
image.png image.png image.png

標準テレビジョン放送(デジタル放送を除く。)に関する送信の標準方式

(R',G',B') = 逆ガンマ補正(R,G,B)
V' = 0.30*R' + 0.59G' + 0.11*B'
V = ガンマ補正(V')

日本のアナログ放送の規格では、BT.601 より少し精度を落として定義されています。
こちらもガンマ補正を外します。

元画像 日本のアナログ放送
image.png image.png

BT.601 係数を更に粗くする

更に係数の精度を落とす処理もたまに見かけます。手抜きが目的なのでガンマ補正はそのままです。

V = 0.3*R + 0.6*G + 0.1*B
元画像 BT.601(ガンマ補正のまま) 粗い方
image.png image.png image.png

精度についてはこんな感じで一見して違いが分からなかったりするので良いのですが、
ガンマ補正を解かず処理すると、彩度の高い色ほど暗くなり、やはり良くない結果が出ます。

ITU-R Rec BT.709

(R',G',B') = 逆ガンマ補正(R,G,B)
V' = 0.2126*R' + 0.7152*G' + 0.0722*B'
V = ガンマ補正(V')

BT.709 は HDTVの 規格です。これは CIE XYZ と同じ処理です。また、sRGB も BT.709 相当です。

元画像 BT.709(ガンマ補正のまま) BT.709
image.png image.png image.png

標準テレビジョン放送等のうちデジタル放送に関する送信の標準方式

日本のデジタル放送の規格は BT.709 と同じ係数の計算式で精度も同じなので説明を省きます。

YCgCo の Y

V = R/4 + G/2 + B/4 

YCgCo 色空間で使われる輝度(Y)の計算式です。
単純な計算ながらも G を強めに反映する事で、RGB平均よりは良い結果が期待できます。
ビット演算が使えるのも強みです。

V = (R>>2) + (G>>1) + (B>>2) 
元画像 YCgCo
image.png image.png

YCgCo は動画圧縮の都合が優先で、人の視覚特性にあまり合っていないので、グレースケール化に使うことはあまりないですが、ビット演算で多少速度が稼げるのと計算誤差が把握しやすいメリットに対して一考の余地があります。

HSV の V (RGB の最大)

V = max(R,G,B)

R,G,B の一番明るい輝度を使います。列挙した中では一番外れの方式かもしれません。
ただ、例えばイラスト画像等で、色の使い方が独特な場合に、これが都合の良い時もあります。

  • 実例)
元画像 HSV
image.png image.png

HSL の L (RGB の最大と最小の中間)

V = (max(R,G,B) + min(R,G,B)) / 2

こちらも HSV 同様、特殊な状況でたまに使う事があります。

元画像 HSL
image.png image.png

Gチャンネルonly

V = G

どうせ R,G,B のうち G の輝度が圧倒的(6割)なので、G だけで良いのでは?という方法です。

元画像 G
image.png image.png
image.png image.png

The・雑という感じですが。画像によってはあまり違和感がなかったりします。
あと、人の目は暗がりだと緑に近い色がよく見えるので、夜間をイメージする画像としても良いかもしれません。
参考) https://qiita.com/yoya/items/96c36b069e74398796f3#comment-55b8dd0c13f313b8d708

中央値 (実験)

R,G,B の中央値を使う方法です。これはただの思いつきの実験です。実際に使う事はないでしょう。

V = [R,G,B].sort()[1] // Median
元画像 Median
image.png image.png
image.png image.png

駄目だこりゃ。。(;’-‘)

疑問

NTSC 係数 ?

BT.601 係数についてネットで検索すると NTSC coefficient(係数)の呼び方で、

Y = 0.298912*R + 0.586611*G + 0.114478*B

この式がよく出てくるのですが、日本語の記事ばかりな上にいずれも根拠が無いので紹介しませんでした。
そもそも合計が 1.000001 で輝度100% をオーバーシュートしてる。。

尚、ImageMagick ではまた違った係数を使っています。海外の記事だとこちらの係数をよく見かけます。

* MagickCore/enhance.c (GrayscaleImage)
intensity=0.298839*red+0.586811*green+0.114350*blue;

標準光源 C での NTSC RGB Primary xyY の Y の値のようです。

参考までに2エントリ抜粋。

Name Gamma Reference White Red Primary (x) (y) (Y) Green Primary (x) (y) (Y) Blue Primary (x) (y) (Y) Volume (ΔE3) Lab Gamut Efficiency % Coding Efficiency %
NTSC RGB 2.2 C 0.6700 0.3300 0.298839 0.2100 0.7100 0.586811 0.1400 0.0800 0.114350 1,300,252 54.2 99.9
sRGB ≈2.2 D65 0.6400 0.3300 0.212656 0.3000 0.6000 0.715158 0.1500 0.0600 0.072186 832,870 35.0 100.0

これはこれで出典元を確認しておきたいです。恐らく NTSC の仕様から算出できる値だとは思いますが。

最後に

はじめに思いつく RGB 単純平均は悪手で、CIE XYZ が良い方法。という結論で大体合ってますが、実際にその辺のモニタで試すと BT.601 のガンマ補正解除版も良かったりするので、自分は BT.601 を使う事もあります。
幾つか心当たりがあります。

  • 一般にRGB規格は標準光源 C 、D65 が基本だけど、一般の人のモニタは色温度を高めに設定するので、赤が暗くなりがちで BT.601 で赤の比重が高いので相殺分があるかも
  • XYZ といえば CIE1932 だけど被験者は17人でかつ人種の偏りもあるかもしれず、絶対視して良いのか疑問もある

赤を頑張れば、緑が犠牲になるといったトレードオフもあるので、結局、サンプルを作って実際に評価するのが良いと思います。

あと、こちらに画像ファイルを放り込むと、列挙した方式を全部まとめて変換します。参考にして下さい。

自分の眼でご確認頂けると色々分かる事もあるでしょう。linear チェックボックスを on にすると、ガンマ補正を考慮して変換します。

スクリーンショット 2018-04-17 6.00.48.png
スクリーンショット 2018-04-17 6.06.12.png

以上です。