Python でカラー処理する colour-science のメモ 

スペクトルから RGB, XYZ 値へ変換とか
CG 向けには ACES カラー変換とかもあります

## インストール

pip で入るので, 手間はありません. Windows もいけます.

`pip install colour-science`

colour と `u` が入りますので注意です!

`colour` だと別パッケージが入るので注意です!

## 使う

https://colour.readthedocs.io/en/develop/index.html

にまとまっています.

## スペクトルデータを生成

dict data から作る(key: wavelength)

wavelength, values の配列から作る

```
colour.SpectralDistribution(values, wavelengths, name="myspectrum")
```

### 補間する

interpolate

https://colour.readthedocs.io/en/develop/_modules/colour/colorimetry/spectrum.html#SpectralDistribution.interpolate

元の SpectralDistribution の波長領域から外れるところは考慮されない.
たとえば, `[380, 480]` のデータを, `[400, 500, interval=1]` で補間しても, `[400, 480]` だけ処理される. 

### SpectralDistribution 同士の演算

あまり演算機能はないようです.
一旦 `align` or `trim` などで, 波長領域を合わせておき, `.values` で生データ取得して numpy 処理がよさそうです.

https://colour.readthedocs.io/en/develop/generated/colour.SpectralDistribution.html#id0

https://colour.readthedocs.io/en/develop/generated/colour.SpectralDistribution.html#id12

```
sd0 = ...
sd1 = ...

target = colour.SpectralShape(380, 780, 1)

sd0_aligned = sd0.align(target)
sd1_aligned = sd1.align(target)

numpy.sum(sd0_aligned.values * sd1_aligned.values)

```


### 差分

https://colour.readthedocs.io/en/develop/index.html#colour-difference-colour-difference

### カラーチェッカーを plot する

ドキュメントと, pip で入るバージョン(0.3.15)で引数が違いますので注意です


pip 0.3.15 利用では, `text_parameters` を使います.

```
plot_single_colour_checker(
    colour_checker='ColorChecker 2005', text_parameters={'visible': False})
```

`master` を見るとドキュメントと同じ(`test_kwargs`)なので, 将来的には `test_kwargs` に統一されるでしょう.


plot_multi_colour_checker で, ２つ並べて表示できる.

https://colour.readthedocs.io/en/develop/generated/colour.plotting.plot_multi_colour_checkers.html#colour.plotting.plot_multi_colour_checkers

ただしここでのカラーチェッカーは RGB(XYZ) のみ

## スペクトルカラーチェッカー

本来(今後のメイン)は `colour.COLOURCHECKER_SDS` で取得できるが, 現在(2020/07/27)の pip で入る v0.3.15 では `COLOURCHECKERS_SDS` と, `S` をつける必要がある.

`BabelColor Average`,  `ColorChecher N Ohta` のふたつのみ.

BabelColor は 730 nm までしかないので注意.

Ohta: 1997 年に計測されたもので, ISO 標準(?)のもの.

X-Rite の ColorChecker の分光反射分布は公開されていないが, 著者らは X-Rite のカラーチェッカーを分光放射輝度計で計測しており, 
700~780 あたりの分布が Ohta と大きくことなるケースがあるのを確認している.
塗料によるものと思われる.

X-Rite のカラーチェッカーは, 有害物質関連で 2014 年前とあとで塗料の変更がある.

https://www.babelcolor.com/colorchecker.htm

カラーチェッカーを買うときは製造年を確認しましょう. 今からだと基本 2014 年以降でしょうから, ISO 標準(Ohta)とは結果が異るので正しいです.



## スペクトルから sRGB への変換

単一の SpectralDistribution から sRGB へ変換する.

```
sd = colour.SpectralDistribution(sample_sd_data)
cmfs = colour.STANDARD_OBSERVER_CMFS['CIE 1931 2 Degree Standard Observer']
illuminant = colour.ILLUMINANT_SDS['D65']

# Calculating the sample spectral distribution *CIE XYZ* tristimulus values.
XYZ = colour.sd_to_XYZ(sd, cmfs, illuminant)
print(XYZ)
```

### スペクトル画像から sRGB 画像への変換

とりあえずぱぱっとスペクトル画像を sRGB 画像にしたい場合

`[H, W, C]` で numpy ndarray を作成し,

