Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(342)

Side by Side Diff: experimental/skpdiff/SkPMetric.cpp

Issue 19256002: add table pregeneration script for pmetric (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: help Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « experimental/skpdiff/SkDiffContext.cpp ('k') | experimental/skpdiff/SkPMetricUtil_generated.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #include <cmath> 1 #include <cmath>
2 2
3 #include "SkBitmap.h" 3 #include "SkBitmap.h"
4 #include "skpdiff_util.h" 4 #include "skpdiff_util.h"
5 #include "SkPMetric.h" 5 #include "SkPMetric.h"
6 #include "SkPMetricUtil_generated.h"
6 7
7 struct RGB { 8 struct RGB {
8 float r, g, b; 9 float r, g, b;
9 }; 10 };
10 11
11 struct LAB { 12 struct LAB {
12 float l, a, b; 13 float l, a, b;
13 }; 14 };
14 15
15 template<class T> 16 template<class T>
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 const float yw = 0.297361f + 0.627355f + 0.0752847f; 106 const float yw = 0.297361f + 0.627355f + 0.0752847f;
106 const float zw = 0.0270328f + 0.0706879f + 0.991248f; 107 const float zw = 0.0270328f + 0.0706879f + 0.991248f;
107 108
108 // This is the XYZ color point relative to the white point 109 // This is the XYZ color point relative to the white point
109 float f[3] = { x / xw, y / yw, z / zw }; 110 float f[3] = { x / xw, y / yw, z / zw };
110 111
111 // Conversion from XYZ to LAB taken from 112 // Conversion from XYZ to LAB taken from
112 // http://en.wikipedia.org/wiki/CIELAB#Forward_transformation 113 // http://en.wikipedia.org/wiki/CIELAB#Forward_transformation
113 for (int i = 0; i < 3; i++) { 114 for (int i = 0; i < 3; i++) {
114 if (f[i] >= 0.008856f) { 115 if (f[i] >= 0.008856f) {
115 f[i] = powf(f[i], 1.0f / 3.0f); 116 f[i] = SkPMetricUtil::get_cube_root(f[i]);
116 } else { 117 } else {
117 f[i] = 7.787f * f[i] + 4.0f / 29.0f; 118 f[i] = 7.787f * f[i] + 4.0f / 29.0f;
118 } 119 }
119 } 120 }
120 lab->l = 116.0f * f[1] - 16.0f; 121 lab->l = 116.0f * f[1] - 16.0f;
121 lab->a = 500.0f * (f[0] - f[1]); 122 lab->a = 500.0f * (f[0] - f[1]);
122 lab->b = 200.0f * (f[1] - f[2]); 123 lab->b = 200.0f * (f[1] - f[2]);
123 } 124 }
124 125
125 /// Converts a 8888 bitmap to LAB color space and puts it into the output 126 /// Converts a 8888 bitmap to LAB color space and puts it into the output
126 static void bitmap_to_cielab(const SkBitmap* bitmap, ImageLAB* outImageLAB) { 127 static void bitmap_to_cielab(const SkBitmap* bitmap, ImageLAB* outImageLAB) {
127 SkASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config); 128 SkASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config);
128 129
129 int width = bitmap->width(); 130 int width = bitmap->width();
130 int height = bitmap->height(); 131 int height = bitmap->height();
131 SkASSERT(outImageLAB->width == width); 132 SkASSERT(outImageLAB->width == width);
132 SkASSERT(outImageLAB->height == height); 133 SkASSERT(outImageLAB->height == height);
133 134
134 bitmap->lockPixels(); 135 bitmap->lockPixels();
135 RGB rgb; 136 RGB rgb;
136 LAB lab; 137 LAB lab;
137 for (int y = 0; y < height; y++) { 138 for (int y = 0; y < height; y++) {
138 unsigned char* row = (unsigned char*)bitmap->getAddr(0, y); 139 unsigned char* row = (unsigned char*)bitmap->getAddr(0, y);
139 for (int x = 0; x < width; x++) { 140 for (int x = 0; x < width; x++) {
140 // Perform gamma correction which is assumed to be 2.2 141 // Perform gamma correction which is assumed to be 2.2
141 rgb.r = powf(row[x * 4 + 2] / 255.0f, 2.2f); 142 rgb.r = SkPMetricUtil::get_gamma(row[x * 4 + 2]);
142 rgb.g = powf(row[x * 4 + 1] / 255.0f, 2.2f); 143 rgb.g = SkPMetricUtil::get_gamma(row[x * 4 + 1]);
143 rgb.b = powf(row[x * 4 + 0] / 255.0f, 2.2f); 144 rgb.b = SkPMetricUtil::get_gamma(row[x * 4 + 0]);
144 adobergb_to_cielab(rgb.r, rgb.g, rgb.b, &lab); 145 adobergb_to_cielab(rgb.r, rgb.g, rgb.b, &lab);
145 outImageLAB->writePixel(x, y, lab); 146 outImageLAB->writePixel(x, y, lab);
146 } 147 }
147 } 148 }
148 bitmap->unlockPixels(); 149 bitmap->unlockPixels();
149 } 150 }
150 151
151 // From Barten SPIE 1989 152 // From Barten SPIE 1989
152 static float contrast_sensitivity(float cyclesPerDegree, float luminance) { 153 static float contrast_sensitivity(float cyclesPerDegree, float luminance) {
153 float a = 440.0f * powf(1.0f + 0.7f / luminance, -0.2f); 154 float a = 440.0f * powf(1.0f + 0.7f / luminance, -0.2f);
154 float b = 0.3f * powf(1 + 100.0 / luminance, 0.15f); 155 float b = 0.3f * powf(1 + 100.0 / luminance, 0.15f);
155 return a * 156 return a *
156 cyclesPerDegree * 157 cyclesPerDegree *
157 expf(-b * cyclesPerDegree) * 158 expf(-b * cyclesPerDegree) *
158 sqrtf(1.0f + 0.06f * expf(b * cyclesPerDegree)); 159 sqrtf(1.0f + 0.06f * expf(b * cyclesPerDegree));
159 } 160 }
160 161
162 #if 0
163 // We're keeping these around for reference and in case the lookup tables are no longer desired.
164 // They are no longer called by any code in this file.
165
161 // From Daly 1993 166 // From Daly 1993
162 static float visual_mask(float contrast) { 167 static float visual_mask(float contrast) {
163 float x = powf(392.498f * contrast, 0.7f); 168 float x = powf(392.498f * contrast, 0.7f);
164 x = powf(0.0153f * x, 4.0f); 169 x = powf(0.0153f * x, 4.0f);
165 return powf(1.0f + x, 0.25f); 170 return powf(1.0f + x, 0.25f);
166 } 171 }
167 172
168 // From Ward Larson Siggraph 1997 173 // From Ward Larson Siggraph 1997
169 static float threshold_vs_intensity(float adaptationLuminance) { 174 static float threshold_vs_intensity(float adaptationLuminance) {
170 float logLum = log10f(adaptationLuminance); 175 float logLum = log10f(adaptationLuminance);
171 float x; 176 float x;
172 if (logLum < -3.94f) { 177 if (logLum < -3.94f) {
173 x = -2.86f; 178 x = -2.86f;
174 } else if (logLum < -1.44f) { 179 } else if (logLum < -1.44f) {
175 x = powf(0.405f * logLum + 1.6f, 2.18) - 2.86f; 180 x = powf(0.405f * logLum + 1.6f, 2.18) - 2.86f;
176 } else if (logLum < -0.0184f) { 181 } else if (logLum < -0.0184f) {
177 x = logLum - 0.395f; 182 x = logLum - 0.395f;
178 } else if (logLum < 1.9f) { 183 } else if (logLum < 1.9f) {
179 x = powf(0.249f * logLum + 0.65f, 2.7f) - 0.72f; 184 x = powf(0.249f * logLum + 0.65f, 2.7f) - 0.72f;
180 } else { 185 } else {
181 x = logLum - 1.255f; 186 x = logLum - 1.255f;
182 } 187 }
183 return powf(10.0f, x); 188 return powf(10.0f, x);
184 } 189 }
185 190
191 #endif
192
186 /// Simply takes the L channel from the input and puts it into the output 193 /// Simply takes the L channel from the input and puts it into the output
187 static void lab_to_l(const ImageLAB* imageLAB, ImageL* outImageL) { 194 static void lab_to_l(const ImageLAB* imageLAB, ImageL* outImageL) {
188 for (int y = 0; y < imageLAB->height; y++) { 195 for (int y = 0; y < imageLAB->height; y++) {
189 for (int x = 0; x < imageLAB->width; x++) { 196 for (int x = 0; x < imageLAB->width; x++) {
190 LAB lab; 197 LAB lab;
191 imageLAB->readPixel(x, y, &lab); 198 imageLAB->readPixel(x, y, &lab);
192 outImageL->writePixel(x, y, lab.l); 199 outImageL->writePixel(x, y, lab.l);
193 } 200 }
194 } 201 }
195 } 202 }
196 203
197 /// Convolves an image with the given filter in one direction and saves it to th e output image 204 /// Convolves an image with the given filter in one direction and saves it to th e output image
198 static void convolve(const ImageL* imageL, bool vertical, ImageL* outImageL) { 205 static void convolve(const ImageL* imageL, bool vertical, ImageL* outImageL) {
199 SkASSERT(imageL->width == outImageL->width); 206 SkASSERT(imageL->width == outImageL->width);
200 SkASSERT(imageL->height == outImageL->height); 207 SkASSERT(imageL->height == outImageL->height);
201 208
202 const float matrix[] = { 0.05f, 0.25f, 0.4f, 0.25f, 0.05f }; 209 const float matrix[] = { 0.05f, 0.25f, 0.4f, 0.25f, 0.05f };
203 const int matrixCount = sizeof(matrix) / sizeof(float); 210 const int matrixCount = sizeof(matrix) / sizeof(float);
204 const int radius = matrixCount / 2; 211 const int radius = matrixCount / 2;
205 212
206 // Keep track of what rows are being operated on for quick access. 213 // Keep track of what rows are being operated on for quick access.
207 float* rowPtrs[matrixCount]; // Because matrixCount is constant, this won't create a VLA 214 float* rowPtrs[matrixCount]; // Because matrixCount is constant, this won't create a VLA
208 for (int y = radius; y < matrixCount; y++) { 215 for (int y = radius; y < matrixCount; y++) {
209 rowPtrs[y] = imageL->getRow(y - radius); 216 rowPtrs[y] = imageL->getRow(y - radius);
210 } 217 }
211 float* writeRow = outImageL->getRow(0); 218 float* writeRow = outImageL->getRow(0);
212 219
213
214 for (int y = 0; y < imageL->height; y++) { 220 for (int y = 0; y < imageL->height; y++) {
215 for (int x = 0; x < imageL->width; x++) { 221 for (int x = 0; x < imageL->width; x++) {
216 float lSum = 0.0f; 222 float lSum = 0.0f;
217 for (int xx = -radius; xx <= radius; xx++) { 223 for (int xx = -radius; xx <= radius; xx++) {
218 int nx = x; 224 int nx = x;
219 int ny = y; 225 int ny = y;
220 226
221 // We mirror at edges so that edge pixels that the filter weight ing still makes 227 // We mirror at edges so that edge pixels that the filter weight ing still makes
222 // sense. 228 // sense.
223 if (vertical) { 229 if (vertical) {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 277
272 lab_to_l(baselineLAB, baselineL.getLayer(0)); 278 lab_to_l(baselineLAB, baselineL.getLayer(0));
273 lab_to_l(testLAB, testL.getLayer(0)); 279 lab_to_l(testLAB, testL.getLayer(0));
274 280
275 // Compute cpd - Cycles per degree on the pyramid 281 // Compute cpd - Cycles per degree on the pyramid
276 cyclesPerDegree[0] = 0.5f * pixelsPerDegree; 282 cyclesPerDegree[0] = 0.5f * pixelsPerDegree;
277 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { 283 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) {
278 cyclesPerDegree[levelIndex] = cyclesPerDegree[levelIndex - 1] * 0.5f; 284 cyclesPerDegree[levelIndex] = cyclesPerDegree[levelIndex - 1] * 0.5f;
279 } 285 }
280 286
287 // Contrast sensitivity is based on image dimensions. Therefore it cannot be statically
288 // generated.
289 float* contrastSensitivityTable = SkNEW_ARRAY(float, maxLevels * 1000);
290 for (int levelIndex = 0; levelIndex < maxLevels; levelIndex++) {
291 for (int csLum = 0; csLum < 1000; csLum++) {
292 contrastSensitivityTable[levelIndex * 1000 + csLum] =
293 contrast_sensitivity(cyclesPerDegree[levelIndex], (float)csLum / 10.0 f + 1e-5f);
294 }
295 }
296
281 // Compute G - The convolved lum for the baseline 297 // Compute G - The convolved lum for the baseline
282 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { 298 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) {
283 convolve(baselineL.getLayer(levelIndex - 1), false, &scratchImageL); 299 convolve(baselineL.getLayer(levelIndex - 1), false, &scratchImageL);
284 convolve(&scratchImageL, true, baselineL.getLayer(levelIndex)); 300 convolve(&scratchImageL, true, baselineL.getLayer(levelIndex));
285 } 301 }
286 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) { 302 for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) {
287 convolve(testL.getLayer(levelIndex - 1), false, &scratchImageL); 303 convolve(testL.getLayer(levelIndex - 1), false, &scratchImageL);
288 convolve(&scratchImageL, true, testL.getLayer(levelIndex)); 304 convolve(&scratchImageL, true, testL.getLayer(levelIndex));
289 } 305 }
290 306
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 348
333 float baselineContrast2 = fabsf(baselineL2); 349 float baselineContrast2 = fabsf(baselineL2);
334 float testContrast2 = fabsf(testL2); 350 float testContrast2 = fabsf(testL2);
335 float denominator = (baselineContrast2 > testContrast2) ? 351 float denominator = (baselineContrast2 > testContrast2) ?
336 baselineContrast2 : testContrast2; 352 baselineContrast2 : testContrast2;
337 353
338 // Avoid divides by close to zero 354 // Avoid divides by close to zero
339 if (denominator < 1e-5) { 355 if (denominator < 1e-5) {
340 denominator = 1e-5; 356 denominator = 1e-5;
341 } 357 }
342
343 contrast[levelIndex] = numerator / denominator; 358 contrast[levelIndex] = numerator / denominator;
344 contrastSum += contrast[levelIndex]; 359 contrastSum += contrast[levelIndex];
345 } 360 }
346 361
347 if (contrastSum < 1e-5) { 362 if (contrastSum < 1e-5) {
348 contrastSum = 1e-5; 363 contrastSum = 1e-5;
349 } 364 }
350 365
351 float F = 0.0f; 366 float F = 0.0f;
352 for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) { 367 for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) {
353 float mask = visual_mask(contrast[levelIndex] * 368 float contrastSensitivity = contrastSensitivityTable[levelIndex * 1000 +
354 contrast_sensitivity(cyclesPerDegree[levelIndex], l Adapt)); 369 (int)(lAdap t * 10.0)];
370 float mask = SkPMetricUtil::get_visual_mask(contrast[levelIndex] *
371 contrastSensitivity) ;
355 372
356 F += contrast[levelIndex] + 373 F += contrast[levelIndex] +
357 thresholdFactorFrequency[levelIndex] * mask / contrastSum; 374 thresholdFactorFrequency[levelIndex] * mask / contrastSum;
358 } 375 }
359 376
360 if (F < 1.0f) { 377 if (F < 1.0f) {
361 F = 1.0f; 378 F = 1.0f;
362 } 379 }
363 380
364 if (F > 10.0f) { 381 if (F > 10.0f) {
365 F = 10.0f; 382 F = 10.0f;
366 } 383 }
367 384
368 385
369 bool isFailure = false; 386 bool isFailure = false;
370 if (fabsf(lBaseline - lTest) > F * threshold_vs_intensity(lAdapt)) { 387 if (fabsf(lBaseline - lTest) > F * SkPMetricUtil::get_threshold_vs_i ntensity(lAdapt)) {
371 isFailure = true; 388 isFailure = true;
372 } else { 389 } else {
373 LAB baselineColor; 390 LAB baselineColor;
374 LAB testColor; 391 LAB testColor;
375 baselineLAB->readPixel(x, y, &baselineColor); 392 baselineLAB->readPixel(x, y, &baselineColor);
376 testLAB->readPixel(x, y, &testColor); 393 testLAB->readPixel(x, y, &testColor);
377 float contrastA = baselineColor.a - testColor.a; 394 float contrastA = baselineColor.a - testColor.a;
378 float contrastB = baselineColor.b - testColor.b; 395 float contrastB = baselineColor.b - testColor.b;
379 float colorScale = 1.0f; 396 float colorScale = 1.0f;
380 if (lAdapt < 10.0f) { 397 if (lAdapt < 10.0f) {
(...skipping 10 matching lines...) Expand all
391 if (isFailure) { 408 if (isFailure) {
392 failures++; 409 failures++;
393 poi->push()->set(x, y); 410 poi->push()->set(x, y);
394 } 411 }
395 } 412 }
396 } 413 }
397 414
398 SkDELETE_ARRAY(cyclesPerDegree); 415 SkDELETE_ARRAY(cyclesPerDegree);
399 SkDELETE_ARRAY(contrast); 416 SkDELETE_ARRAY(contrast);
400 SkDELETE_ARRAY(thresholdFactorFrequency); 417 SkDELETE_ARRAY(thresholdFactorFrequency);
418 SkDELETE_ARRAY(contrastSensitivityTable);
401 return 1.0 - (double)failures / (width * height); 419 return 1.0 - (double)failures / (width * height);
402 } 420 }
403 421
404 const char* SkPMetric::getName() { 422 const char* SkPMetric::getName() {
405 return "perceptual"; 423 return "perceptual";
406 } 424 }
407 425
408 int SkPMetric::queueDiff(SkBitmap* baseline, SkBitmap* test) { 426 int SkPMetric::queueDiff(SkBitmap* baseline, SkBitmap* test) {
427 double startTime = get_seconds();
409 int diffID = fQueuedDiffs.count(); 428 int diffID = fQueuedDiffs.count();
410 double startTime = get_seconds();
411 QueuedDiff& diff = fQueuedDiffs.push_back(); 429 QueuedDiff& diff = fQueuedDiffs.push_back();
412 diff.result = 0.0; 430 diff.result = 0.0;
413 431
414 // Ensure the images are comparable 432 // Ensure the images are comparable
415 if (baseline->width() != test->width() || baseline->height() != test->height () || 433 if (baseline->width() != test->width() || baseline->height() != test->height () ||
416 baseline->width() <= 0 || baseline->height() <= 0) { 434 baseline->width() <= 0 || baseline->height() <= 0) {
417 diff.finished = true; 435 diff.finished = true;
418 return diffID; 436 return diffID;
419 } 437 }
420 438
421 ImageLAB baselineLAB(baseline->width(), baseline->height()); 439 ImageLAB baselineLAB(baseline->width(), baseline->height());
422 ImageLAB testLAB(baseline->width(), baseline->height()); 440 ImageLAB testLAB(baseline->width(), baseline->height());
423 441
424 bitmap_to_cielab(baseline, &baselineLAB); 442 bitmap_to_cielab(baseline, &baselineLAB);
425 bitmap_to_cielab(test, &testLAB); 443 bitmap_to_cielab(test, &testLAB);
426 444
427 diff.result = pmetric(&baselineLAB, &testLAB, &diff.poi); 445 diff.result = pmetric(&baselineLAB, &testLAB, &diff.poi);
428 446
429 SkDebugf("Time: %f\n", (get_seconds() - startTime)); 447 SkDebugf("Time: %f\n", (get_seconds() - startTime));
430 448
431 return diffID; 449 return diffID;
432 } 450 }
433 451
434 452
435 void SkPMetric::deleteDiff(int id) { 453 void SkPMetric::deleteDiff(int id) {
436 fQueuedDiffs[id].poi.reset(); 454
437 } 455 }
438 456
439 bool SkPMetric::isFinished(int id) { 457 bool SkPMetric::isFinished(int id) {
440 return fQueuedDiffs[id].finished; 458 return fQueuedDiffs[id].finished;
441 } 459 }
442 460
443 double SkPMetric::getResult(int id) { 461 double SkPMetric::getResult(int id) {
444 return fQueuedDiffs[id].result; 462 return fQueuedDiffs[id].result;
445 } 463 }
446 464
447 int SkPMetric::getPointsOfInterestCount(int id) { 465 int SkPMetric::getPointsOfInterestCount(int id) {
448 return fQueuedDiffs[id].poi.count(); 466 return fQueuedDiffs[id].poi.count();
449 } 467 }
450 468
451 SkIPoint* SkPMetric::getPointsOfInterest(int id) { 469 SkIPoint* SkPMetric::getPointsOfInterest(int id) {
452 return fQueuedDiffs[id].poi.begin(); 470 return fQueuedDiffs[id].poi.begin();
453 } 471 }
OLDNEW
« no previous file with comments | « experimental/skpdiff/SkDiffContext.cpp ('k') | experimental/skpdiff/SkPMetricUtil_generated.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698