LoginSignup
3
4

More than 5 years have passed since last update.

Python + OpenCVでカラーマトリクスの適用

Last updated at Posted at 2017-10-03

cv2.cvtColorで [rec709(sRGB)] ⇔ [XYZ] は用意されているが、
任意のマトリクスを指定するような機能は用意されていないと思われる
そのためマトリクス適用は自分で行う必要がある。
色変換のシンプルな例をテストした。

マトリクスの転置が必要なこととチャネル順がBGRであることに注意

カラーのarrayを直接入力して試した時にマトリクスは転置しないとだめだと気が付いたが、画像に対して同じような処理をしたら結果が変わってしまった。
openCVでは画像読み込みするとRGBではなくてBGRで読み込まれる。
単純な処理しかしない場合はこのまま進めても問題ないがr,g,bごとに違う処理をするときは順番に注意しなければいけない。

sRGBの原色(赤青緑白)をXYZに変換する

sRGBの原色をXYZやxyの色空間に変換した場合は下の値になれば正解

sRGB XYZ xy
1, 0, 0 0.4125, 0.2127, 0.0193 0.64, 0.33
0, 1, 0 0.3576, 0.7152, 0.1192 0.30, 0.60
0, 0, 1 0.1804, 0.0722, 0.9503 0.15, 0.06
1, 1, 1 0.9505, 1.0000, 1.0888 0.3127, 0.3290

sRGBからXYZに変換するマトリクスを適用(数値入力の場合)

  • マトリクスの転置を行うこと(最初から転置したマトリクスの書き方をしてれば問題ないが)
  • .Tで転置
import numpy as np

#4色(赤青緑白)を設定してこの色を変換する
linearColor = np.array([
[1,0,0],#red
[0,1,0],#grenn
[0,0,1],#blue
[1,1,1],#white
])

#sRGBからXYZに変換するマトリクス (https://en.wikipedia.org/wiki/SRGB)
sRGB_to_XYZ_Matrix = np.matrix([[0.4124,0.3576,0.1805],[0.2126,0.7152,0.0722],[0.0193,0.1192,0.9505]])

#マトリクスを適用する。この時マトリクスを転置すること
XYZ = linearColor * sRGB_to_XYZ_Matrix.T
"""
XYZ
matrix([[ 0.4124,  0.2126,  0.0193],
        [ 0.3576,  0.7152,  0.1192],
        [ 0.1805,  0.0722,  0.9505],
        [ 0.9505,  1.    ,  1.089 ]])
"""

ちなみにxyYの値は

XYZの合計で割ったものがxyz。xyYのYはXYZのYと同じ

#色域を表す場合に一般的に使われるxyYに変換する。https://en.wikipedia.org/wiki/SRGB の値と同じxyYになっていれば問題ない
xyz = XYZ/np.sum(XYZ, axis=1)           #X,Y,Zの合計で割ったものが小文字xyz
xyY = np.c_[xyz[:,0],xyz[:,1],XYZ[:,1]] #xyY
"""
xyY
matrix([[ 0.6400745 ,  0.32997051,  0.2126    ],
        [ 0.3       ,  0.6       ,  0.7152    ],
        [ 0.15001662,  0.06000665,  0.0722    ],
        [ 0.31271591,  0.32900148,  1.        ]])
"""

このXYZを画像として表示する場合

  • マトリクスを適用するとタイプがarrayからmatrixになるので注意。必要に応じて.Aでarrayに戻せる
  • openCVで表示や画像の保存をする場合はRGBではなくBGRの順になっている必要がある
#XYZ画像表示
import cv2
XYZ = XYZ.A.reshape(1,4,3)              #高さ1、幅4ピクセルのカラー画像の形式に整える
XYZ = XYZ.astype(np.float32)            #float64だと次のBGR2RGB変換が出来ないのでfloatt32に変更
XYZ = cv2.cvtColor(XYZ,cv2.COLOR_BGR2RGB) #BGRに変換
img = cv2.resize(XYZ, (4*50,1*50),interpolation=cv2.INTER_NEAREST) #50倍の大きさの画像にする
cv2.imshow("XYZ",(img))                 #表示
cv2.waitKey(0)
cv2.destroyAllWindows()

XYZに変換したものを画像として表示
XYZ.png

openCVで画像として読み込むとうまくいかない

このような画像を用意して読み込む。(縦1、横4ピクセルでarrayに入力したものと同じ値の画像を用意)
srgbPrimary.png

リニアのfloat32のexr画像を使った。
8bit画像だと255で割ったり場合によってはデガンマしないといけないから。

XYZの値が画像から読み込んだ場合では変わってしまった。
error_XYZ.png

原因は下のように画像読み込みをするとBGRの順になってしまうから。

#4色(赤青緑白)の画像を読み込む
linearColor = cv2.imread('srgbPrimary.exr',3)
"""
linearColor
array([[[ 0.,  0.,  1.],
        [ 0.,  1.,  0.],
        [ 1.,  0.,  0.],
        [ 1.,  1.,  1.]]], dtype=float32)
"""

この状態でマトリクスを適用するともちろん正しくないものになる
BGRのまま進めても問題ない場合もあるが、チャネル単位で処理を変えたりマトリクスを掛ける場合は順番が大事。
とりあえずRGBの順に変換にしてしまうのが手っ取り早い。
cv2.COLOR_BGR2RGBで簡単に変換できる。
また、float64だとcv2.COLOR_BGR2RGBに対応していないみたいなので、float32変換した。

sRGBからXYZに変換するマトリクスを適用(画像読み込みの場合)

import numpy as np
import cv2

#4色(赤青緑白)の画像を読み込む
linearColor = cv2.imread('srgbPrimary.exr',3)
h,w, _ = linearColor.shape #もとの画像の解像度取得
linearColor = cv2.cvtColor(linearColor,cv2.COLOR_BGR2RGB)  #BGRをRGBに変換
linearColor = linearColor.reshape(h*w,3) #マトリクスを適用しやすいように縦*横のarrayを一行に変換する

#sRGBからXYZに変換するマトリクス (https://en.wikipedia.org/wiki/SRGB)
sRGB_to_XYZ_Matrix = np.matrix([[0.4124,0.3576,0.1805],[0.2126,0.7152,0.0722],[0.0193,0.1192,0.9505]])

#マトリクスを適用する。この時マトリクスを転置すること
XYZ = linearColor * sRGB_to_XYZ_Matrix.T

#XYZ画像表示
XYZ = XYZ.A                #タイプをmatrixからarrayに変換
XYZ = XYZ.reshape(h,w,3)
XYZ = XYZ.astype(np.float32)
XYZ = cv2.cvtColor(XYZ,cv2.COLOR_RGB2BGR)
img = cv2.resize(XYZ, (w*50,h*50),interpolation=cv2.INTER_NEAREST)
cv2.imshow("XYZ_Color",(img))
cv2.waitKey(0)
cv2.destroyAllWindows()

XYZに変換したものを画像として表示。問題ない。
XYZ.png

ここでも同じようなことが書いてある
https://stackoverflow.com/questions/35167381/fastest-way-to-apply-color-matrix-to-rgb-image-using-opencv-3-0

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4