- commit
- 27cb34cdedde170670622389adee9d1a52dce9da
- parent
- 0df253af9185d8cd99f1f537614c106c83503c25
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2022-08-19 15:46
use J for lightness L is usually used for absolute luminance
Diffstat
| M | analysis.md | 34 | +++++++++++++++++----------------- |
| M | plots/contrast_comparison.png | 0 | |
| M | plots/contrast_comparison.py | 20 | ++++++++++---------- |
| M | plots/coverage.py | 6 | +++--- |
| M | plots/lightness_comparison.png | 0 | |
| M | plots/lightness_comparison.py | 2 | +- |
6 files changed, 31 insertions, 31 deletions
diff --git a/analysis.md b/analysis.md
@@ -53,15 +53,15 @@ correct 100% of the time. 53 53 ### A naive approach 54 54 55 55 ```js56 -1 function sRGBtoL(srgb) {-1 56 function sRGBtoJ(srgb) { 57 57 return (srgb[0] + srgb[1] + srgb[2]) / 3; 58 58 } 59 59 60 60 function contrast(fg, bg) {61 -1 var lfg = sRGBtoL(fg);62 -1 var lbg = sRGBtoL(bg);-1 61 var jfg = sRGBtoJ(fg); -1 62 var jbg = sRGBtoJ(bg); 63 6364 -1 return lbg - lfg;-1 64 return jbg - jfg; 65 65 }; 66 66 ``` 67 67 @@ -73,7 +73,7 @@ values. 73 73 74 74 ### Historical context 75 7576 -1 Lightness (L) is a measure for the perceived amount of light. Luminance (Y) is-1 76 Lightness (J) is a measure for the perceived amount of light. Luminance (Y) is 77 77 a measure for the physical amount of light. In order to understand perceived 78 78 contrast, we first have to understand the relationship between luminance and 79 79 lightness. @@ -86,11 +86,11 @@ Y2` has the same value. This is known as Weber contrast and has been called the 86 86 ["gold standard" for text contrast]. 87 87 88 88 Fechner concluded that the relation between a physical measure `Y` and a89 -1 perceived measure `L` can be expressed as `L = a * log(Y) + b`. This is called-1 89 perceived measure `J` can be expressed as `J = a * log(Y) + b`. This is called 90 90 the Weber-Fechner law. 91 91 92 92 In 1961 Stevens published a different model that was found to more accurately93 -1 predict human vision. It has the form `L = a * pow(Y, alpha) + b`. The exponent-1 93 predict human vision. It has the form `J = a * pow(Y, alpha) + b`. The exponent 94 94 `alpha` has a value of approximately 1/3.[^1] 95 95 96 96 ### WCAG 2.x @@ -236,7 +236,7 @@ function sRGBtoY(srgb) { 236 236 return 0.2126 * r + 0.7152 * g + 0.0722 * b; 237 237 } 238 238239 -1 function YtoL(y) {-1 239 function YtoJ(y) { 240 240 return (Math.log(y + 0.05) - Math.log(0.05)) / Math.log(21); 241 241 } 242 242 @@ -244,10 +244,10 @@ function contrast(fg, bg) { 244 244 var yfg = sRGBtoY(fg); 245 245 var ybg = sRGBtoY(bg); 246 246247 -1 var lfg = YtoL(yfg);248 -1 var lbg = YtoL(ybg);-1 247 var jfg = YtoJ(yfg); -1 248 var jbg = YtoJ(ybg); 249 249250 -1 return lbg - lfg;-1 250 return jbg - jfg; 251 251 }; 252 252 253 253 function normalize(c) { @@ -270,7 +270,7 @@ function sRGBtoY(srgb) { 270 270 return y; 271 271 } 272 272273 -1 function YtoL(y) {-1 273 function YtoJ(y) { 274 274 return Math.pow(y, 0.6); 275 275 } 276 276 @@ -278,13 +278,13 @@ function contrast(fg, bg) { 278 278 var yfg = sRGBtoY(fg); 279 279 var ybg = sRGBtoY(bg); 280 280281 -1 var lfg = YtoL(yfg);282 -1 var lbg = YtoL(ybg);-1 281 var jfg = YtoJ(yfg); -1 282 var jbg = YtoJ(ybg); 283 283 284 284 if (ybg > yfg) {285 -1 return Math.pow(lbg, 0.56 / 0.6) - Math.pow(lfg, 0.57 / 0.6);-1 285 return Math.pow(jbg, 0.56 / 0.6) - Math.pow(jfg, 0.57 / 0.6); 286 286 } else {287 -1 return Math.pow(ybg, 0.65 / 0.6) - Math.pow(lfg, 0.62 / 0.6);-1 287 return Math.pow(jbg, 0.65 / 0.6) - Math.pow(jfg, 0.62 / 0.6); 288 288 } 289 289 }; 290 290 @@ -313,7 +313,7 @@ The difference goes up to 20%. 313 313 314 314 The other three plots compare APCA to a modified version of APCA where one of 315 315 the steps has been replaced by the corresponding step from WCAG 2.x. This way316 -1 we can see that `sRGBtoY` contributes 4% to the difference, `YtoL` contributes-1 316 we can see that `sRGBtoY` contributes 4% to the difference, `YtoJ` contributes 317 317 15%, and `contrast` contributes 3%. 318 318 319 319 Since the conversion from luminance to lightness causes the biggest difference,
diff --git a/plots/contrast_comparison.png b/plots/contrast_comparison.png
Binary files differ.diff --git a/plots/contrast_comparison.py b/plots/contrast_comparison.py
@@ -12,8 +12,8 @@ def wcag_l(y, flare=0.05): 12 12 return np.log(y / flare + 1) / np.log(1 / flare + 1) 13 13 14 1415 -1 def wcag_contrast(lfg, lbg):16 -1 return lbg - lfg-1 15 def wcag_contrast(jfg, jbg): -1 16 return jbg - jfg 17 17 18 18 19 19 def apca_y(color): @@ -28,10 +28,10 @@ def apca_l(y): 28 28 return y ** 0.6 29 29 30 3031 -1 def apca_contrast(lfg, lbg):32 -1 _lfg = lfg ** (np.where(lbg > lfg, 0.57, 0.62) / 0.6)33 -1 _lbg = lbg ** (np.where(lbg > lfg, 0.56, 0.65) / 0.6)34 -1 return _lbg - _lfg-1 31 def apca_contrast(jfg, jbg): -1 32 _jfg = jfg ** (np.where(jbg > jfg, 0.57, 0.62) / 0.6) -1 33 _jbg = jbg ** (np.where(jbg > jfg, 0.56, 0.65) / 0.6) -1 34 return _jbg - _jfg 35 35 36 36 37 37 if __name__ == '__main__': @@ -48,13 +48,13 @@ if __name__ == '__main__': 48 48 49 49 apca_yfg = apca_y(fg) 50 50 apca_ybg = apca_y(bg)51 -1 apca_lfg = apca_l(apca_yfg)52 -1 apca_lbg = apca_l(apca_ybg)53 -1 apca = apca_contrast(apca_lfg, apca_lbg)-1 51 apca_jfg = apca_l(apca_yfg) -1 52 apca_jbg = apca_l(apca_ybg) -1 53 apca = apca_contrast(apca_jfg, apca_jbg) 54 54 55 55 for a, b, y, l, contrast, title in [ 56 56 (0, 0, wcag_y, apca_l, apca_contrast, 'sRGBtoY'),57 -1 (0, 1, apca_y, wcag_l, apca_contrast, 'YtoL'),-1 57 (0, 1, apca_y, wcag_l, apca_contrast, 'YtoJ'), 58 58 (1, 0, apca_y, apca_l, wcag_contrast, 'contrast'), 59 59 (1, 1, wcag_y, wcag_l, wcag_contrast, 'all'), 60 60 ]:
diff --git a/plots/coverage.py b/plots/coverage.py
@@ -32,9 +32,9 @@ def apca_y(color): 32 32 33 33 34 34 def apca_contrast(yfg, ybg):35 -1 lfg = yfg ** np.where(ybg > yfg, 0.57, 0.62)36 -1 lbg = ybg ** np.where(ybg > yfg, 0.56, 0.65)37 -1 c = (lbg - lfg) * 1.14-1 35 jfg = yfg ** np.where(ybg > yfg, 0.57, 0.62) -1 36 jbg = ybg ** np.where(ybg > yfg, 0.56, 0.65) -1 37 c = (jbg - jfg) * 1.14 38 38 c = np.where(np.abs(c) < 0.1, 0, np.where(c > 0, c - 0.027, c + 0.027)) 39 39 return np.abs(c) * 100 40 40
diff --git a/plots/lightness_comparison.png b/plots/lightness_comparison.png
Binary files differ.diff --git a/plots/lightness_comparison.py b/plots/lightness_comparison.py
@@ -8,7 +8,7 @@ def norm(x, f): 8 8 9 9 if __name__ == '__main__': 10 10 plt.xlabel('luminance of screen (Y)')11 -1 plt.ylabel('predicted perceived lightness (L)')-1 11 plt.ylabel('predicted perceived lightness (J)') 12 12 13 13 x = np.linspace(0, 1) ** 2 14 14 legend = []