見出し画像

[Swift]CIImageの色を想定通りに操作できない時の解決

色の演算が想定通りにいかないとか、(0.5, 0.5, 0.5)がニュートラルグレーにならないとか、そういう時の話です。

問題

例えばSemanticSegmentationで取得した髮と肌のmatteを↓のような感じでCIAdditionCompositingフィルタに突っ込んで合成するとします。

直感的には髮と肌の領域が合成されて生え際が綺麗に白になった画像が出力される気がします。
しかし実際に試してみると

画像1

のようになって、合成することでむしろ眉・目・口まわりのグレーの領域が減少してしまっています。

CIAdditionCompositingの動作がおかしいのかと思ってMetalで2枚の画像を合成する簡単なフィルタを自作して試しても同じような結果になります。

もっと簡単な加算処理にしても綺麗に色を足し合わせれないので、試しに(0.5, 0.5, 0.5)一色で出力するフィルタを書いてみると、その時点で出力される色が想定と違っていることがわかりました。

画像2

127, 127, 127になるはずが188, 188, 188になっている…。

解決

気になって色々調べたりググったりした結果、気になる記事を見つけました。

RGB 値による色の変化が非線形であることです。
(カラーマネージメントされているため、sRGB ではなくリニア RGB だから…ということが判明)

そのため、256 階調の RGB 値を 0-1 に計算してしまうと、思った色が出ません。

例えば、ニュートラルグレー 128,128,128 は、単純に計算すると 0.5,0.5,0.5 ということになります。
しかし、その値を設定してみると、明るめのグレーとなり、Hex は BCBCBC という値になります。

Blenderについての解説記事ですが、BC=188なので現象としては同じ。
そういえばCore Imageでは度々ColorSpaceという単語が出てきたような…。

CIContextに与えるオプションのworkingColorSpaceで以下のような記述があります。

By default, Core Image assumes that processing nodes are 128 bits-per-pixel, linear light, premultiplied RGBA floating-point values that use the GenericRGB color space. You can specify a different working color space by providing a Quartz 2D CGColorSpace object (CGColorSpace). Note that the working color space must be RGB based. If you have YUV data as input (or other data that is not RGB based), you can use ColorSync functions to convert to the working color space. (See Quartz 2D Programming Guide for information on creating and using CGColorSpace objects.)

To request that Core Image perform no color management, specify the NSNull object as the value for this key. Use this option for images that don’t contain color data (such as elevation maps, normal vector maps, and sampled function tables).

(DeepLに二番目のパラグラフを投げた結果)
コアイメージにカラーマネジメントを行わないように要求するには、このキーの値としてNSNullオブジェクトを指定します。このオプションは、カラーデータを含まない画像(エレベーションマップ、法線ベクトルマップ、サンプリングされた関数テーブルなど)に使用します。

これですね。
そういえば他の処理を調べた時にworkingColorSpaceにNullを指定するように指示されてそのまま使ってる箇所があるのを思い出しました。

let context = CIContext(options: [.workingColorSpace: kCFNull])

CIContextの初期化を修正してもう一度matteを合成してみると

画像3

綺麗に合成されました。

雑感

今まで「ColorSpace(色空間)って何に使うんだろう…?」と思っていましたが、結構普通に使うようですね。

Core Imageでは随所にColorSpaceを指定できるオプションがあるので、CIContext以外で指定する以外にもピンポイントでColorSpaceを変更できる気がしますが、試した感じだとNullを指定できない箇所ばかりでした。
CGColorSpaceでlinearと付いてるものについては“with a linear transfer function”とあったので何か変換用の関数がありそうな気もしますがちょっとよくわからなかったです。

Metal Shading Languageにはsrgb_to_linear()関数が用意されているので、フィルタまわりでsRGBとlinearを使い分けたい場合はMetalで書いてしまうのも手かもしれません。 ■

この記事が気に入ったらサポートをしてみませんか?