背景
筆者はこれまで画像データの読み書きは OpenCV または OpenImageIO の Python用Bindings を叩く自作のラッパーを作って行っていた。が、Color Science for Python に良い感じの関数があったので、これを使ってみることにした。
おことわり
本記事は colour-science 0.3.14 および OpenImageIO 2.2.0 の環境で確認しています。特に OpenImageIO が無い環境では殆ど何も動かないと思いますのでご注意ください。
目的
- Color Science for Python の
write_image
,read_image
の動作確認を行う - 具体的には筆者が利用する表1.のファイル形式について 1bitの情報も失うことなく Write/Read できることを確認する
- 合わせて Davinci Resolve や NUKE などのツール類で正しく読み込めることも確認する
ファイル形式 | bit深度 | 備考 |
---|---|---|
TIFF | 8bit, 16bit | 整数型 |
PNG | 8bit, 16bit | 整数型 |
DPX | 10bit, 12bit | 整数型 |
OpenEXR | 16bit, 32bit | 浮動小数点型 |
結論
表1の全てのファイル形式について write_image
, read_image
を使った読み書きを行い、bit欠損が生じないことを確認した。確認コードは以下。
また、Davinci Resolve と NUKE にて各種形式のファイルが読み込めることも確認した。例として Davinci Resolve で 10bit DPX ファイルを開いた際のスクリーンショットを図1 に示す。
サンプルコード
例として、8bit PNG と 10bit DPX ファイルの Write/Read のコードを示す。
8bit PNG
import numpy as np from colour import write_image, read_image if __name__ == '__main__': os.chdir(os.path.dirname(os.path.abspath(__file__))) # main_func() # 256x256 の画像データを作成。この時点では全画素の値は1。 img = np.ones((256, 256, 3), dtype=np.uint8) # 上記の箱を上書きするグラデーションデータを 1Line分作成 gradation_achromatic = np.uint8(np.arange(256)) # カラーデータ化。今回は黄色にした。 gradation_yellow = np.dstack( (gradation_achromatic, gradation_achromatic, np.zeros_like(gradation_achromatic))) # 最初に作った img を上書き。numpy のブロードキャストを利用。 # (256, 256, 3) のデータに (1, 256, 3) を乗算すると # numpy が空気を読んで (1, 256, 3) --> (256, 256, 3) に拡張する。 img = img * gradation_yellow # write_image を使ってファイルに吐き出す。 # なお、write_image の第1引数は float 型にする必要がある。 # 16bit PNG にする場合は bit_depth='uint16' にするだけで良い write_image(img/255, "8bit.png", bit_depth='uint8') # read_image を使ってファイル読み込み after_img = read_image("8bit.png", bit_depth='uint8') print(np.all(img == after_img)) # データが同一なら True が返る
上記コードで生成される画像は以下。
10bit DPX
DPX ファイルの bit深度は ImageAttribute_Specification
を利用して指定する必要があることに注意すること。ImageAttribute_Specification
で指定できるオプションについては OpenImageIO のドキュメントを参照すること。
import numpy as np from colour import write_image, read_image from colour.io.image import ImageAttribute_Specification if __name__ == '__main__': img = np.ones((768, 1024, 3), dtype=np.uint16) gradation_achromatic = np.uint16(np.arange(1024)) gradation_cyan = np.dstack( (np.zeros_like(gradation_achromatic), gradation_achromatic, gradation_achromatic)) img = img * gradation_cyan # write_image を使ってファイルに吐き出す。 # 10bit DPX の場合 bit_depth は 'uint16' を指定する。 # DPX の bit深度は別途 attributes オプションで指定する。 bit_option = ImageAttribute_Specification("oiio:BitsPerSample", 10) write_image( img/1023, "10bit.dpx", bit_depth='uint16', attributes=[bit_option]) # read_image を使ってファイル読み込み # 10bit での read はできないので、float型で読んでから uint32 に変換する after_img = read_image("10bit.dpx", bit_depth='float32') after_img = np.uint32(np.round(after_img * 1023)) print(np.all(img == after_img)) # データが同一なら True が返る
感想
write_image
, read_image
がちゃんと動いているのを確認できたので、これからメインで使っていこうと思う。
参考資料
- openimageio.pdf, https://github.com/OpenImageIO/oiio/blob/master/src/doc/openimageio.pdf
- 4.3 API Reference - Colour Science for Python, "Input and Output", https://colour.readthedocs.io/en/v0.3.14/colour.io.html