summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2026-06-14 18:52:46 +0200
committerPaweł Redman <pawel.redman@gmail.com>2026-06-14 18:52:46 +0200
commit8ce3589d02e14322fc9aa7fe647a6f4f66a4eb59 (patch)
tree6e3b0989f171c7c14d8d6f3e432ea0571d1e2160
parent89a67854f5af72836498a9412f759d4337fad32d (diff)
Fix typos in migrated GSoC posts
Co-authored-by: Cursor <cursoragent@cursor.com>
-rw-r--r--site/content/gsoc/convergence_problems.typ2
-rw-r--r--site/content/gsoc/differentiating_code.typ4
-rw-r--r--site/content/gsoc/differentiating_code_addendum.typ4
-rw-r--r--site/content/gsoc/mallett_merge.typ2
-rw-r--r--site/content/gsoc/method_comparison.typ8
-rw-r--r--site/content/gsoc/mid_august.typ8
-rw-r--r--site/content/gsoc/new_color_quality_metrics.typ4
-rw-r--r--site/content/gsoc/nondimensionalization.typ2
-rw-r--r--site/content/gsoc/otsu2018_3.typ2
-rw-r--r--site/content/gsoc/otsu2018_4.typ2
-rw-r--r--site/content/gsoc/proposal.typ8
11 files changed, 23 insertions, 23 deletions
diff --git a/site/content/gsoc/convergence_problems.typ b/site/content/gsoc/convergence_problems.typ
index 320261a..8be6671 100644
--- a/site/content/gsoc/convergence_problems.typ
+++ b/site/content/gsoc/convergence_problems.typ
@@ -10,7 +10,7 @@
caption: [Solvers tend to get attracted to spectra like this and get stuck],
)
-I've been trying to write code that find parameters to the reflectivity model I mentioned in #link-self("gsoc/nondimensionalization")[an earlier post]. The objective is to minimize the difference between the color given the model and the target. It turns out the optimization is rather difficult and all SciPy solvers (including global optimization algorithms) frequently fail. At the moment I only have a loose idea of what's going on.
+I've been trying to write code that finds parameters to the reflectivity model I mentioned in #link-self("gsoc/nondimensionalization")[an earlier post]. The objective is to minimize the difference between the color given the model and the target. It turns out the optimization is rather difficult and all SciPy solvers (including global optimization algorithms) frequently fail. At the moment I only have a loose idea of what's going on.
Because of the sigmoid function used in the model, very large coefficients create spectra resembling a rectangular or a step function (like on the picture). Then, it's always possible to adjust the coefficients to make the spectrum even steeper without changing its overall shape. After doing this enough times, coefficients are so large that varying them barely changes the spectrum, a situation solvers treat as having found a good minimum. I suppose it's because the gradient becomes very small, but a better-suited algorithm would look at the near-zero Hessian and notice it's moving along an asymptote (a bad minimum here). I'm also not sure why solvers are (sometimes) attracted to big numbers in the first place.
diff --git a/site/content/gsoc/differentiating_code.typ b/site/content/gsoc/differentiating_code.typ
index 8d7aa03..d68efb7 100644
--- a/site/content/gsoc/differentiating_code.typ
+++ b/site/content/gsoc/differentiating_code.typ
@@ -30,7 +30,7 @@ $
Y &= k integral overline(E)(lambda) overline(y)(lambda) dif lambda \
Z &= k integral overline(E)(lambda) overline(z)(lambda) dif lambda
$
-where $overline(x)$, $overline(y)$ and $overline(z)$ are the cmfs (color matching functions), quantizing human color vision. They are closely related to (but not the same as) spectral sensivities of the three different kinds of cone cells in a human eye. In particular $overline(y)$ is the luminosity function used in photometry, describing how bright light of different wavelengths appears. The normalization constant $k$,
+where $overline(x)$, $overline(y)$ and $overline(z)$ are the cmfs (color matching functions), quantifying human color vision. They are closely related to (but not the same as) spectral sensitivities of the three different kinds of cone cells in a human eye. In particular $overline(y)$ is the luminosity function used in photometry, describing how bright light of different wavelengths appears. The normalization constant $k$,
$
k = (integral I(lambda) y(lambda) dif lambda)^(-1)
$
@@ -74,7 +74,7 @@ $
$
The derivatives of $R$ have already been computed in the previous step. Doing this step by step will eventually lead to $(dif (Delta E)) / (dif L^*)$, $(dif (Delta E)) / (dif a^*)$ and $(dif (Delta E)) / (dif b^*)$ and ultimately the gradient of $Delta E$. Take a look at the #link("https://github.com/enneract/colour/blob/b8c113efb7b819fce81213e6f6fa41db90b0b61c/colour/recovery/jakob2019.py#L198-L252")[full code] to see all the formulas.
-= Automatic differentation
+= Automatic differentiation
There is a clever trick to "automagically" compute derivatives, without having to go step-by-step, differentiating every operation and writing code manually like I did. #link("https://en.wikipedia.org/wiki/Dual_number")[Dual numbers] have the interesting property that, when used correctly, their dual parts are the derivatives of their real parts. They're defined very similarly to complex numbers, but instead of $z = a + b i$, $i^2 = -1$, one has $z = a + b epsilon$, $epsilon^2 = 0$. (Even more interestingly, this can be extended to higher derivatives as well!). See #link("https://en.wikipedia.org/wiki/Automatic_differentiation")[automatic differentiation] on Wikipedia.
diff --git a/site/content/gsoc/differentiating_code_addendum.typ b/site/content/gsoc/differentiating_code_addendum.typ
index b6dcfa5..9d8bad1 100644
--- a/site/content/gsoc/differentiating_code_addendum.typ
+++ b/site/content/gsoc/differentiating_code_addendum.typ
@@ -5,7 +5,7 @@
date: datetime(year: 2020, month: 7, day: 14),
)
-This post describes how to the compute derivatives mentioned in an #link-self("gsoc/differentiating_code")[earlier post] using Maxima, a computer algebra system. Code is listed in a monospaced font and answers are written directly below, in LaTeX. *Ending lines with dollar signs in Maxima (instead of semicolons) surpresses the output,* which is very useful for definitions.
+This post describes how to compute the derivatives mentioned in an #link-self("gsoc/differentiating_code")[earlier post] using Maxima, a computer algebra system. Code is listed in a monospaced font and answers are written directly below, in LaTeX. *Ending lines with dollar signs in Maxima (instead of semicolons) suppresses the output,* which is very useful for definitions.
Start by defining the reflectance model.
```
@@ -49,4 +49,4 @@ subst(delta_E_, delta_E, %);
$
((b - b_"target") (dif b) / (dif c_0) + (a - a_"target") (dif a) / (dif c_0) + (L - L_"target") (dif L) / (dif c_0)) / (Delta E)
$
-The other derivatives look the same. Simply substitute $c_1$ or $c_2$ for $c_0$ in the expression above to get obtain them.
+The other derivatives look the same. Simply substitute $c_1$ or $c_2$ for $c_0$ in the expression above to obtain them.
diff --git a/site/content/gsoc/mallett_merge.typ b/site/content/gsoc/mallett_merge.typ
index d45fc6f..115a4da 100644
--- a/site/content/gsoc/mallett_merge.typ
+++ b/site/content/gsoc/mallett_merge.typ
@@ -1,7 +1,7 @@
#import "/common.typ": post
#show: post.with(
- title: "Mallett and Yuksel (2019) merged",
+ title: "Mallett and Yuksel (2019) merged",
date: datetime(year: 2020, month: 8, day: 20),
)
diff --git a/site/content/gsoc/method_comparison.typ b/site/content/gsoc/method_comparison.typ
index f6219e7..db6b4df 100644
--- a/site/content/gsoc/method_comparison.typ
+++ b/site/content/gsoc/method_comparison.typ
@@ -7,8 +7,8 @@
I took the dataset published alongside
Otsu et al. (2018) @Otsu2018 and #link("https://colour.readthedocs.io/en/develop/generated/colour.COLOURCHECKER_SDS.html")[colorcheckers included in Colour], about 1300 reflectance spectra in total, reconstructed them using various methods and compared them with the original data. I used #link("https://en.wikipedia.org/wiki/Illuminant_D65")[CIE Standard Illuminant D65] and two metrics:
-- CIE 1976 ΔE (ΔE is defined as the Euclidean distance between colors in CIELAB color space.), quantizing the *perceived* difference between two colors. Values around 2.4 are taken as the just-noticeable difference, so deltas smaller than this should be insignificant.
-- Mean squared error (MSE), the mean squared difference between the original and reconstructed spectra. For any reflectance, there infinitely many metamers, other reflectances that look exactly the same (ΔE=0) under a given illuminant. Good spectral upsampling should result in *realistic* spectra, i.e. those that look similar to those of common and natural materials. This helps spectral rendering to achieve natural results as well. See Peters et al. (2019) @Peters2019 for an interesting and more in-depth discussion of the topic.
+- CIE 1976 ΔE (ΔE is defined as the Euclidean distance between colors in CIELAB color space.), quantifying the *perceived* difference between two colors. Values around 2.4 are taken as the just-noticeable difference, so deltas smaller than this should be insignificant.
+- Mean squared error (MSE), the mean squared difference between the original and reconstructed spectra. For any reflectance, there are infinitely many metamers, other reflectances that look exactly the same (ΔE=0) under a given illuminant. Good spectral upsampling should result in *realistic* spectra, i.e. those that look similar to those of common and natural materials. This helps spectral rendering to achieve natural results as well. See Peters et al. (2019) @Peters2019 for an interesting and more in-depth discussion of the topic.
Below are chromaticity diagrams with error heatmaps drawn over them. The color of each square bin represents the mean error of samples lying inside the square. The numbers above the diagrams are, in order, the minimum, maximum, mean and standard deviation of the errors.
@@ -34,7 +34,7 @@ Letting SciPy's `L-BFGS-B` solver decide on its own when to stop actually makes
caption: [Errors for Otsu et al. (2018) with `clip` set to False.],
)
-The picture above shows that the method has no round-trip errors, ie. the recovered spectra are exact metamers (have the exact same color).
+The picture above shows that the method has no round-trip errors, i.e., the recovered spectra are exact metamers (have the exact same color).
#figure(
img(static("gsoc/error_otsu.jpg")),
@@ -79,6 +79,6 @@ Since all methods (bugs and user errors aside) have negligible ΔE's, I'll summa
[Jakob and Hanika (2019)], [0.029 2], [60 ×], [0.152], [0.032],
)
-Unsurprisingly, the data-driven method produces results that look like data the most. Jakob and Hanika's equation seems model much more artificial reflectances.
+Unsurprisingly, the data-driven method produces results that look like data the most. Jakob and Hanika's equation seems to model much more artificial reflectances.
#bibliography("/refs.bib")
diff --git a/site/content/gsoc/mid_august.typ b/site/content/gsoc/mid_august.typ
index d847872..ef06246 100644
--- a/site/content/gsoc/mid_august.typ
+++ b/site/content/gsoc/mid_august.typ
@@ -25,15 +25,15 @@ The #link("https://github.com/colour-science/colour/pull/607")[pull request] is
#html.elem("span", attrs: (id: "mallett_and_yuksel_2019"))[]
= Mallett and Yuksel (2019) <mallett_and_yuksel_2019>
-This method seeks to create reflectivities $bold(s)_r$, $bold(s)_g$, $bold(s)_b$ that match RGB primaries. and use them for reconstruction. Given an $(r, g, b)$ triplet, the recovered spectrum is simply:
+This method seeks to create reflectivities $bold(s)_r$, $bold(s)_g$, $bold(s)_b$ that match RGB primaries and use them for reconstruction. Given an $(r, g, b)$ triplet, the recovered spectrum is simply:
$
bold(s) = r bold(s)_r + g bold(s)_g + b bold(s)_b
$
-For this to work, the basis reflectivites have to obey a few constraints:
-- They must color-match RGB primaries, ie. have RGB values of [1, 0, 0], [0, 1, 0] and [0, 0, 1].
+For this to work, the basis reflectivities have to obey a few constraints:
+- They must color-match RGB primaries, i.e., have RGB values of [1, 0, 0], [0, 1, 0] and [0, 0, 1].
- They must sum up to an all-ones spectrum (100% reflectivity at all wavelengths), so that pure white can be recovered correctly.
- None of them can have values outside [0, 1].
-- They must minimize a chosen scalar function, e.g. one quantizing "roughness," so that the spectra are smooth and noisy solutions are avoided.
+- They must minimize a chosen scalar function, e.g. one quantifying "roughness," so that the spectra are smooth and noisy solutions are avoided.
The code for this is surprisingly straightforward, thanks to SciPy's excellent interface to constrained and bounded optimization.
A quick overview of the code:
diff --git a/site/content/gsoc/new_color_quality_metrics.typ b/site/content/gsoc/new_color_quality_metrics.typ
index 298b553..4f83c00 100644
--- a/site/content/gsoc/new_color_quality_metrics.typ
+++ b/site/content/gsoc/new_color_quality_metrics.typ
@@ -22,11 +22,11 @@ There are infinitely many light spectra that humans perceive as white (see #link
[*s [a.u.]*], [...], [0.000000], [0.619473], [0.000000], [...], [0.000000], [0.166711], [0.833289], [0.000000], [...], [],
)
-This combination of deep blue and orange laser light, showing up as two very narrow peaks on a plot, will match the standard CIE D65 daylight illluminant perfectly. Of course, if you tried to light up a room with it, you'd notice that it's not "proper" white. Because there would be no light other than blue and orange to reflect off surfaces, everything would look blue, orange, or something in between.
+This combination of deep blue and orange laser light, showing up as two very narrow peaks on a plot, will match the standard CIE D65 daylight illuminant perfectly. Of course, if you tried to light up a room with it, you'd notice that it's not "proper" white. Because there would be no light other than blue and orange to reflect off surfaces, everything would look blue, orange, or something in between.
Fluorescent lamps are infamous for having very peaky emission spectra as well. #link("https://commons.wikimedia.org/wiki/File:Fluorescent_lighting_spectrum_peaks_labeled_with_colored_peaks_added.png")[This Wikimedia Commons image] shows that the peaks are emission lines of various rare elements inside the lamp. Just like in case of my theoretical lamp, objects lit with such lamps don't have quite the right colors. The same problem applies to LEDs as well. The effect a light source has on colors of things is called *color rendering*.
-Below is a chart showing how colors are affected by various daylight (6500 K) illuminants. D65 is the reference, Kinoton is some sort of a projector, FL1 is a standard fluorescent lamp, a LED, a mercury lamp (a bit too blue) and my made up spectrum.
+Below is a chart showing how colors are affected by various daylight (6500 K) illuminants. D65 is the reference, Kinoton is some sort of a projector, FL1 is a standard fluorescent lamp, a LED, a mercury lamp (a bit too blue) and my made-up spectrum.
#figure(
img(static("gsoc/color_rendering.png")),
diff --git a/site/content/gsoc/nondimensionalization.typ b/site/content/gsoc/nondimensionalization.typ
index aba870a..7c6dff6 100644
--- a/site/content/gsoc/nondimensionalization.typ
+++ b/site/content/gsoc/nondimensionalization.typ
@@ -9,7 +9,7 @@ Here's a simple but interesting bit of math that came up while working on an imp
$
R(lambda) = sigma(c_0 lambda^2 + c_1 lambda + c_2)
$
-where $c_0, c_1, c_2$ are model parameters, $lambda in [360 "nm", 830 "nm"]$ and $sigma: RR -> [0, 1]$ is a sigmoid function. Reflectivities tend to be rather smooth and the values of the polynomial not too large. If wavelengths are expressed numerically in nanometers, $c_1$ and especially $c_0$ will tend to be very small. This makes it harder to find minima of the function, as solvers tend get confused, unless special care is taken to change the default tolerances properly.
+where $c_0, c_1, c_2$ are model parameters, $lambda in [360 "nm", 830 "nm"]$ and $sigma: RR -> [0, 1]$ is a sigmoid function. Reflectivities tend to be rather smooth and the values of the polynomial not too large. If wavelengths are expressed numerically in nanometers, $c_1$ and especially $c_0$ will tend to be very small. This makes it harder to find minima of the function, as solvers tend to get confused, unless special care is taken to change the default tolerances properly.
A more straightforward solution is to rescale $lambda$ to a more convenient range, a process a physicist would call *nondimensionalization*. Following the authors of the article, let
$
diff --git a/site/content/gsoc/otsu2018_3.typ b/site/content/gsoc/otsu2018_3.typ
index aa1666e..c37b8d8 100644
--- a/site/content/gsoc/otsu2018_3.typ
+++ b/site/content/gsoc/otsu2018_3.typ
@@ -5,7 +5,7 @@
date: datetime(year: 2020, month: 7, day: 26),
)
-In the #link-self("gsoc/otsu2018_2")[previous post] I explained how PCA can be used to create a convenient basis for representing real-world reflectivities. The procedure uses a set of measured spectra to compute an orthonormal basis, sorted by variance or "statistical significance." All but the three most significant vectors are then truncated. I called the difference between the actual spectrum and the reconstructed error term $bold(epsilon)$.
+In the #link-self("gsoc/otsu2018_2")[previous post] I explained how PCA can be used to create a convenient basis for representing real-world reflectivities. The procedure uses a set of measured spectra to compute an orthonormal basis, sorted by variance or "statistical significance." All but the three most significant vectors are then truncated. I called the difference between the actual spectrum and the reconstructed spectrum the error term $bold(epsilon)$.
Naturally the smaller the error, the better. Because $bold(epsilon)$ is a vector, the authors chose the Euclidean norm (2-norm or L2) to represent the (scalar) error:
$
diff --git a/site/content/gsoc/otsu2018_4.typ b/site/content/gsoc/otsu2018_4.typ
index a5d01db..d0584f6 100644
--- a/site/content/gsoc/otsu2018_4.typ
+++ b/site/content/gsoc/otsu2018_4.typ
@@ -7,7 +7,7 @@
In the #link-self("gsoc/otsu2018_3")[previous part] I mentioned that reflectances reconstructed using this method can exceed the [0, 1] range and violate conservation of energy. Indeed, I could simply ask it for an imaginary color and get a weird, unphysical spectrum.
-What's interesting is that this doesn't happen in practice, when upsampling real data. Out of almost 1300 reflectances I tested, I found only one bad reconstruction. Using the original clustering, the result for `magenta` from `BabelColor Average` colorchecker reflects 100,6% of light at 730 nm. Clipping this value to 100% creates a tiny color difference, orders of magnitude smaller than the #link("https://en.wikipedia.org/wiki/Just-noticeable_difference")[just-noticeable difference].
+What's interesting is that this doesn't happen in practice, when upsampling real data. Out of almost 1300 reflectances I tested, I found only one bad reconstruction. Using the original clustering, the result for `magenta` from `BabelColor Average` colorchecker reflects 100.6% of light at 730 nm. Clipping this value to 100% creates a tiny color difference, orders of magnitude smaller than the #link("https://en.wikipedia.org/wiki/Just-noticeable_difference")[just-noticeable difference].
#figure(
img(static("gsoc/otsu2018_error.png")),
diff --git a/site/content/gsoc/proposal.typ b/site/content/gsoc/proposal.typ
index 467633b..9731600 100644
--- a/site/content/gsoc/proposal.typ
+++ b/site/content/gsoc/proposal.typ
@@ -29,11 +29,11 @@ Spectral upsampling is the process of extrapolating spectral information from RG
= Technical Details
-Colour is a Python library, targeting both Python 2.7+ and Python 3.5+. Like many scientific projects, it relies on #link("https://www.scipy.org/")[SciPy] for array operations, numerical algorithms (most importantly function minimization) and plotting. There is a number other dependencies, but they would not be relevant to this project in particular. There is also an extensive list of #link("https://www.colour-science.org/contributing/")[rules and guidelines] for contributors regarding code style, documentation, unit testing and so on that I will have to follow in order to have my work accepted upstream.
+Colour is a Python library, targeting both Python 2.7+ and Python 3.5+. Like many scientific projects, it relies on #link("https://www.scipy.org/")[SciPy] for array operations, numerical algorithms (most importantly function minimization) and plotting. There are a number of other dependencies, but they would not be relevant to this project in particular. There is also an extensive list of #link("https://www.colour-science.org/contributing/")[rules and guidelines] for contributors regarding code style, documentation, unit testing and so on that I will have to follow in order to have my work accepted upstream.
*How does spectral upsampling work?*
-Colour already has a submodule called colour.recovery, which deals with reflectance recovery. Currently two methods are implemented, Smith (1999) @Smits1999 and Meng et al. (2015) @Meng2015. Because I can use the existing code and tests as reference, integrating new code into the codebase will be relatively simple. The priority would be to extend the submodule by adding support for Otsu et al. (2018) @Otsu2018 and Jakob and Hanika (2019) @Jakob2019, with implementations of Mallett and Yuksel (2019) @Mallett2019 and Peters et al. (2019) @Peters2019 being secondary goals. A tertiary goal that I thought up myself and is not a part of the original #link("https://github.com/colour-science/GSoC/blob/master/2020/GSoC-2020-Project-Ideas.md#new-spectral-upsampling-methods")[project proposal], but was deemed a good idea by one of the mentors, is implementing the interpolation scheme described in Meng et al.’s article, which currently is missing from Colour.
+Colour already has a submodule called colour.recovery, which deals with reflectance recovery. Currently two methods are implemented, Smits (1999) @Smits1999 and Meng et al. (2015) @Meng2015. Because I can use the existing code and tests as reference, integrating new code into the codebase will be relatively simple. The priority would be to extend the submodule by adding support for Otsu et al. (2018) @Otsu2018 and Jakob and Hanika (2019) @Jakob2019, with implementations of Mallett and Yuksel (2019) @Mallett2019 and Peters et al. (2019) @Peters2019 being secondary goals. A tertiary goal that I thought up myself and is not a part of the original #link("https://github.com/colour-science/GSoC/blob/master/2020/GSoC-2020-Project-Ideas.md#new-spectral-upsampling-methods")[project proposal], but was deemed a good idea by one of the mentors, is implementing the interpolation scheme described in Meng et al.’s article, which currently is missing from Colour.
Given spectral distribution of the illumination, the process of converting the spectral reflectance of a surface to tristimulus values, which quantify the color perception of the surface, is straightforward. If the reflectance is represented as a vector with a suitable N-dimensional basis in the function space, the conversion is done by a simple linear map φ : ℝᴺ→ℝ³. Unless N = 3, which is rarely enough in real world scenarios, this mapping is non-invertible. (The original text contains an error and reads "invertible.") In other words, for each color, there are infinitely many reflectances, which are then called metamers. Worse still, two reflectances that produce exactly the same color under one illuminant can look very different under another.
@@ -45,7 +45,7 @@ The most important part of an implementation is writing code that can exactly re
=== Community Bonding Period
-To get to know the community and the codebase, I will use this time to try and get a minor pull request merged in. Implementing the missing part of Meng, et. al. is one possibility, but I will have to discuss this topic in further detail. In the meantime I will set up a blog and start posting there, as per NumFOCUS’s requirements.
+To get to know the community and the codebase, I will use this time to try and get a minor pull request merged in. Implementing the missing part of Meng et al. is one possibility, but I will have to discuss this topic in further detail. In the meantime I will set up a blog and start posting there, as per NumFOCUS’s requirements.
=== Phase 1
@@ -73,7 +73,7 @@ The time frame here is harder to estimate. The method looks considerably more co
=== Final Week
-The last few weeks will be the time to consider other spectral upsampling methods, discussing the topic with the mentors and the community. The specifics will depend on how fast I complete the goals of the first two phases and what is decided on. The hard goal would be to fully implement at least one more method. For example, Mallet and Yuksel’s is similar to Otsu, et. al.’s and I would not expect to have problems adding support for it before the final week.
+The last few weeks will be the time to consider other spectral upsampling methods, discussing the topic with the mentors and the community. The specifics will depend on how fast I complete the goals of the first two phases and what is decided on. The hard goal would be to fully implement at least one more method. For example, Mallett and Yuksel’s is similar to Otsu et al.’s and I would not expect to have problems adding support for it before the final week.
By the final week, I expect to have created several good and reviewed commits, worthy of being merged into Colour. The final week will be the time to make sure everything is ready for the final evaluation.