Skip to content

Instantly share code, notes, and snippets.

@wakita
Last active September 16, 2019 22:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wakita/04537a15215608f2b341f87f96b92457 to your computer and use it in GitHub Desktop.
Save wakita/04537a15215608f2b341f87f96b92457 to your computer and use it in GitHub Desktop.
知覚的均等な色見に関するグラデーション
import numpy as np
from colormath.color_objects import LabColor, XYZColor, sRGBColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie2000
# 各種関数を Numpy array に対応
labcolor = np.vectorize(LabColor)
lab_l = np.vectorize(lambda lab: lab.lab_l)
lab_a = np.vectorize(lambda lab: lab.lab_a)
lab_b = np.vectorize(lambda lab: lab.lab_b)
srgb = np.vectorize(lambda lab: convert_color(lab, sRGBColor, rgb_gamma=1.8))
Δ = np.vectorize(lambda lab1, lab2: delta_e_cie2000(lab1, lab2))
def Radial(n_colors, L, R):
GRAY = LabColor(L, 0, 0)
# 問題の設定
n_colors = 6
L1, a1, b1 = 50, 30, 0
L2, a2, b2 = 50, -30, 0
lab1, lab2 = LabColor(L1, a1, b1), LabColor(L2, a2, b2)
# 初期解は GRAY を中心とした CIE a*-b* 平面上の半径 R の円に沿って等距離に取得
initial_T = np.ones((n_colors, 1)) / n_colors
def objective(T):
# CIE a*列とb*列が与えられたときの目的関数
t = T.cumsum() / T.sum()
labs = labcolor((1 - t) * L1 + t * L2,
(1 - t) * a1 + t * a2,
(1 - t) * b1 + t * b2)
return (np.abs(t.sum() - 1) * # 制約1: 媒介変数の和が 1
np.var(Δ(labs[1:], labs[:-1]))) # 制約: 隣接色との距離の分散が小さい
from crfmnes import CRFMNES
# 最適化の設定: よくわからないまま設定した。
optimizer = CRFMNES(np.size(initial_T), objective, initial_T, 1, 8)
T, f_best = optimizer.optimize(100)
# 得られた CIEL*a*b* 値について目的関数を評価
print(T)
t = T.cumsum() / T.sum()
labs = labcolor((1 - t) * L1 + t * L2,
(1 - t) * a1 + t * a2,
(1 - t) * b1 + t * b2)
Ldist = Δ(labs[1:], labs[:-1])
print('色差', Ldist.min(), Ldist.max(), Ldist.var())
print(Ldist)
# 描画
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw=dict(xlim=[0, n_colors]))
for i, srgb in zip(range(len(labs)), srgb(labs)):
print(srgb)
ax.add_patch(plt.Rectangle((i, 0), 1, 1, fc=srgb.get_rgb_hex()))
ax.set_axis_off()
plt.show()
import numpy as np
from colormath.color_objects import LabColor, XYZColor, sRGBColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie2000
# 各種関数を Numpy array に対応
labcolor = np.vectorize(LabColor)
lab_l = np.vectorize(lambda lab: lab.lab_l)
lab_a = np.vectorize(lambda lab: lab.lab_a)
lab_b = np.vectorize(lambda lab: lab.lab_b)
srgb = np.vectorize(lambda lab: convert_color(lab, sRGBColor, rgb_gamma=1.8))
Δ = np.vectorize(lambda lab1, lab2: delta_e_cie2000(lab1, lab2))
# 問題の設定
n_colors = 6
lab1, lab2 = LabColor(70, 25, 0), LabColor(70, -25, 0)
initial_params = np.array([[50.0] * n_colors + [0.0, 0.0] * n_colors]).T
def boundary_condition(labs):
srgbs = srgb(labs)
for rgb in srgbs:
if rgb.rgb_r > 1 or rgb.rgb_g > 1 or rgb.rgb_b > 1: return False
if rgb.rgb_r < 0 or rgb.rgb_g < 0 or rgb.rgb_b < 0: return False
return True
def objective(params):
params = params[:,0]
labs = labcolor(params[:n_colors], params[n_colors:n_colors*2], params[n_colors*2:])
if not boundary_condition(labs): return np.inf
diffs = Δ(labs[1:], labs[:-1])
return (Δ(labs[0], lab1) * n_colors + # 制約1: 最初の色は lab1 と一致
Δ(labs[-1], lab2) * n_colors + # 制約2: 最後の色は lab2 と一致
diffs.sum() + # 制約3: 隣接色との距離の合計が小さい
np.var(diffs)) # 制約4: 隣接色との距離の分散が小さい
from crfmnes import CRFMNES
# 最適化の設定: よくわからないまま設定した。
optimizer = CRFMNES(np.size(initial_params), objective, initial_params, 70, 16)
params, f_best = optimizer.optimize(300)
# 得られた CIEL*a*b* 値について目的関数を評価
labs = labcolor(params[:n_colors], params[n_colors:n_colors*2], params[n_colors*2:])
Ldist = Δ(labs[1:], labs[:-1])
print('色差', Ldist.min(), Ldist.max(), Ldist.var())
print(Ldist)
# 描画
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw=dict(xlim=[0, n_colors]))
for i, srgb in zip(range(len(labs)), srgb(labs)):
print(srgb)
ax.add_patch(plt.Rectangle((i, 0), 1, 1, fc=srgb.get_rgb_hex()))
ax.set_axis_off()
plt.show()
import numpy as np
from colormath.color_objects import LabColor, XYZColor, sRGBColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie2000
# 各種関数を Numpy array に対応
labcolor = np.vectorize(LabColor)
srgb = np.vectorize(lambda lab: convert_color(lab, sRGBColor, rgb_gamma=1.8))
Δ = np.vectorize(lambda lab1, lab2: delta_e_cie2000(lab1, lab2))
# 問題の設定
n_colors = 6
L, R = 70, 23 # CIE L* 値と無彩色 (GRAY) からのΔ値
GRAY = LabColor(L, 0, 0)
# 初期解は GRAY を中心とした CIE a*-b* 平面上の半径 R の円に沿って等距離に取得
initial_abs = R * np.array([(np.cos(t), np.sin(t))
for t in np.arange(n_colors) * np.pi * 2 / n_colors])
initial_abs = initial_abs.T.reshape(np.size(initial_abs), 1)
def boundary_condition(labs):
srgbs = srgb(labs)
for rgb in srgbs[:,0]:
if rgb.rgb_r > 1 or rgb.rgb_g > 1 or rgb.rgb_b > 1: return False
return True
assert(boundary_condition(labcolor(L, initial_abs[:n_colors], initial_abs[n_colors:])))
def objective(abs):
# CIE a*列とb*列が与えられたときの目的関数
labs = labcolor(L, abs[:n_colors], abs[n_colors:])
if not boundary_condition(labs): return np.inf
return (np.abs(Δ(labs, GRAY) - R).mean() + # 制約1: 無彩色との距離が R
np.var(Δ(labs, np.roll(labs, 1)))) # 制約2: 隣接する色との距離の分散が小さい
from crfmnes import CRFMNES
# 最適化の設定: よくわからないまま設定した。
optimizer = CRFMNES(np.size(initial_abs), objective, initial_abs, 1, 8)
abs, f_best = optimizer.optimize(200)
# 得られた CIEL*a*b* 値について目的関数を評価
labs = labcolor(L, abs[:n_colors], abs[n_colors:])
Rdist = np.abs(Δ(labs, GRAY) - R)
Ddist = Δ(labs, GRAY)
print('半径', Rdist.min(), Rdist.max(), Rdist.var())
print('色差', Ddist.min(), Ddist.max(), Ddist.var())
# 描画
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw=dict(xlim=[0, n_colors]))
for i, srgb in zip(range(len(labs)), srgb(labs)):
print(srgb)
ax.add_patch(plt.Rectangle((i, 0), 1, 1, fc=srgb.get_rgb_hex()))
ax.set_axis_off()
plt.show()
@wakita
Copy link
Author

