謎's キッチン

謎のひとりごと。Amazon欲しい物リストはこちら: https://www.amazon.co.jp/hz/wishlist/ls/CCPOV7C6JTD2

[カラーサイエンス][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変換の方が近い)。うーむ、目視カラーデータの方も怪しい気がするような。