summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2020-06-23 20:08:29 +0200
committerPaweł Redman <pawel.redman@gmail.com>2020-06-23 20:08:29 +0200
commitc0b52c1c841957b27e2dd82a11ecb3b4ff8db265 (patch)
treeda07444182cf7caf88e65fb940f9315349ac03f4
parent33278ad88f8290054aa2b421182019b9167f50ff (diff)
Update the tests again and remove some useless ones.
-rw-r--r--gsoc_common.py60
-rw-r--r--test_colorchecker.py27
-rw-r--r--test_coverage.py104
-rw-r--r--test_diff.py59
-rw-r--r--test_divergence.py9
-rw-r--r--test_error_function.py29
-rw-r--r--test_example.py28
-rw-r--r--test_interpolator.py29
8 files changed, 148 insertions, 197 deletions
diff --git a/gsoc_common.py b/gsoc_common.py
index b73d81c..695edc6 100644
--- a/gsoc_common.py
+++ b/gsoc_common.py
@@ -3,47 +3,53 @@ from colour import *
from colour.difference import delta_E_CIE1976
from colour.colorimetry import *
from colour.plotting import *
+from colour.models import RGB_COLOURSPACES
from matplotlib import pyplot as plt
+
D65_xy = ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
D65 = SpectralDistribution(ILLUMINANT_SDS["D65"])
+colourspace = RGB_COLOURSPACES["ProPhoto RGB"]
# The same wavelength grid is used throughout
wvl = SpectralShape(360, 830, 5)
wvlp = (wvl.range() - 360) / (830 - 360)
+
# This is the model of spectral reflectivity described in the article.
def model(wvlp, ccp):
- yy = ccp[0] * wvlp**2 + ccp[1] * wvlp + ccp[2]
- return 1 / 2 + yy / (2 * np.sqrt(1 + yy ** 2))
+ yy = ccp[0] * wvlp**2 + ccp[1] * wvlp + ccp[2]
+ return 1 / 2 + yy / (2 * np.sqrt(1 + yy ** 2))
+
# Create a SpectralDistribution using given coefficients
def model_sd(ccp, primed=True):
- # FIXME: don't hardcode the wavelength grid; there should be a way
- # of creating a SpectralDistribution from the function alone
- grid = wvlp if primed else wvl.range()
- return SpectralDistribution(model(grid, ccp), wvl.range(), name="Model")
+ # FIXME: don't hardcode the wavelength grid; there should be a way
+ # of creating a SpectralDistribution from the function alone
+ grid = wvlp if primed else wvl.range()
+ return SpectralDistribution(model(grid, ccp), wvl.range(), name="Model")
+
# Makes a comparison plot with SDs and swatches
def plot_comparison(target, matched_sd, label, error, ill_sd, show=True):
- if type(target) is SpectralDistribution:
- target_XYZ = sd_to_XYZ(target, illuminant=ill_sd) / 100
- else:
- target_XYZ = target
- target_RGB = np.clip(XYZ_to_sRGB(target_XYZ), 0, 1)
- target_swatch = ColourSwatch(label, target_RGB)
- matched_XYZ = sd_to_XYZ(matched_sd, illuminant=ill_sd) / 100
- matched_RGB = np.clip(XYZ_to_sRGB(matched_XYZ), 0, 1)
- matched_swatch = ColourSwatch("Model", matched_RGB)
-
- axes = plt.subplot(2, 1, 1)
- plt.title(label)
- if type(target) is SpectralDistribution:
- plot_multi_sds([target, matched_sd], axes=axes, standalone=False)
- else:
- plot_single_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],
- standalone=show, axes=axes)
+ if type(target) is SpectralDistribution:
+ target_XYZ = sd_to_XYZ(target, illuminant=ill_sd) / 100
+ else:
+ target_XYZ = target
+ target_RGB = np.clip(XYZ_to_sRGB(target_XYZ), 0, 1)
+ target_swatch = ColourSwatch(label, target_RGB)
+ matched_XYZ = sd_to_XYZ(matched_sd, illuminant=ill_sd) / 100
+ matched_RGB = np.clip(XYZ_to_sRGB(matched_XYZ), 0, 1)
+ matched_swatch = ColourSwatch("Model", matched_RGB)
+
+ axes = plt.subplot(2, 1, 1)
+ plt.title(label)
+ if type(target) is SpectralDistribution:
+ plot_multi_sds([target, matched_sd], axes=axes, standalone=False)
+ else:
+ plot_single_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],
+ standalone=show, axes=axes)
diff --git a/test_colorchecker.py b/test_colorchecker.py
index 612d7fb..1503874 100644
--- a/test_colorchecker.py
+++ b/test_colorchecker.py
@@ -1,14 +1,25 @@
import numpy as np
from colour import *
-from colour.recovery import XYZ_to_sd_Jakob2019
-from gsoc_common import D65, plot_comparison
+from colour.recovery import RGB_to_sd_Jakob2019
+from gsoc_common import *
+
# This demo goes through SDs in a color checker
if __name__ == "__main__":
- for name, sd in COLOURCHECKER_SDS['ColorChecker N Ohta'].items():
- XYZ = sd_to_XYZ(sd, illuminant=D65) / 100
+ for name, sd in COLOURCHECKER_SDS['ColorChecker N Ohta'].items():
+ XYZ = sd_to_XYZ(sd, illuminant=D65) / 100
+
+ RGB = XYZ_to_RGB(
+ XYZ,
+ D65_xy,
+ colourspace.whitepoint,
+ colourspace.XYZ_to_RGB_matrix,
+ )
+
+ recovered_sd, error = RGB_to_sd_Jakob2019(
+ RGB,
+ colourspace,
+ return_error=True
+ )
- print("Color checker: The target is '%s' with X=%g, Y=%g, Z=%g"
- % (name, *XYZ))
- recovered_sd, error = XYZ_to_sd_Jakob2019(XYZ, return_error=True)
- plot_comparison(sd, recovered_sd, name, error, D65)
+ plot_comparison(sd, recovered_sd, name, error, D65)
diff --git a/test_coverage.py b/test_coverage.py
index ee519f4..de2d8a2 100644
--- a/test_coverage.py
+++ b/test_coverage.py
@@ -7,69 +7,67 @@ from gsoc_common import model_sd, D65, D65_xy, plot_comparison
# Solve for a specific RGB color
def optimize_RGB(linear_RGB, coeffs_0):
- RGB = eotf_inverse_sRGB(linear_RGB)
- XYZ = sRGB_to_XYZ(RGB, D65_xy)
-
- coeffs, error = coefficients_Jakob2019(
- XYZ,
- dimensionalise=False,
- coefficients_0=coeffs_0
- )
-
- if error > 1e-6:
- with open("out/bad.txt", "a") as fd:
- fd.write("\n")
- fd.write("linear_RGB=%s\n" % linear_RGB)
- fd.write("XYZ=%s\n" % XYZ)
- fd.write("coeffs_0=%s\n" % coeffs_0)
- fd.write("coeffs=%s, error=%g\n" % (coeffs, error))
-
- # A low budget graph to quickly see what's going on
- log_error = np.log10(error)
- bars = "|" * max(0, int(5 * (log_error + 9)))
-
- print("%12.5g %12.5g %12.5g %5.3f %s" % (*XYZ, log_error, bars))
- return coeffs
+ coeffs, error = coefficients_Jakob2019(
+ linear_RGB,
+ RGB_COLOURSPACES["sRGB"],
+ dimensionalise=False,
+ coefficients_0=coeffs_0
+ )
+
+ if error > 1e-6:
+ with open("out/bad.txt", "a") as fd:
+ fd.write("\n")
+ fd.write("linear_RGB=%s\n" % linear_RGB)
+ fd.write("XYZ=%s\n" % XYZ)
+ fd.write("coeffs_0=%s\n" % coeffs_0)
+ fd.write("coeffs=%s, error=%g\n" % (coeffs, error))
+
+ # A low budget graph to quickly see what's going on
+ log_error = np.log10(error)
+ bars = "|" * max(0, int(5 * (log_error + 9)))
+
+ print("%12.5g %12.5g %12.5g %5.3f %s" % (*linear_RGB, log_error, bars))
+ return coeffs
# Solve for all lightness values of a fully saturated RGB color
def optimize_chromaticity(linear_RGB):
- # alpha's aren't spaced equally (see the article)
- def smoothstep(x):
- return x**2 * (3 - 2 * x)
+ # alpha's aren't spaced equally (see the article)
+ def smoothstep(x):
+ return x**2 * (3 - 2 * x)
- steps = np.arange(0, LIGHTNESS_STEPS)
- alphas = smoothstep(smoothstep(steps / LIGHTNESS_STEPS))
+ steps = np.arange(0, LIGHTNESS_STEPS)
+ alphas = smoothstep(smoothstep(steps / LIGHTNESS_STEPS))
- i_mid = LIGHTNESS_STEPS // 5
- coeffs_mid = optimize_RGB(linear_RGB * alphas[i_mid], (0, 0, 0))
+ i_mid = LIGHTNESS_STEPS // 5
+ coeffs_mid = optimize_RGB(linear_RGB * alphas[i_mid], (0, 0, 0))
- coeffs_0 = coeffs_mid
- for i in range(i_mid + 1, LIGHTNESS_STEPS):
- coeffs_0 = optimize_RGB(linear_RGB * alphas[i], coeffs_0)
+ coeffs_0 = coeffs_mid
+ for i in range(i_mid + 1, LIGHTNESS_STEPS):
+ coeffs_0 = optimize_RGB(linear_RGB * alphas[i], coeffs_0)
- coeffs_0 = coeffs_mid
- for i in reversed(range(0, i_mid)):
- coeffs_0 = optimize_RGB(linear_RGB * alphas[i], coeffs_0)
+ coeffs_0 = coeffs_mid
+ for i in reversed(range(0, i_mid)):
+ coeffs_0 = optimize_RGB(linear_RGB * alphas[i], coeffs_0)
# This program runs XYZ_to_sd_Jakob2019 converges on a large number of inputs,
# covering an entire RGB gamut, to see if it'll diverge somewhere.
if __name__ == "__main__":
- CHROMA_STEPS = 8
- LIGHTNESS_STEPS = 64
-
- with open("out/bad.txt", "w") as fd:
- fd.write("Going through %dx%dx%d cubes\n"
- % (LIGHTNESS_STEPS, CHROMA_STEPS, CHROMA_STEPS))
-
- args = []
- for A in np.linspace(0, 1, CHROMA_STEPS):
- for B in np.linspace(0, 1, CHROMA_STEPS):
- for RGB in [np.array([1, A, B]),
- np.array([A, 1, B]),
- np.array([A, B, 1])]:
- args.append(RGB)
-
- pool = multiprocessing.Pool()
- pool.map(optimize_chromaticity, args)
+ CHROMA_STEPS = 8
+ LIGHTNESS_STEPS = 64
+
+ with open("out/bad.txt", "w") as fd:
+ fd.write("Going through %dx%dx%d cubes\n"
+ % (LIGHTNESS_STEPS, CHROMA_STEPS, CHROMA_STEPS))
+
+ args = []
+ for A in np.linspace(0, 1, CHROMA_STEPS):
+ for B in np.linspace(0, 1, CHROMA_STEPS):
+ for RGB in [np.array([1, A, B]),
+ np.array([A, 1, B]),
+ np.array([A, B, 1])]:
+ args.append(RGB)
+
+ pool = multiprocessing.Pool()
+ pool.map(optimize_chromaticity, args)
diff --git a/test_diff.py b/test_diff.py
index 0ee5f58..4429894 100644
--- a/test_diff.py
+++ b/test_diff.py
@@ -5,46 +5,47 @@ from colour.recovery import error_function_Jakob2019
from matplotlib import pyplot as plt
from gsoc_common import plot_comparison
+
# This test checks if derivatives are calculated correctly by comparing them
# to finite differences.
if __name__ == "__main__":
- shape = SpectralShape(360, 830, 1)
- cmfs = STANDARD_OBSERVER_CMFS["CIE 1931 2 Degree Standard Observer"].align(shape)
+ shape = SpectralShape(360, 830, 1)
+ cmfs = STANDARD_OBSERVER_CMFS["CIE 1931 2 Degree Standard Observer"].align(shape)
- illuminant = SpectralDistribution(ILLUMINANT_SDS["D65"]).align(shape)
- illuminant_XYZ = sd_to_XYZ(illuminant) / 100
+ illuminant = SpectralDistribution(ILLUMINANT_SDS["D65"]).align(shape)
+ illuminant_XYZ = sd_to_XYZ(illuminant) / 100
- target = np.array([50, -20, 30]) # Some arbitrary Lab colour
- xs = np.linspace(-10, 10, 500)
- h = xs[1] - xs[0]
+ target = np.array([50, -20, 30]) # Some arbitrary Lab colour
+ xs = np.linspace(-10, 10, 500)
+ h = xs[1] - xs[0]
- # Vary one coefficient at a time
- for c_index in range(3):
- errors = np.empty(len(xs))
- derrors = np.empty(len(xs))
+ # Vary one coefficient at a time
+ for c_index in range(3):
+ errors = np.empty(len(xs))
+ derrors = np.empty(len(xs))
- for i, x in enumerate(xs):
- c = np.array([1.0, 1, 1])
- c[c_index] = x
+ for i, x in enumerate(xs):
+ c = np.array([1.0, 1, 1])
+ c[c_index] = x
- error, derror_dc = error_function_Jakob2019(
- c, target, shape, cmfs, illuminant, illuminant_XYZ
- )
+ error, derror_dc = error_function_Jakob2019(
+ c, target, shape, cmfs, illuminant, illuminant_XYZ
+ )
- errors[i] = error
- derrors[i] = derror_dc[c_index]
+ errors[i] = error
+ derrors[i] = derror_dc[c_index]
- plt.subplot(2, 3, 1 + c_index)
- plt.xlabel("c%d" % c_index)
- plt.ylabel("ΔE")
- plt.plot(xs, errors)
+ plt.subplot(2, 3, 1 + c_index)
+ plt.xlabel("c%d" % c_index)
+ plt.ylabel("ΔE")
+ plt.plot(xs, errors)
- plt.subplot(2, 3, 4 + c_index)
- plt.xlabel("c%d" % c_index)
- plt.ylabel("dΔE/dc%d" % c_index)
+ plt.subplot(2, 3, 4 + c_index)
+ plt.xlabel("c%d" % c_index)
+ plt.ylabel("dΔE/dc%d" % c_index)
- plt.plot(xs, derrors, "k-")
- plt.plot(xs[:-1] + h / 2, np.diff(errors) / h, "r:")
+ plt.plot(xs, derrors, "k-")
+ plt.plot(xs[:-1] + h / 2, np.diff(errors) / h, "r:")
- plt.show()
+ plt.show()
diff --git a/test_divergence.py b/test_divergence.py
deleted file mode 100644
index fc7956c..0000000
--- a/test_divergence.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import numpy as np
-from colour import *
-from colour.recovery import XYZ_to_sd_Jakob2019
-from gsoc_common import D65, plot_comparison
-
-if __name__ == "__main__":
- XYZ = np.array([0.1788, 0.3576, 0.0596])
- found_sd, error = XYZ_to_sd_Jakob2019(XYZ, return_error=True)
- plot_comparison(XYZ, found_sd, "Target", error, D65)
diff --git a/test_error_function.py b/test_error_function.py
deleted file mode 100644
index 99e1268..0000000
--- a/test_error_function.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import numpy as np
-from colour import *
-from colour.recovery import error_function_Jakob2019
-from gsoc_common import model_sd, D65_xy
-
-if __name__ == "__main__":
- shape = SpectralShape(360, 830, 1)
- cmfs = STANDARD_OBSERVER_CMFS["CIE 1931 2 Degree Standard Observer"].align(shape)
-
- illuminant = SpectralDistribution(ILLUMINANT_SDS["D65"]).align(shape)
- illuminant_XYZ = sd_to_XYZ(illuminant) / 100
-
- coefficients = np.array([8.70184886, -13.19804478, 2.12180137])
- target = np.array([20, 50, 30])
-
- error, derror_dc, R, XYZ, Lab = error_function_Jakob2019(
- coefficients, target, shape, cmfs,
- illuminant, illuminant_XYZ, True
- )
-
- sd = model_sd(coefficients)
- good_XYZ = sd_to_XYZ(sd, illuminant=illuminant)
- good_Lab = XYZ_to_Lab(good_XYZ / 100, D65_xy)
-
- print("Good XYZ: %g %g %g" % (*good_XYZ,))
- print(" EF XYZ: %g %g %g" % (*XYZ,))
- print("Good Lab: %g %g %g" % (*good_Lab,))
- print(" EF Lab: %g %g %g" % (*Lab,))
- print(" EF* Lab: %g %g %g" % (*XYZ_to_Lab(XYZ / 100, D65_xy),))
diff --git a/test_example.py b/test_example.py
deleted file mode 100644
index a4104b1..0000000
--- a/test_example.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import numpy as np
-from colour import *
-from colour.models import eotf_inverse_sRGB
-from colour.difference import delta_E_CIE1976
-from colour.recovery import XYZ_to_sd_Jakob2019
-from gsoc_common import model_sd, D65, D65_xy, plot_comparison
-
-if __name__ == "__main__":
- # These numbers are taken from Jakob and Hanika's Jupyter notebook.
- RGB_ref = np.array([0.79264853, 0.4, 0.63703843]) # *linear* sRGB
- cc_ref = np.array([ 18.70184886, -13.19804478, 2.12180137])
-
- XYZ = sRGB_to_XYZ(eotf_inverse_sRGB(RGB_ref), D65_xy)
- Lab = XYZ_to_Lab(XYZ, D65_xy)
- print("Target: X=%g, Y=%g, Z=%g, L=%g, a=%g, b=%g" % (*XYZ, *Lab))
-
- reference_sd = model_sd(cc_ref)
- reference_XYZ = sd_to_XYZ(reference_sd, illuminant=D65) / 100
- reference_Lab = XYZ_to_Lab(reference_XYZ, D65_xy)
-
- found_sd, error = XYZ_to_sd_Jakob2019(XYZ, return_error=True)
- found_XYZ = sd_to_XYZ(found_sd, illuminant=D65) / 100
- found_Lab = XYZ_to_Lab(found_XYZ, D65_xy)
-
- print("Our results differ from the reference by ΔE = %g" \
- % delta_E_CIE1976(reference_Lab, found_Lab))
-
- plot_comparison(XYZ, found_sd, "Reference", error, D65)
diff --git a/test_interpolator.py b/test_interpolator.py
index ab542f3..ba476da 100644
--- a/test_interpolator.py
+++ b/test_interpolator.py
@@ -3,25 +3,26 @@ from colour import *
from colour.difference import delta_E_CIE1976
from colour.models import eotf_inverse_sRGB
from colour.recovery import Jakob2019Interpolator
-from gsoc_common import D65, D65_xy, model_sd, plot_comparison
+from gsoc_common import *
+
# This script tests if the interpolator correctly handles multi-dimensional
# inputs.
if __name__ == "__main__":
- interp = Jakob2019Interpolator()
- interp.from_file("data/srgb.coeff")
+ interp = Jakob2019Interpolator()
+ interp.from_file("data/srgb.coeff")
- RGBs = np.random.random((7, 6, 5, 4, 3))
- ccs = interp.coefficients(RGBs)
+ RGBs = np.random.random((7, 6, 5, 4, 3))
+ ccs = interp.coefficients(RGBs)
- RGB = eotf_inverse_sRGB(RGBs[0, 0, 0, 0, :])
- XYZ = sRGB_to_XYZ(RGB)
- Lab = XYZ_to_Lab(XYZ, D65_xy)
- cc = ccs[0, 0, 0, 0, :]
+ RGB = eotf_inverse_sRGB(RGBs[0, 0, 0, 0, :])
+ XYZ = sRGB_to_XYZ(RGB)
+ Lab = XYZ_to_Lab(XYZ, D65_xy)
+ cc = ccs[0, 0, 0, 0, :]
- matched_sd = model_sd(cc, primed=False)
- matched_XYZ = sd_to_XYZ(matched_sd, illuminant=D65) / 100
- matched_Lab = XYZ_to_Lab(matched_XYZ, D65_xy)
- error = delta_E_CIE1976(Lab, matched_Lab)
+ matched_sd = model_sd(cc, primed=False)
+ matched_XYZ = sd_to_XYZ(matched_sd, illuminant=D65) / 100
+ matched_Lab = XYZ_to_Lab(matched_XYZ, D65_xy)
+ error = delta_E_CIE1976(Lab, matched_Lab)
- plot_comparison(XYZ, matched_sd, "Model", error, D65)
+ plot_comparison(XYZ, matched_sd, "Model", error, D65)