diff options
-rw-r--r-- | jakob_hanika.py | 56 | ||||
-rw-r--r-- | test_colorchecker.py | 37 |
2 files changed, 48 insertions, 45 deletions
diff --git a/jakob_hanika.py b/jakob_hanika.py index 78803a1..3b35670 100644 --- a/jakob_hanika.py +++ b/jakob_hanika.py @@ -1,37 +1,9 @@ import numpy as np, scipy.optimize as optimize from colour import * from colour.difference import * -from colour.plotting import * -from matplotlib import pyplot as plt - - - -# Makes a comparison plot with SDs and swatches -def plot_comparison(target_sd, matched_sd, label, error): - target_XYZ = sd_to_XYZ(target_sd) - target_RGB = np.clip(XYZ_to_sRGB(target_XYZ / 100), 0, 1) - target_swatch = ColourSwatch(label, target_RGB) - matched_XYZ = sd_to_XYZ(matched_sd) - matched_RGB = np.clip(XYZ_to_sRGB(matched_XYZ / 100), 0, 1) - matched_swatch = ColourSwatch("Model", matched_RGB) - - axes = plt.subplot(2, 1, 1) - plt.title(label) - plot_multi_sds([target_sd, matched_sd], axes=axes, standalone=False) - - axes = plt.subplot(2, 1, 2) - plt.title("ΔE = %g" % error) - plot_multi_colour_swatches([target_swatch, matched_swatch], axes=axes) - - - -# The same illuminant is used throughout -il = ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] # The same wavelength grid is used throughout -wvl = np.arange(360, 830, 10) - - +wvl = np.arange(360, 830, 5) # This is the model of spectral reflectivity described in the article. def model(wvl, cc): @@ -45,10 +17,10 @@ def model(wvl, cc): # The goal is to minimize the color difference between a given distrbution # and the one computed from the model above. -def error_function(cc, target): +def error_function(cc, target, illuminant): ev = model(wvl, cc) sd = SpectralDistribution(ev, wvl) - Lab = XYZ_to_Lab(sd_to_XYZ(sd), il) + Lab = XYZ_to_Lab(sd_to_XYZ(sd), illuminant) return delta_E_CIE1976(target, Lab) @@ -59,26 +31,20 @@ def error_function(cc, target): def cb_basinhopping(x, f, accept): return f < 0.1 -# This demo goes through SDs in a color checker -for name, sd in COLOURCHECKERS_SDS['ColorChecker N Ohta'].items(): - XYZ = sd_to_XYZ(sd) - target = XYZ_to_Lab(XYZ, il) - - print("The target is '%s' with L=%g, a=%g, b=%g" % (name, *target)) - +# Finds coefficients for Jakob and Hanika's function +def jakob_hanika(target, illuminant): # First, a conventional solver is called. For 'yellow green' this # actually fails: gets stuck at a local minimum that's far away # from the global one. # FIXME: better parameters, a better x0, a better method? - # FIXME: stop iterating as soon as delta E is negligible (instead of - # going). + # FIXME: stop iterating as soon as delta E is negligible opt = optimize.minimize( - error_function, (0, 0, 0), target, method="L-BFGS-B", - options={"disp": True, "ftol": 1e-5} + error_function, (0, 0, 0), (target, illuminant), + method="L-BFGS-B", options={"disp": True, "ftol": 1e-5} ) print(opt) - error = error_function(opt.x, target) + error = error_function(opt.x, target, illuminant) print("Delta E is %g" % error) # Basin hopping is far more likely to find the actual minimum we're @@ -86,7 +52,7 @@ for name, sd in COLOURCHECKERS_SDS['ColorChecker N Ohta'].items(): if error > 0.1: print("Error too large, trying global optimization") opt = optimize.basinhopping( - lambda cc: error_function(cc, target), + lambda cc: error_function(cc, target, illuminant), (0, 0, 0), disp=True, callback=cb_basinhopping ) print(opt) @@ -94,4 +60,4 @@ for name, sd in COLOURCHECKERS_SDS['ColorChecker N Ohta'].items(): print("Global delta E is %g" % error) matched_sd = SpectralDistribution(model(wvl, opt.x), wvl, name="Model") - plot_comparison(sd, matched_sd, name, error) + return opt.x, matched_sd, error diff --git a/test_colorchecker.py b/test_colorchecker.py new file mode 100644 index 0000000..907322b --- /dev/null +++ b/test_colorchecker.py @@ -0,0 +1,37 @@ +import numpy as np +from colour import * +from colour.plotting import * +from matplotlib import pyplot as plt +from jakob_hanika import jakob_hanika + +D65 = ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] + +# Makes a comparison plot with SDs and swatches +def plot_comparison(target_sd, matched_sd, label, error): + target_XYZ = sd_to_XYZ(target_sd) + target_RGB = np.clip(XYZ_to_sRGB(target_XYZ / 100), 0, 1) + target_swatch = ColourSwatch(label, target_RGB) + matched_XYZ = sd_to_XYZ(matched_sd) + matched_RGB = np.clip(XYZ_to_sRGB(matched_XYZ / 100), 0, 1) + matched_swatch = ColourSwatch("Model", matched_RGB) + + axes = plt.subplot(2, 1, 1) + plt.title(label) + plot_multi_sds([target_sd, matched_sd], axes=axes, standalone=False) + + axes = plt.subplot(2, 1, 2) + plt.title("ΔE = %g" % error) + plot_multi_colour_swatches([target_swatch, matched_swatch], axes=axes) + + + +if __name__ == "__main__": + # This demo goes through SDs in a color checker + for name, sd in COLOURCHECKERS_SDS['ColorChecker N Ohta'].items(): + XYZ = sd_to_XYZ(sd) + target = XYZ_to_Lab(XYZ, D65) + + print("The target is '%s' with L=%g, a=%g, b=%g" % (name, *target)) + _, matched_sd, error = jakob_hanika(target, D65) + + plot_comparison(sd, matched_sd, name, error) |