apca-introduction

The missing introduction to APCA  https://p.ce9e.org/apca-introduction/
git clone https://git.ce9e.org/apca-introduction.git

commit
1126a01c61885de64eae7eb1152cd0e8c5e5958b
parent
1cb50665affef87b2ef825e5aad0d2bfe6b749b6
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2022-07-16 19:59
normalize sRGBtoY before comparison

Diffstat

M analysis.md 33 +++++++++++++++++++++++++++------
M plots/sRGBtoY_comparison.png 0
M plots/sRGBtoY_comparison.py 14 ++++++++++++--

3 files changed, 39 insertions, 8 deletions


diff --git a/analysis.md b/analysis.md

@@ -185,7 +185,7 @@ function sRGBtoY_modified(srgb, exponent) {
  185   185   if (y < 0.022) {
  186   186     y += Math.pow(0.022 - y, 1.414);
  187   187   }
  188    -1   return Math.exp(Math.pow(y, exponent)) / Math.exp(1);
   -1   188   return Math.exp(Math.pow(y, exponent));
  189   189 }
  190   190 ```
  191   191 
@@ -205,14 +205,35 @@ To get a better feeling for how these formulas compare, I plotted the results
  205   205 of `sRGBtoY()`. In order to reduce colors to a single dimension, I used gray
  206   206 `[x, x, x]`, red `[x, 0, 0]`, green `[0, x, 0]` and blue `[0, 0, x]` values.
  207   207 
   -1   208 I also normalized the values so they are in the same range as WCAG 2.x. I used
   -1   209 factors (because they do not change the contrast ratio) and powers (because
   -1   210 they are monotonous on the contrast ratio).
   -1   211 
   -1   212 ```js
   -1   213 var average_exponent = 0.6;
   -1   214 var y0 = Math.exp(Math.pow(0.022, 1.414 * average_exponent));
   -1   215 var y1 = Math.exp(1);
   -1   216 
   -1   217 function normalize(y) {
   -1   218   // scale the lower end to 1
   -1   219   y /= y0;
   -1   220 
   -1   221   // scale the upper end to 21
   -1   222   // we use a power so the lower end stays at 1
   -1   223   y = Math.pow(y, Math.log(21) / Math.log(y1 / y0));
   -1   224 
   -1   225   // scale down to the desired range
   -1   226   return y / 20;
   -1   227 }
   -1   228 ```
   -1   229 
  208   230 ![sRGBtoY comparison](plots/sRGBtoY_comparison.png)
  209   231 
  210   232 The four curves for APCA are very similar. Despite the very different formula,
  211    -1 the WCAG 2.x curve also has a similar shape, although it is a lot lower. I
  212    -1 added another curve that uses the WCAG 2.x formula, but with an ambient light
  213    -1 value of 0.6 instead of 0.05. This one is very similar to the APCA curves. The
  214    -1 second column shows the differences between the APCA curves and this modified
  215    -1 WCAG 2.x.
   -1   233 the WCAG 2.x curve also has a similar shape. I added a modified WCAG 2.x curve
   -1   234 with an ambient light value of 0.6 instead of 0.05. This one is very similar
   -1   235 to the APCA curves. The second column shows the differences between the APCA
   -1   236 curves and this modified WCAG 2.x.
  216   237 
  217   238 The APCA contrast formula is certainly not as obvious a choice as the one from
  218   239 WCAG 2.x. I was not able to find much information on how it was derived. A

diff --git a/plots/sRGBtoY_comparison.png b/plots/sRGBtoY_comparison.png

Binary files differ.

diff --git a/plots/sRGBtoY_comparison.py b/plots/sRGBtoY_comparison.py

@@ -1,3 +1,5 @@
   -1     1 import math
   -1     2 
    1     3 from matplotlib import pyplot as plt
    2     4 import numpy as np
    3     5 
@@ -10,7 +12,11 @@ def wcag(x, factor, ambient):
   10    12 	y = x / 255
   11    13 	y = np.where(y < 0.04045, y / 12.92, ((y + 0.055) / 1.055) ** 2.4)
   12    14 	y *= factor
   13    -1 	return (y + ambient) / (1 + ambient)
   -1    15 	y += ambient
   -1    16 
   -1    17 	y0 = ambient
   -1    18 	y1 = 1 + ambient
   -1    19 	return (y / y0) ** math.log(21, y1 / y0) / 20
   14    20 
   15    21 
   16    22 def apca(x, factor, exp):
@@ -19,7 +25,11 @@ def apca(x, factor, exp):
   19    25 	y *= factor
   20    26 	y += np.where(y < 0.022, 0.022 - y, 0) ** 1.414
   21    27 	y **= exp
   22    -1 	return np.exp(y) / np.exp(1)
   -1    28 	y = np.exp(y)
   -1    29 
   -1    30 	y0 = math.exp((0.022 ** 1.414) ** 0.6)
   -1    31 	y1 = math.exp(1)
   -1    32 	return (y / y0) ** math.log(21, y1 / y0) / 20
   23    33 
   24    34 
   25    35 if __name__ == '__main__':