[カラーサイエンス][Python] 正しく変換したアニメカラーデータ
ねこまたやさんのアニメカラー測定データがどうにも怪しい (実測データと目視データが異なる) ので調べてみた。
同サイトのD65 XYZ値は100倍になっているようで、まずここでハマる。測定機器の凸版 CS CM1000は調べても詳細が出てこないが、下記を見る限りD50光源らしい?
このツールでの測色はD50にしないと正常なプロファイルが取れません。
光源の切替えはCS-CM1000付属のソフト上で行います。
ピンと来たので、測定データのD65 XYZ値をXYZ Scaling変換でD50 XYZ値に一旦戻してから、より高精度なBradford変換でD65 XYZ値に変換してみたら、どうにも其れっぽい色が出てきた。値のズレはXYZ Scaling変換にも一因があるようだ。
import numpy as np def fix_color(xyz): xyz_mat = np.matrix(xyz).T # http://technorgb.blogspot.jp/2015/08/blog-post_22.html # http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html xyz_to_lms_mat = np.matrix([ [ 0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [ 0.0389, -0.0685, 1.0296] ]) d65_mat = np.matrix([0.95047, 1.0, 1.08883]).T d50_mat = np.matrix([0.96422, 1.0, 0.82521]).T d65_xyz_to_d50_xyz_mat = np.diagflat(d50_mat.A / d65_mat.A) # XYZ Scaling method d50_xyz_to_d65_xyz_mat = xyz_to_lms_mat.I * np.diagflat((xyz_to_lms_mat * d65_mat) / (xyz_to_lms_mat * d50_mat)) * xyz_to_lms_mat # Bradford method xyz_mat = d50_xyz_to_d65_xyz_mat * (d65_xyz_to_d50_xyz_mat * xyz_mat) return xyz_mat.A1
sRGBへの変換 (Numpy使いきれてないのがバレる)
import numpy as np # https://www.w3.org/Graphics/Color/srgb def linearsrgb_to_srgb(lin): # return [0 if c < 0 else 12.92 * c if c <= 0.0031308 else 1.055 * np.power(c, (1/2.4)) - 0.055 for c in lin] return [12.92 * c if abs(c) <= 0.0031308 else math.copysign(1.055 * np.power(abs(c), (1/2.4)) - 0.055, c) for c in lin] def d65xyz_to_srgb(xyz): d65xyz_to_linearsrgb_mat = np.matrix([ [ 3.2406255, -1.537208 , -0.4986286], [-0.9689307, 1.8757561, 0.0415175], [ 0.0557101, -0.2040211, 1.0569959] ]) xyz_mat = np.matrix(xyz).T lin_srgb = d65xyz_to_linearsrgb_mat * xyz_mat srgb = linearsrgb_to_srgb(lin_srgb) return [i*255 for i in srgb]
書き出し
import numpy as np import csv with open("test.html", "w") as htmlfile: htmlfile.write("<html><head></head><body>") # http://www.nekomataya.info/teck_info/taiyo_color/TaiyoChart.txt with open("TaiyoChart.txt", "rb") as cvsfile: cl = 0 chartreader = csv.reader(cvsfile, delimiter='\t') for row in chartreader: if cl < 2: cl = cl + 1 continue if len(row) < 9: continue cname = row[2] xyz = map(float,[row[7], row[8], row[9]]) xyz_str = ",".join(map(str,xyz)) srgb = d65xyz_to_srgb(fix_color(np.array(xyz)/100)) srgb_str = ",".join([str(int(round(c))) for c in srgb]) o_srgb = [row[12], row[13], row[14]] o_srgb_str = ",".join(o_srgb) m_srgb = [row[4], row[5], row[6]] m_srgb_str = ",".join(m_srgb) print("%s = XYZ(%s), sRGB(%s), o_sRGB(%s)"%(cname, xyz_str, srgb_str, o_srgb_str)) htmlfile.write("<div style='display: inline-block; height 5em; width: 10em;'>\ <p style='margin: 0'>%s</p>\ <p style='background-color: rgb(%s); margin: 0'>XYZ=%s<br>sRGB=%s</p>\ <p style='background-color: rgb(%s); margin: 0'>o_sRGB=%s</p>\ <p style='background-color: rgb(%s); margin: 0'>m_sRPG=%s</p></div>"%\ (cname, srgb_str, xyz_str, srgb_str, o_srgb_str, o_srgb_str, m_srgb_str, m_srgb_str)) htmlfile.write("</body></html>")
若干表現変更。
暗い色はsRGB変換よりもgamma 2.2変換の方が、アニメカラー測定データにある目視カラーデータに近くなるように見える (X7より後はsRGB変換の方が近い)。うーむ、目視カラーデータの方も怪しい気がするような。