wakita commented Sep 14, 2019

  • 境界条件を設定して、色を sRGB で表現可能な範囲に納めました。(boundary_condition)

  • 指定した無彩色との色差、指定した隣接色との色差を出力するようにしました。

@wakita
Copy link
Author

wakita commented Sep 14, 2019

今回のように色相環に沿ったグラデーションを扱うのなら LabColor ではなく、それを極座標で表現した LCHabColor を利用する方が素直だったらしい。変数を半減できると思う。要修正。

@wakita
Copy link
Author

wakita commented Sep 16, 2019

隣接する色の色差を Δ_i として、Σ Δ_i + Var[Δ_i] を最小化するのが直接的な解のような気がする。

gradation-l.py は、lab1, lab2 のグラデーションが CIE Lab* 空間上の線分上に乗ることを仮定し (lab1, lab2) を一次補完した媒介変数 t を最適化している。ただ、最適解が本当に線分上に乗るのかという点について自信がない。

@wakita
Copy link
Author

wakita commented Sep 16, 2019

gradation-l2.py を追加。直前のコメントのように Δ_i に関する最適化を実施している。初期値は同一色とした。目的関数の重みの与え方に工夫が必要だった。決め手は * n_colors。これをいれるまで、両端の色が指定した二色に一致せず、すべての色が一致する局所解に陥ってしまった。もしかすると * (n_colors - 1) の方が収束が早いかもしれない。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment