UNPKG

perceptualdiff

Version:

A port of the perceptualdiff image comparison (pdiff.sourceforge.net), and adding some additional features

143 lines (123 loc) 3.85 kB
/* Metric Copyright (C) 2006-2011 Yangli Hector Yee Copyright (C) 2011-2014 Steven Myint Copyright (C) 2014 Marcel Erz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ function log10(x) { return Math.log(x) / Math.LN10; } /* * Given the adaptation luminance, this function returns the * threshold of visibility in cd per m^2 * TVI means Threshold vs Intensity function * This version comes from Ward Larson Siggraph 1997 */ function tvi(adaptation_luminance) { // returns the threshold luminance given the adaptation luminance // units are candelas per meter squared var log_a = log10(adaptation_luminance); var r; if (log_a < -3.94) { r = -2.86; } else if (log_a < -1.44) { r = Math.pow(0.405 * log_a + 1.6, 2.18) - 2.86; } else if (log_a < -0.0184) { r = log_a - 0.395; } else if (log_a < 1.9) { r = Math.pow(0.249 * log_a + 0.65, 2.7) - 0.72; } else { r = log_a - 1.255; } return Math.pow(10.0, r); } // computes the contrast sensitivity function (Barten SPIE 1989) // given the cycles per degree (cpd) and luminance (lum) function csf(cpd, lum) { var a = 440.0 * Math.pow((1.0 + 0.7 / lum), -0.2); var b = 0.3 * Math.pow((1.0 + 100.0 / lum), 0.15); return csfCache = a * cpd * Math.exp(-b * cpd) * Math.sqrt(1.0 + 0.06 * Math.exp(b * cpd)); } /* * Visual Masking Function from Daly 1993 */ function mask(contrast) { var a = Math.pow(392.498 * contrast, 0.7); var b = Math.pow(0.0153 * a, 4.0); return Math.pow(1.0 + b, 0.25); } // convert Adobe RGB (1998) with reference white D65 to XYZ function adobe_rgb_to_xyz(r, g, b) { // matrix is from http://www.brucelindbloom.com/ var x = r * 0.576700 + g * 0.185556 + b * 0.188212; var y = r * 0.297361 + g * 0.627355 + b * 0.0752847; var z = r * 0.0270328 + g * 0.0706879 + b * 0.991248; return { x: x, y: y, z: z }; } var global_white = adobe_rgb_to_xyz(1, 1, 1); var epsilon = 216 / 24389; var kappa = 24389 / 27; // convert Adobe RGB (1998) with reference white D65 to XYZ function adobe_rgb_to_lab(cr, cg, cb) { var p = adobe_rgb_to_xyz(cr, cg, cb); var x = p.x, y = p.y, z = p.z; var f = []; var r = []; r[0] = x / global_white.x; r[1] = y / global_white.y; r[2] = z / global_white.z; for (var i = 0; i < 3; i++) { if (r[i] > epsilon) { f[i] = Math.pow(r[i], 1 / 3); } else { f[i] = (kappa * r[i] + 16) / 116; } } var A = 500 * (f[0] - f[1]); var B = 200 * (f[1] - f[2]); return { A: A, B: B, y: y }; } function adaptation(levels, num_one_degree_pixels) { var num_pixels = 1.0; var adaptation_level = 0; for (var i = 0; i < levels; i++) { adaptation_level = i; if (num_pixels > num_one_degree_pixels) { break; } num_pixels *= 2; } // LCOV_EXCL_LINE return adaptation_level; } module.exports = { tvi: tvi, csf: csf, mask: mask, adobe_rgb_to_xyz: adobe_rgb_to_xyz, adobe_rgb_to_lab: adobe_rgb_to_lab, adaptation: adaptation };