tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tcuFuzzyImageCompare.js (14104B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('framework.common.tcuFuzzyImageCompare');
     23 goog.require('framework.common.tcuTexture');
     24 goog.require('framework.common.tcuTextureUtil');
     25 goog.require('framework.delibs.debase.deMath');
     26 goog.require('framework.delibs.debase.deRandom');
     27 
     28 goog.scope(function() {
     29 
     30 var tcuFuzzyImageCompare = framework.common.tcuFuzzyImageCompare;
     31 var deMath = framework.delibs.debase.deMath;
     32 var deRandom = framework.delibs.debase.deRandom;
     33 var tcuTexture = framework.common.tcuTexture;
     34 var tcuTextureUtil = framework.common.tcuTextureUtil;
     35 
     36    var DE_ASSERT = function(x) {
     37        if (!x)
     38            throw new Error('Assert failed');
     39    };
     40 
     41    /**
     42     * tcuFuzzyImageCompare.FuzzyCompareParams struct
     43     * @constructor
     44     * @param {number=} maxSampleSkip_
     45     * @param {number=} minErrThreshold_
     46     * @param {number=} errExp_
     47     */
     48    tcuFuzzyImageCompare.FuzzyCompareParams = function(maxSampleSkip_, minErrThreshold_, errExp_) {
     49        /** @type {number} */ this.maxSampleSkip = maxSampleSkip_ === undefined ? 8 : maxSampleSkip_;
     50        /** @type {number} */ this.minErrThreshold = minErrThreshold_ === undefined ? 4 : minErrThreshold_;
     51        /** @type {number} */ this.errExp = errExp_ === undefined ? 4.0 : errExp_;
     52    };
     53 
     54    /**
     55     * @param {Array<number>} v
     56     * @return {Array<number>}
     57     */
     58    tcuFuzzyImageCompare.roundArray4ToUint8Sat = function(v) {
     59        return [
     60            deMath.clamp(Math.trunc(v[0] + 0.5), 0, 255),
     61            deMath.clamp(Math.trunc(v[1] + 0.5), 0, 255),
     62            deMath.clamp(Math.trunc(v[2] + 0.5), 0, 255),
     63            deMath.clamp(Math.trunc(v[3] + 0.5), 0, 255)
     64        ];
     65    };
     66 
     67    /**
     68     * @param {Array<number>} pa
     69     * @param {Array<number>} pb
     70     * @param {number} minErrThreshold
     71     * @return {number}
     72     */
     73    tcuFuzzyImageCompare.compareColors = function(pa, pb, minErrThreshold) {
     74        /** @type {number}*/ var r = Math.max(Math.abs(pa[0] - pb[0]) - minErrThreshold, 0);
     75        /** @type {number}*/ var g = Math.max(Math.abs(pa[1] - pb[1]) - minErrThreshold, 0);
     76        /** @type {number}*/ var b = Math.max(Math.abs(pa[2] - pb[2]) - minErrThreshold, 0);
     77        /** @type {number}*/ var a = Math.max(Math.abs(pa[3] - pb[3]) - minErrThreshold, 0);
     78 
     79        /** @type {number}*/ var scale = 1.0 / (255 - minErrThreshold);
     80        /** @type {number}*/ var sqSum = (r * r + g * g + b * b + a * a) * (scale * scale);
     81 
     82        return Math.sqrt(sqSum);
     83    };
     84 
     85    /**
     86     * @param {tcuTexture.RGBA8View} src
     87     * @param {number} u
     88     * @param {number} v
     89     * @param {number} NumChannels
     90     * @return {Array<number>}
     91     */
     92    tcuFuzzyImageCompare.bilinearSample = function(src, u, v, NumChannels) {
     93        /** @type {number}*/ var w = src.width;
     94        /** @type {number}*/ var h = src.height;
     95 
     96        /** @type {number}*/ var x0 = Math.floor(u - 0.5);
     97        /** @type {number}*/ var x1 = x0 + 1;
     98        /** @type {number}*/ var y0 = Math.floor(v - 0.5);
     99        /** @type {number}*/ var y1 = y0 + 1;
    100 
    101        /** @type {number}*/ var i0 = deMath.clamp(x0, 0, w - 1);
    102        /** @type {number}*/ var i1 = deMath.clamp(x1, 0, w - 1);
    103        /** @type {number}*/ var j0 = deMath.clamp(y0, 0, h - 1);
    104        /** @type {number}*/ var j1 = deMath.clamp(y1, 0, h - 1);
    105 
    106        /** @type {number}*/ var a = (u - 0.5) - Math.floor(u - 0.5);
    107        /** @type {number}*/ var b = (v - 0.5) - Math.floor(v - 0.5);
    108 
    109        /** @type {Array<number>} */ var p00 = src.read(i0, j0, NumChannels);
    110        /** @type {Array<number>} */ var p10 = src.read(i1, j0, NumChannels);
    111        /** @type {Array<number>} */ var p01 = src.read(i0, j1, NumChannels);
    112        /** @type {Array<number>} */ var p11 = src.read(i1, j1, NumChannels);
    113        /** @type {number} */ var dst = 0;
    114 
    115        // Interpolate.
    116        /** @type {Array<number>}*/ var f = [];
    117        for (var c = 0; c < NumChannels; c++) {
    118                f[c] = p00[c] * (1.0 - a) * (1.0 - b) +
    119                (p10[c] * a * (1.0 - b)) +
    120                (p01[c] * (1.0 - a) * b) +
    121                (p11[c] * a * b);
    122        }
    123 
    124        return tcuFuzzyImageCompare.roundArray4ToUint8Sat(f);
    125    };
    126 
    127    /**
    128     * @param {tcuTexture.RGBA8View} dst
    129     * @param {tcuTexture.RGBA8View} src
    130     * @param {number} shiftX
    131     * @param {number} shiftY
    132     * @param {Array<number>} kernelX
    133     * @param {Array<number>} kernelY
    134     * @param {number} DstChannels
    135     * @param {number} SrcChannels
    136     */
    137    tcuFuzzyImageCompare.separableConvolve = function(dst, src, shiftX, shiftY, kernelX, kernelY, DstChannels, SrcChannels) {
    138        DE_ASSERT(dst.width == src.width && dst.height == src.height);
    139 
    140        /** @type {tcuTexture.TextureLevel} */ var tmp = new tcuTexture.TextureLevel(dst.getFormat(), dst.height, dst.width);
    141        var tmpView = new tcuTexture.RGBA8View(tmp.getAccess());
    142 
    143        /** @type {number} */ var kw = kernelX.length;
    144        /** @type {number} */ var kh = kernelY.length;
    145 
    146        /** @type {Array<number>} */ var sum = [];
    147        /** @type {number} */ var f;
    148        /** @type {Array<number>} */ var p;
    149 
    150        // Horizontal pass
    151        // \note Temporary surface is written in column-wise order
    152        for (var j = 0; j < src.height; j++) {
    153            for (var i = 0; i < src.width; i++) {
    154                sum[0] = sum[1] = sum[2] = sum[3] = 0;
    155                for (var kx = 0; kx < kw; kx++) {
    156                    f = kernelX[kw - kx - 1];
    157                    p = src.read(deMath.clamp(i + kx - shiftX, 0, src.width - 1), j, SrcChannels);
    158                    sum = deMath.add(sum, deMath.scale(p, f));
    159                }
    160 
    161                sum = tcuFuzzyImageCompare.roundArray4ToUint8Sat(sum);
    162                tmpView.write(j, i, sum, DstChannels);
    163            }
    164        }
    165 
    166        // Vertical pass
    167        for (var j = 0; j < src.height; j++) {
    168            for (var i = 0; i < src.width; i++) {
    169                sum[0] = sum[1] = sum[2] = sum[3] = 0;
    170                for (var ky = 0; ky < kh; ky++) {
    171                    f = kernelY[kh - ky - 1];
    172                    p = tmpView.read(deMath.clamp(j + ky - shiftY, 0, tmpView.width - 1), i, DstChannels);
    173                    sum = deMath.add(sum, deMath.scale(p, f));
    174                }
    175 
    176                sum = tcuFuzzyImageCompare.roundArray4ToUint8Sat(sum);
    177                dst.write(i, j, sum, DstChannels);
    178            }
    179        }
    180    };
    181 
    182    /**
    183     * @param {tcuFuzzyImageCompare.FuzzyCompareParams} params
    184     * @param {deRandom.Random} rnd
    185     * @param {Array<number>} pixel
    186     * @param {tcuTexture.RGBA8View} surface
    187     * @param {number} x
    188     * @param {number} y
    189     * @param {number} NumChannels
    190     * @return {number}
    191     */
    192    tcuFuzzyImageCompare.compareToNeighbor = function(params, rnd, pixel, surface, x, y, NumChannels) {
    193        /** @type {number} */ var minErr = 100;
    194 
    195        // (x, y) + (0, 0)
    196        minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, surface.read(x, y, NumChannels), params.minErrThreshold));
    197        if (minErr == 0.0)
    198            return minErr;
    199 
    200        // Area around (x, y)
    201        /** @type {Array<Array.<number>>} */ var s_coords =
    202        [
    203            [-1, -1],
    204            [0, -1],
    205            [1, -1],
    206            [-1, 0],
    207            [1, 0],
    208            [-1, 1],
    209            [0, 1],
    210            [1, 1]
    211        ];
    212 
    213        /** @type {number} */ var dx;
    214        /** @type {number} */ var dy;
    215 
    216        for (var d = 0; d < s_coords.length; d++) {
    217            dx = x + s_coords[d][0];
    218            dy = y + s_coords[d][1];
    219 
    220            if (!deMath.deInBounds32(dx, 0, surface.width) || !deMath.deInBounds32(dy, 0, surface.height))
    221                continue;
    222 
    223            minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, surface.read(dx, dy, NumChannels), params.minErrThreshold));
    224            if (minErr == 0.0)
    225                return minErr;
    226        }
    227 
    228        // Random bilinear-interpolated samples around (x, y)
    229        for (var s = 0; s < 32; s++) {
    230            dx = x + rnd.getFloat() * 2.0 - 0.5;
    231            dy = y + rnd.getFloat() * 2.0 - 0.5;
    232 
    233            /** @type {Array<number>} */ var sample = tcuFuzzyImageCompare.bilinearSample(surface, dx, dy, NumChannels);
    234 
    235            minErr = Math.min(minErr, tcuFuzzyImageCompare.compareColors(pixel, sample, params.minErrThreshold));
    236            if (minErr == 0.0)
    237                return minErr;
    238        }
    239 
    240        return minErr;
    241    };
    242 
    243    /**
    244     * @param {Array<number>} c
    245     * @return {number}
    246     */
    247    tcuFuzzyImageCompare.toGrayscale = function(c) {
    248        return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
    249    };
    250 
    251    /**
    252     * @param {tcuTexture.TextureFormat} format
    253     * @return {boolean}
    254     */
    255    tcuFuzzyImageCompare.isFormatSupported = function(format) {
    256        return format.type == tcuTexture.ChannelType.UNORM_INT8 && (format.order == tcuTexture.ChannelOrder.RGB || format.order == tcuTexture.ChannelOrder.RGBA);
    257    };
    258 
    259    /**
    260     * @param {tcuFuzzyImageCompare.FuzzyCompareParams} params
    261     * @param {tcuTexture.ConstPixelBufferAccess} ref
    262     * @param {tcuTexture.ConstPixelBufferAccess} cmp
    263     * @param {tcuTexture.PixelBufferAccess} errorMask
    264     * @return {number}
    265     */
    266    tcuFuzzyImageCompare.fuzzyCompare = function(params, ref, cmp, errorMask) {
    267        assertMsgOptions(ref.getWidth() == cmp.getWidth() && ref.getHeight() == cmp.getHeight(),
    268            'Reference and result images have different dimensions', false, true);
    269 
    270        assertMsgOptions(ref.getWidth() == errorMask.getWidth() && ref.getHeight() == errorMask.getHeight(),
    271            'Reference and error mask images have different dimensions', false, true);
    272 
    273        if (!tcuFuzzyImageCompare.isFormatSupported(ref.getFormat()) || !tcuFuzzyImageCompare.isFormatSupported(cmp.getFormat()))
    274            throw new Error('Unsupported format in fuzzy comparison');
    275 
    276        /** @type {number} */ var width = ref.getWidth();
    277        /** @type {number} */ var height = ref.getHeight();
    278        /** @type {deRandom.Random} */ var rnd = new deRandom.Random(667);
    279 
    280        // Filtered
    281        /** @type {tcuTexture.TextureLevel} */ var refFiltered = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    282        /** @type {tcuTexture.TextureLevel} */ var cmpFiltered = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    283 
    284        var refView = new tcuTexture.RGBA8View(ref);
    285        var cmpView = new tcuTexture.RGBA8View(cmp);
    286        var refFilteredView = new tcuTexture.RGBA8View(tcuTexture.PixelBufferAccess.newFromTextureLevel(refFiltered));
    287        var cmpFilteredView = new tcuTexture.RGBA8View(tcuTexture.PixelBufferAccess.newFromTextureLevel(cmpFiltered));
    288 
    289        // Kernel = {0.15, 0.7, 0.15}
    290        /** @type {Array<number>} */ var kernel = [0.1, 0.8, 0.1];
    291        /** @type {number} */ var shift = Math.floor((kernel.length - 1) / 2);
    292 
    293        switch (ref.getFormat().order) {
    294            case tcuTexture.ChannelOrder.RGBA: tcuFuzzyImageCompare.separableConvolve(refFilteredView, refView, shift, shift, kernel, kernel, 4, 4); break;
    295            case tcuTexture.ChannelOrder.RGB: tcuFuzzyImageCompare.separableConvolve(refFilteredView, refView, shift, shift, kernel, kernel, 4, 3); break;
    296            default:
    297                throw new Error('tcuFuzzyImageCompare.fuzzyCompare - Invalid ChannelOrder');
    298        }
    299 
    300        switch (cmp.getFormat().order) {
    301            case tcuTexture.ChannelOrder.RGBA: tcuFuzzyImageCompare.separableConvolve(cmpFilteredView, cmpView, shift, shift, kernel, kernel, 4, 4); break;
    302            case tcuTexture.ChannelOrder.RGB: tcuFuzzyImageCompare.separableConvolve(cmpFilteredView, cmpView, shift, shift, kernel, kernel, 4, 3); break;
    303            default:
    304                throw new Error('tcuFuzzyImageCompare.fuzzyCompare - Invalid ChannelOrder');
    305        }
    306 
    307        /** @type {number} */ var numSamples = 0;
    308        /** @type {number} */ var errSum = 0.0;
    309 
    310        // Clear error mask to green.
    311        errorMask.clear([0.0, 1.0, 0.0, 1.0]);
    312 
    313        for (var y = 1; y < height - 1; y++) {
    314            for (var x = 1; x < width - 1; x += params.maxSampleSkip > 0 ? rnd.getInt(0, params.maxSampleSkip) : 1) {
    315                /** @type {number} */ var err = Math.min(tcuFuzzyImageCompare.compareToNeighbor(params, rnd, refFilteredView.read(x, y, 4), cmpFilteredView, x, y, 4),
    316                                       tcuFuzzyImageCompare.compareToNeighbor(params, rnd, cmpFilteredView.read(x, y, 4), refFilteredView, x, y, 4));
    317 
    318                err = Math.pow(err, params.errExp);
    319 
    320                errSum += err;
    321                numSamples += 1;
    322 
    323                // Build error image.
    324                /** @type {number} */ var red = err * 500.0;
    325                /** @type {number} */ var luma = tcuFuzzyImageCompare.toGrayscale(cmp.getPixel(x, y));
    326                /** @type {number} */ var rF = 0.7 + 0.3 * luma;
    327                errorMask.setPixel([red * rF, (1.0 - red) * rF, 0.0, 1.0], x, y);
    328 
    329            }
    330        }
    331 
    332        // Scale error sum based on number of samples taken
    333        errSum *= ((width - 2) * (height - 2)) / numSamples;
    334 
    335        return errSum;
    336    };
    337 
    338 });