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:
parent
f2e7dbb09f
commit
92fe073efb
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
2586
experimental/skpdiff/SkPMetricUtil_generated.h
Normal file
2586
experimental/skpdiff/SkPMetricUtil_generated.h
Normal file
File diff suppressed because it is too large
Load Diff
149
experimental/skpdiff/generate_pmetric_tables.py
Executable file
149
experimental/skpdiff/generate_pmetric_tables.py
Executable 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()
|
Loading…
Reference in New Issue
Block a user