OLD | NEW |
1 function log(message) { | 1 window.ignoreCyan = false; |
2 document.getElementById('log').textContent += message + '\n'; | |
3 } | |
4 | 2 |
5 function testImageColors(source) { | 3 function testImageColors(source, cyanInTargetGamut) { |
| 4 window.ignoreCyan = !cyanInTargetGamut; |
| 5 |
6 var image = document.querySelector('img'); | 6 var image = document.querySelector('img'); |
7 | |
8 image.onload = function() { | 7 image.onload = function() { |
9 runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChang
ed); | 8 runAfterLayoutAndPaint(window.testRunner ? changeColorProfile : profileChang
ed); |
10 }; | 9 }; |
11 | 10 |
12 image.src = source; | 11 image.src = source; |
13 } | 12 } |
14 | 13 |
15 function changeColorProfile() { | 14 function changeColorProfile() { |
16 /* The test image contains the Munsell colors in a known color space. Convert | 15 /* The test image contains the Munsell colors in a known color space. Convert |
17 * the colors to sRGB color space and test the transformed color accuracy. | 16 * the colors to sRGB color space and test the transformed color accuracy. |
18 */ | 17 */ |
19 window.testRunner.setColorProfile('sRGB', profileChanged); | 18 window.testRunner.setColorProfile('sRGB', profileChanged); |
20 } | 19 } |
21 | 20 |
22 function profileChanged() { | 21 function profileChanged() { |
23 setTimeout(drawImageToCanvas, 0); | 22 setTimeout(drawImageToCanvas, 0); |
24 } | 23 } |
25 | 24 |
26 function drawImageToCanvas() { | 25 function drawImageToCanvas() { |
27 var image = document.querySelector('img'); | 26 var image = document.querySelector('img'); |
28 | 27 |
29 var canvas = document.querySelector('canvas'); | 28 var canvas = document.querySelector('canvas'); |
30 canvas.width = image.width; | 29 canvas.width = image.width; |
31 canvas.height = image.height; | 30 canvas.height = image.height; |
32 | 31 |
33 canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); | 32 canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height); |
34 chartColorTransform(canvas); | 33 chartColorTransform(canvas); |
35 } | 34 } |
36 | 35 |
| 36 function log(message) { |
| 37 document.getElementById('log').textContent += message + '\n'; |
| 38 } |
| 39 |
37 function getCanvasColor(canvas, i) { | 40 function getCanvasColor(canvas, i) { |
38 var x = 40 + (i % 6) * (canvas.width / 6); | 41 var x = 40 + (i % 6) * (canvas.width / 6); |
39 var y = 40 + Math.floor(i / 6) * (canvas.height / 4); | 42 var y = 40 + Math.floor(i / 6) * (canvas.height / 4 - 40); |
40 try { | 43 try { |
41 var data = canvas.getContext('2d').getImageData(x, y, 1, 1).data; | 44 var data = canvas.getContext('2d').getImageData(x, y, 1, 1).data; |
42 if (data[3] == 255) | 45 if (data[3] == 255) |
43 return { rgb: [data[0], data[1], data[2]] }; | 46 return { rgb: [data[0], data[1], data[2]] }; |
44 return { rgb: [0, 0, 0] }; | 47 return { rgb: [0, 0, 0] }; |
45 } catch (error) { | 48 } catch (error) { |
46 console.error(error); | 49 console.error(error); |
47 return { rgb: [255, 255, 255] }; | 50 return { rgb: [255, 255, 255] }; |
48 } | 51 } |
49 } | 52 } |
(...skipping 26 matching lines...) Expand all Loading... |
76 { color: 'Neutral 3.5', rgb: [ 83, 83, 83 ] }, | 79 { color: 'Neutral 3.5', rgb: [ 83, 83, 83 ] }, |
77 { color: 'Black', rgb: [ 50, 49, 50 ] } | 80 { color: 'Black', rgb: [ 50, 49, 50 ] } |
78 ); | 81 ); |
79 } | 82 } |
80 | 83 |
81 if (i < 0 && i >= munsell_srgb_colors.length) | 84 if (i < 0 && i >= munsell_srgb_colors.length) |
82 return { color: 'invalid-color', rgb: [ 0, 0, 0 ] }; | 85 return { color: 'invalid-color', rgb: [ 0, 0, 0 ] }; |
83 return munsell_srgb_colors[i]; | 86 return munsell_srgb_colors[i]; |
84 } | 87 } |
85 | 88 |
| 89 function getStudioColor(i) { |
| 90 if (!window.studio_srgb_colors) { |
| 91 // Studio video range white & black and proportions thereof, in sRGB space. |
| 92 window.studio_srgb_colors = new Array( |
| 93 { color: 'SG White', rgb: [ 237, 237, 237 ] }, |
| 94 { color: 'SG White 1/2', rgb: [ 130, 130, 130 ] }, |
| 95 { color: 'SG White 1/4', rgb: [ 74, 74, 74 ] }, |
| 96 { color: 'SG Black 4x', rgb: [ 79, 79, 79 ] }, |
| 97 { color: 'SG Black 2x', rgb: [ 48, 48, 48 ] }, |
| 98 { color: 'SG Black', rgb: [ 31, 31, 31 ] } |
| 99 ); |
| 100 } |
| 101 |
| 102 if (i < 0 && i >= studio_srgb_colors.length) |
| 103 return { color: 'invalid-color', rgb: [ 0, 0, 0 ] }; |
| 104 return studio_srgb_colors[i]; |
| 105 } |
| 106 |
86 function getColorError(cx, cy) { | 107 function getColorError(cx, cy) { |
87 var dr = (cx[0] - cy[0]); | 108 var dr = (cx[0] - cy[0]); |
88 var dg = (cx[1] - cy[1]); | 109 var dg = (cx[1] - cy[1]); |
89 var db = (cx[2] - cy[2]); | 110 var db = (cx[2] - cy[2]); |
90 return Math.round(Math.sqrt((dr * dr) + (dg * dg) + (db * db))); | 111 return Math.round(Math.sqrt((dr * dr) + (dg * dg) + (db * db))); |
91 } | 112 } |
92 | 113 |
93 function pad(string, size) { | 114 function pad(string, size) { |
94 size = size || 14; | 115 size = size || 14; |
95 if (string.length < size) | 116 if (string.length < size) |
96 string += ' '.repeat(size - string.length); | 117 string += ' '.repeat(size - string.length); |
97 return string; | 118 return string; |
98 } | 119 } |
99 | 120 |
100 function drawRule(size) { | 121 function drawRule(size) { |
101 log('-'.repeat(size || 44)); | 122 log('-'.repeat(size || 44)); |
102 } | 123 } |
103 | 124 |
104 function chartColorTransform(canvas) { | 125 function chartColorTransform(canvas) { |
105 /* | 126 /* |
106 * Add header over table of color names, acutal and expected values, and the | 127 * Add header over table of color names, actual and expected values, and the |
107 * per color error (Euclidean distance). | 128 * per color error (Euclidean distance). |
108 */ | 129 */ |
109 log(pad('Color') + pad('Actual') + pad('Expected') + 'dE'); | 130 log(pad('Color') + pad('Actual') + pad('Expected') + 'dE'); |
110 drawRule(); | 131 drawRule(); |
111 | 132 |
112 var totalSquaredError = 0.0; | 133 var totalSquaredError = 0.0; |
113 | 134 |
114 /* | 135 /* |
115 * Report per color error dE, by comparing with the expected Munsell colors. | 136 * Report per color error dE, by comparing with the expected Munsell & Studio
colors. |
116 */ | 137 */ |
117 for (var i = 0; i < 24;) { | 138 for (var i = 0; i < 30;) { |
118 var expected = getMunsellColor(i); | 139 var expected = (i < 24) ? getMunsellColor(i) : getStudioColor(i - 24); |
119 var actual = getCanvasColor(canvas, i); | 140 var actual = getCanvasColor(canvas, i); |
120 var dE = getColorError(actual.rgb, expected.rgb); | 141 var dE = getColorError(actual.rgb, expected.rgb); |
121 | 142 |
122 log(pad(expected.color) + pad(actual.rgb.join(',')) + pad(expected.rgb.join(
',')) + dE); | 143 log(pad(expected.color) + pad(actual.rgb.join(',')) + pad(expected.rgb.join(
',')) + dE); |
123 totalSquaredError += dE * dE; | |
124 | 144 |
125 if (++i % 6 == 0 && i < 24) | 145 if (ignoreCyan && (i + 1) == 18) |
| 146 ; // Do not include the Munsell Cyan (out-of-srgb-gamut) color error. |
| 147 else |
| 148 totalSquaredError += dE * dE; |
| 149 |
| 150 if (++i % 6 == 0 && i <= 24) |
126 drawRule(); | 151 drawRule(); |
127 } | 152 } |
128 | 153 |
129 /* | 154 /* |
130 * Report the total RMS color error neglecting out-of-srgb-gamut color Cyan. | 155 * Report the total RMS color error: lower is better, and should be << 2, whic
h is the |
| 156 * JND (Just Noticable Difference) perception threshold. Above a JND, the col
or error |
| 157 * is noticable to the human eye. |
131 */ | 158 */ |
132 drawRule(); | 159 drawRule(); |
133 log('\nResult: total RMS color error: ' + Math.sqrt(totalSquaredError / 24.0).
toFixed(2)); | 160 log('\nResult: total RMS color error: ' + Math.sqrt(totalSquaredError / 30.0).
toFixed(2)); |
134 | 161 |
135 if (window.testRunner) | 162 if (window.testRunner) |
136 window.testRunner.notifyDone(); | 163 window.testRunner.notifyDone(); |
137 } | 164 } |
OLD | NEW |