```
cmfs = colour.STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']
illuminant = colour.ILLUMINANTS_SDS['D65']

xyz = colour.multi_sds_to_XYZ(ndimage, cmfs, illuminant, method='Integration',
                        shape=colour.SpectralShape(380, 780, 10))

rgb = colour.XYZ_to_sRGB(xyz / 100)
```

とする.  XYZ が [0, 100], RGB が [0, 1] の範囲での値になるので 100 で割る.
color matching function やスペクトルの範囲は必要に応じて調整ください.

`MultiSpectralDistribution` 形式の複数スペクトルデータから変換する関数は無さそう.


## 標準比視感度

`colour.PHOTOPIC_LEFS['CIE 1924 Photopic Standard Observer']` で取得できる.

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ありがとう標準比視感度 from colour-science <a href="https://t.co/yVlAduFRSt">https://t.co/yVlAduFRSt</a> <a href="https://t.co/SGdQCnrIgb">pic.twitter.com/SGdQCnrIgb</a></p>&mdash; Syoyo Fujita 🌸 レイトラ 🐯 2 周年 🎉 (@syoyo) <a href="https://twitter.com/syoyo/status/1288079948264034304?ref_src=twsrc%5Etfw">July 28, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

これを畳み込むのは `luminous_efficacy` 関数がある.

https://colour.readthedocs.io/en/develop/_modules/colour/colorimetry/photometry.html?highlight=Luminosity


## サンプル

Debayer(demosaic) する

https://github.com/colour-science/colour-demosaicing

処理自体は基本 numpy

### OpenColorIO 用の LUT table を作る

1D

```
table = colour.LUT1D.linear_table(4096)
table = colour.gamma_function(table, 1.8)
LUT = colour.LUT1D(table=table)

path = '/content/downloads/LUT1D.spi1'

colour.write_LUT(LUT, path)
```

3D 

```
table = colour.LUT3D.linear_table(33)
table = colour.gamma_function(table, 1.8)
LUT = colour.LUT3D(table=table)

path = '/content/downloads/LUT3D.cube'

colour.write_LUT(LUT, path)
```

## CSV へエクスポート

https://colour.readthedocs.io/en/develop/generated/colour.write_sds_to_csv_file.html

ドキュメントではわかりづらいのですが, `SpectralDistribution` を値に持つ dict を作ります.
CSV では, dict の key が波長データの名前になります.

```
sds_bora = colour.SpectralDistribution(...)
sds_muda = colour.SpectralDistribution(...)

d = {}
d['bora'] = sds
d['muda'] = sds
colour.write_sds_to_csv_file(d, "bora.csv")
```

```
wavelength,bora,muda
380.0,0.113,0.113
385.0,0.131,0.131
390.0,0.15,0.15
395.0,0.169,0.169
400.0,0.183,0.183
405.0,0.193,0.193
...
```

### CVS から読む

https://colour.readthedocs.io/en/develop/generated/colour.read_sds_from_csv_file.html

read_sds_from_csv_file を使います.

波長データを key とした dict として復元されます.

```
colour.read_sds_from_csv_file("bora.csv")

OrderedDict([('bora',
              SpectralDistribution([[  3.80000000e+02,   1.13000000e-01],
                                    [  3.85000000e+02,   1.31000000e-01],
                                    [  3.90000000e+02,   1.50000000e-01],
...
```

### JSON は?

標準ではなさそうです. 自前で JSON 変換でしょうか.

#### SpectralDistribution を dict に変換する

dict から SpectralDistribution は作れますが, 逆は標準では用意されていません.

```
a = dict((k, v) for k, v in zip(patch_sd.wavelengths, patch_sd.values))
```

と, wavelengths と values を zip して, wavelength を key として dict を作ることができます.


## Tips

### スペクトルと, カスタムの color matching function を純粋に積分したい(スペクトルとスペクトルの積分).

`sd_to_XYZ` で sd, cmfs を指定し `method='Integration'` を選ぶ.

https://colour.readthedocs.io/en/develop/generated/colour.sd_to_XYZ.html

デフォルトでは定数 illuminant が使われる.
method デフォルトでは `ASTM E308` で, 波長のクランプなどが行われる模様.
`Integration` にすると, 純粋に積分してくれる.

内部的には numpy で処理しているので, 自前で numpy 処理してもよいかもしれません.

## その他メモ

カラーチェッカーでキャリブレーション
https://github.com/colour-science/colour/issues/598