UNPKG

webdriverio-automation

Version:

WebdriverIO-Automation android ios project

208 lines (165 loc) 6.81 kB
/* * Copyright (c) 2015, Leon Sorokin * All rights reserved. (MIT Licensed) * * ColorHistogram.js - an image quantization lib */ /** * @preserve TypeScript port: * Copyright 2015-2016 Igor Bezkrovnyi * All rights reserved. (MIT Licensed) * * colorHistogram.ts - part of Image Quantization Library */ import { HueStatistics } from "../../utils/hueStatistics" import { PointContainer } from "../../utils/pointContainer" import { stableSort } from "../../utils/arithmetic" interface Box { x : number, y : number, h : number, w : number } export class ColorHistogram { private static _boxSize = [ 64, 64 ]; private static _boxPixels = 2; private static _hueGroups = 10; // 1 = by global population, 2 = subregion population threshold private _method : number; // HueStatistics instance private _hueStats : HueStatistics; private _histogram : {[color : string] : number}; // # of highest-frequency colors to start with for palette reduction private _initColors : number; // if > 0, enables hues stats and min-color retention per group private _minHueCols : number; constructor(method : number, colors : number) { // 1 = by global population, 2 = subregion population threshold this._method = method; // if > 0, enables hues stats and min-color retention per group this._minHueCols = colors << 2;//opts.minHueCols || 0; // # of highest-frequency colors to start with for palette reduction this._initColors = colors << 2; // HueStatistics instance this._hueStats = new HueStatistics(ColorHistogram._hueGroups, this._minHueCols); this._histogram = Object.create(null); } sample(pointBuffer : PointContainer) : void { switch (this._method) { case 1: this._colorStats1D(pointBuffer); break; case 2: this._colorStats2D(pointBuffer); break; } } getImportanceSortedColorsIDXI32() { // TODO: fix typing issue in stableSort func const sorted = stableSort(Object.keys(this._histogram), (a, b) => this._histogram[ b ] - this._histogram[ a ]); if (sorted.length === 0) { return []; } let idxi32 : string[]; switch (this._method) { case 1: const initialColorsLimit = Math.min(sorted.length, this._initColors), last = sorted[ initialColorsLimit - 1 ], freq = this._histogram[ last ]; idxi32 = sorted.slice(0, initialColorsLimit); // add any cut off colors with same freq as last let pos = initialColorsLimit, len = sorted.length; while (pos < len && this._histogram[ sorted[ pos ] ] == freq) idxi32.push(sorted[ pos++ ]); // inject min huegroup colors this._hueStats.injectIntoArray(idxi32); break; case 2: idxi32 = sorted; break; default: // TODO: rethink errors throw new Error("Incorrect method") } // int32-ify values return (<string[]><any>idxi32).map(function (v : string) { return +v; }); } // global top-population private _colorStats1D(pointBuffer : PointContainer) { const histG = this._histogram, pointArray = pointBuffer.getPointArray(), len = pointArray.length; for (let i = 0; i < len; i++) { const col = pointArray[ i ].uint32; // collect hue stats this._hueStats.check(col); if (col in histG) histG[ col ]++; else histG[ col ] = 1; } } // population threshold within subregions // FIXME: this can over-reduce (few/no colors same?), need a way to keep // important colors that dont ever reach local thresholds (gradients?) private _colorStats2D(pointBuffer : PointContainer) { const width = pointBuffer.getWidth(), height = pointBuffer.getHeight(), pointArray = pointBuffer.getPointArray(); const boxW = ColorHistogram._boxSize[ 0 ], boxH = ColorHistogram._boxSize[ 1 ], area = boxW * boxH, boxes = this._makeBoxes(width, height, boxW, boxH), histG = this._histogram; boxes.forEach(box => { let effc = Math.round((box.w * box.h) / area) * ColorHistogram._boxPixels; if (effc < 2) effc = 2; const histL : { [key : string ] : number} = {}; this._iterateBox(box, width, (i : number) => { const col = pointArray[ i ].uint32; // collect hue stats this._hueStats.check(col); if (col in histG) histG[ col ]++; else if (col in histL) { if (++histL[ col ] >= effc) histG[ col ] = histL[ col ]; } else histL[ col ] = 1; }); }); // inject min huegroup colors this._hueStats.injectIntoDictionary(histG); } // iterates @bbox within a parent rect of width @wid; calls @fn, passing index within parent private _iterateBox(bbox : Box, wid : number, fn : Function) { const b = bbox, i0 = b.y * wid + b.x, i1 = (b.y + b.h - 1) * wid + (b.x + b.w - 1), incr = wid - b.w + 1; let cnt = 0, i = i0; do { fn.call(this, i); i += (++cnt % b.w == 0) ? incr : 1; } while (i <= i1); } /** * partitions a rectangle of width x height into * array of boxes stepX x stepY (or less) */ private _makeBoxes(width : number, height : number, stepX : number, stepY : number) { const wrem = width % stepX, hrem = height % stepY, xend = width - wrem, yend = height - hrem, boxesArray : Box[] = []; for (let y = 0; y < height; y += stepY) for (let x = 0; x < width; x += stepX) boxesArray.push({ x, y, w : (x == xend ? wrem : stepX), h : (y == yend ? hrem : stepY) }); return boxesArray; } }