add table pregeneration script for pmetric

R=djsollen@google.com

Review URL: https://codereview.chromium.org/19256002

git-svn-id: http://skia.googlecode.com/svn/trunk@10106 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
zachr@google.com 2013-07-16 15:47:07 +00:00
parent f2e7dbb09f
commit 92fe073efb
4 changed files with 2767 additions and 12 deletions

View File

@ -101,6 +101,7 @@ void SkDiffContext::diffDirectories(const char baselinePath[], const char testPa
}
for (int baselineIndex = 0; baselineIndex < baselineEntries.count(); baselineIndex++) {
SkDebugf("[%i/%i] ", baselineIndex, baselineEntries.count());
const char* baseFilename = baselineEntries[baselineIndex].c_str();
// Find the real location of each file to compare
@ -140,6 +141,7 @@ void SkDiffContext::diffPatterns(const char baselinePattern[], const char testPa
}
for (int entryIndex = 0; entryIndex < baselineEntries.count(); entryIndex++) {
SkDebugf("[%i/%i] ", entryIndex, baselineEntries.count());
const char* baselineFilename = baselineEntries[entryIndex].c_str();
const char* testFilename = testEntries [entryIndex].c_str();

View File

@ -3,6 +3,7 @@
#include "SkBitmap.h"
#include "skpdiff_util.h"
#include "SkPMetric.h"
#include "SkPMetricUtil_generated.h"
struct RGB {
float r, g, b;
@ -112,7 +113,7 @@ void adobergb_to_cielab(float r, float g, float b, LAB* lab) {
// http://en.wikipedia.org/wiki/CIELAB#Forward_transformation
for (int i = 0; i < 3; i++) {
if (f[i] >= 0.008856f) {
f[i] = powf(f[i], 1.0f / 3.0f);
f[i] = SkPMetricUtil::get_cube_root(f[i]);
} else {
f[i] = 7.787f * f[i] + 4.0f / 29.0f;
}
@ -138,9 +139,9 @@ static void bitmap_to_cielab(const SkBitmap* bitmap, ImageLAB* outImageLAB) {
unsigned char* row = (unsigned char*)bitmap->getAddr(0, y);
for (int x = 0; x < width; x++) {
// Perform gamma correction which is assumed to be 2.2
rgb.r = powf(row[x * 4 + 2] / 255.0f, 2.2f);
rgb.g = powf(row[x * 4 + 1] / 255.0f, 2.2f);
rgb.b = powf(row[x * 4 + 0] / 255.0f, 2.2f);
rgb.r = SkPMetricUtil::get_gamma(row[x * 4 + 2]);
rgb.g = SkPMetricUtil::get_gamma(row[x * 4 + 1]);
rgb.b = SkPMetricUtil::get_gamma(row[x * 4 + 0]);
adobergb_to_cielab(rgb.r, rgb.g, rgb.b, &lab);
outImageLAB->writePixel(x, y, lab);
}
@ -158,8 +159,12 @@ static float contrast_sensitivity(float cyclesPerDegree, float luminance) {
sqrtf(1.0f + 0.06f * expf(b * cyclesPerDegree));
}
#if 0
// We're keeping these around for reference and in case the lookup tables are no longer desired.
// They are no longer called by any code in this file.
// From Daly 1993
static float visual_mask(float contrast) {
static float visual_mask(float contrast) {
float x = powf(392.498f * contrast, 0.7f);
x = powf(0.0153f * x, 4.0f);
return powf(1.0f + x, 0.25f);
@ -183,6 +188,8 @@ static float threshold_vs_intensity(float adaptationLuminance) {
return powf(10.0f, x);
}
#endif
/// Simply takes the L channel from the input and puts it into the output
static void lab_to_l(const ImageLAB* imageLAB, ImageL* outImageL) {
for (int y = 0; y < imageLAB->height; y++) {
@ -210,7 +217,6 @@ static void convolve(const ImageL* imageL, bool vertical, ImageL* outImageL) {
}
float* writeRow = outImageL->getRow(0);
for (int y = 0; y < imageL->height; y++) {
for (int x = 0; x < imageL->width; x++) {
float lSum = 0.0f;
@ -278,6 +284,16 @@ float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
cyclesPerDegree[levelIndex] = cyclesPerDegree[levelIndex - 1] * 0.5f;
}
// Contrast sensitivity is based on image dimensions. Therefore it cannot be statically
// generated.
float* contrastSensitivityTable = SkNEW_ARRAY(float, maxLevels * 1000);
for (int levelIndex = 0; levelIndex < maxLevels; levelIndex++) {
for (int csLum = 0; csLum < 1000; csLum++) {
contrastSensitivityTable[levelIndex * 1000 + csLum] =
contrast_sensitivity(cyclesPerDegree[levelIndex], (float)csLum / 10.0f + 1e-5f);
}
}
// Compute G - The convolved lum for the baseline
for (int levelIndex = 1; levelIndex < maxLevels; levelIndex++) {
convolve(baselineL.getLayer(levelIndex - 1), false, &scratchImageL);
@ -339,7 +355,6 @@ float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
if (denominator < 1e-5) {
denominator = 1e-5;
}
contrast[levelIndex] = numerator / denominator;
contrastSum += contrast[levelIndex];
}
@ -350,8 +365,10 @@ float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
float F = 0.0f;
for (int levelIndex = 0; levelIndex < maxLevels - 2; levelIndex++) {
float mask = visual_mask(contrast[levelIndex] *
contrast_sensitivity(cyclesPerDegree[levelIndex], lAdapt));
float contrastSensitivity = contrastSensitivityTable[levelIndex * 1000 +
(int)(lAdapt * 10.0)];
float mask = SkPMetricUtil::get_visual_mask(contrast[levelIndex] *
contrastSensitivity);
F += contrast[levelIndex] +
thresholdFactorFrequency[levelIndex] * mask / contrastSum;
@ -367,7 +384,7 @@ float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
bool isFailure = false;
if (fabsf(lBaseline - lTest) > F * threshold_vs_intensity(lAdapt)) {
if (fabsf(lBaseline - lTest) > F * SkPMetricUtil::get_threshold_vs_intensity(lAdapt)) {
isFailure = true;
} else {
LAB baselineColor;
@ -398,6 +415,7 @@ float pmetric(const ImageLAB* baselineLAB, const ImageLAB* testLAB, SkTDArray<Sk
SkDELETE_ARRAY(cyclesPerDegree);
SkDELETE_ARRAY(contrast);
SkDELETE_ARRAY(thresholdFactorFrequency);
SkDELETE_ARRAY(contrastSensitivityTable);
return 1.0 - (double)failures / (width * height);
}
@ -406,8 +424,8 @@ const char* SkPMetric::getName() {
}
int SkPMetric::queueDiff(SkBitmap* baseline, SkBitmap* test) {
int diffID = fQueuedDiffs.count();
double startTime = get_seconds();
int diffID = fQueuedDiffs.count();
QueuedDiff& diff = fQueuedDiffs.push_back();
diff.result = 0.0;
@ -433,7 +451,7 @@ int SkPMetric::queueDiff(SkBitmap* baseline, SkBitmap* test) {
void SkPMetric::deleteDiff(int id) {
fQueuedDiffs[id].poi.reset();
}
bool SkPMetric::isFinished(int id) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,149 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function
from math import *
COPYRIGHT = '''/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/'''
HELP = '// To regenerate SkPMetricUtil_generated.h, simply run ./generate_pmetric_tables.py'
# From Barten SPIE 1989
def contrast_sensitivity(cycles_per_degree, luminance):
a = 440.0 * pow(1.0 + 0.7 / luminance, -0.2)
b = 0.3 * pow(1 + 100.0 / luminance, 0.15)
return a * cycles_per_degree * exp(-b * cycles_per_degree) * sqrt(1.0 + 0.06 * exp(b * cycles_per_degree))
# From Ward Larson Siggraph 1997
def threshold_vs_intensity(adaptation_luminance):
log_lum = float('-inf') # Works in Python 2.6+
try:
log_lum = log10(adaptation_luminance)
except ValueError:
pass
x = 0.0
if log_lum < -3.94:
x = -2.86
elif log_lum < -1.44:
x = pow(0.405 * log_lum + 1.6, 2.18) - 2.86
elif log_lum < -0.0184:
x = log_lum - 0.395
elif log_lum < 1.9:
x = pow(0.249 * log_lum + 0.65, 2.7) - 0.72
else:
x = log_lum - 1.255
return pow(10.0, x)
# From Daly 1993
def visual_mask(contrast):
x = pow(392.498 * contrast, 0.7)
x = pow(0.0153 * x, 4.0)
return pow(1.0 + x, 0.25)
# float gCubeRootTable[]
CUBE_ROOT_ACCESS_FUNCTION = '''
float get_cube_root(float value) {
SkASSERT(value >= 0.0f);
SkASSERT(value * 1023.0f < 1024.0f);
return gCubeRootTable[(int)(value * 1023.0f)];
}
'''
def generate_cube_root_table(stream):
print('static float gCubeRootTable[] = {', end='', file=stream)
for i in range(1024):
if i % 6 == 0:
print('\n ', end='', file=stream)
print("%.10f" % pow(i / 1024.0, 1.0 / 3.0), end='f,', file=stream)
print('\n};', end='', file=stream)
print(CUBE_ROOT_ACCESS_FUNCTION, file=stream)
# float gGammaTable[]
GAMMA_ACCESS_FUNCTION = '''
float get_gamma(unsigned char value) {
return gGammaTable[value];
}
'''
def generate_gamma_table(stream):
print('static float gGammaTable[] = {', end='', file=stream)
for i in range(256):
if i % 6 == 0:
print('\n ', end='', file=stream)
print("%.10f" % pow(i / 255.0, 2.2), end='f,', file=stream)
print('\n};', end='', file=stream)
print(GAMMA_ACCESS_FUNCTION, file=stream)
# float gTVITable[]
TVI_ACCESS_FUNCTION = '''
float get_threshold_vs_intensity(float value) {
SkASSERT(value >= 0.0f);
SkASSERT(value < 100.0f);
return gTVITable[(int)(value * 100.0f)];
}
'''
def generate_tvi_table(stream):
print('static float gTVITable[] = {', end='', file=stream)
for i in range(10000):
if i % 6 == 0:
print('\n ', end='', file=stream)
print("%.10f" % threshold_vs_intensity(i / 100.0), end='f,', file=stream)
print('\n};', end='', file=stream)
print(TVI_ACCESS_FUNCTION, file=stream)
# float gVisualMaskTable[]
VISUAL_MASK_DOMAIN = 4000
VISUAL_MASK_ACCESS_FUNCTION = '''
float get_visual_mask(float value) {{
SkASSERT(value >= 0.0f);
SkASSERT(value < {}.0f);
return gVisualMaskTable[(int)value];
}}'''
def generate_visual_mask_table(stream):
print('static float gVisualMaskTable[] = {', end='', file=stream)
for i in range(VISUAL_MASK_DOMAIN):
if i % 6 == 0:
print('\n ', end='', file=stream)
print("%.10f" % visual_mask(i), end='f,', file=stream)
print('\n};', end='', file=stream)
print(VISUAL_MASK_ACCESS_FUNCTION.format(VISUAL_MASK_DOMAIN), file=stream)
def generate_lookup_tables(stream):
print(COPYRIGHT, file=stream)
print(HELP, file=stream)
print('namespace SkPMetricUtil {', file=stream)
generate_cube_root_table(stream)
generate_gamma_table(stream)
generate_tvi_table(stream)
generate_visual_mask_table(stream)
print('}', file=stream)
def main():
pmetric_util_out = open('SkPMetricUtil_generated.h', 'wb')
generate_lookup_tables(pmetric_util_out)
pmetric_util_out.close()
if __name__ == '__main__':
main()