微信小程序 | 人脸识别的最终解决方案

news2024/11/18 15:37:43

📌个人主页:个人主页
​🧀 推荐专栏:小程序开发成神之路 --(这是一个为想要入门和进阶小程序开发专门开启的精品专栏!从个人到商业的全套开发教程,实打实的干货分享,确定不来看看? 😻😻)
📝作者简介:一个读研中创业、打工中学习的能搞全栈、也搞算法、目前在搞大数据的奋斗者。
⭐️您的小小关注是我持续输出的动力!⭐️


在这里插入图片描述


干货内容推荐

🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :

  • 《吐血整理的几十款小程序登陆界面【附完整代码】》
  • 《你真的会做小程序按钮吗?看了字节35K前端的样式设计,悟了》
  • 《来接私活吧?玩转小程序开发之丝滑拆红包【附完整代码】》
  • 《来接私活吧?小程序接私活必备功能-婚恋交友【附完整代码】》

一、人脸识别功能现状

1.1 微信原生接口篇

在这里插入图片描述
微信原生的人脸识别接口可以完美的结合小程序所采集到的图像,可以达到实时帧级别的识别,是高效开发人脸识别功能的首选!

但是,由于人脸数据是个人的敏感数据,微信在开放该接口出来时,就是为了方便各行各业的业务开展。所以,我们要用它,就必须具备相应的资质。后续才能通过审核并发布上线。

资质说明链接:微信开放能力文档
在这里插入图片描述


1.2 百度接口篇

百度人脸识别SDK的服务模式是用户在平台开通好相应的权限以及获取到appId等一系列操作之后,再到我们开发端是采用对百度人脸验证平台的相应接口进行发送验证请求才能实现服务的调用。

也就是说:百度已经人脸验证服务帮你全部搭建好了,你只需要发起请求进行调即可。

百度人脸识别服务地址
在这里插入图片描述

可以按量收费,目前根据你的业务量的QPS进行计费统计。

如果您的业务并发支持要求较高,免费测试 QPS 不能满足,您可以随时购买扩充 QPS ,QPS 可包月购买,也可按天购买,灵活多样,适应多场景需求。


1.3 虹软接口篇

在这里插入图片描述

平台链接地址:虹软接口平台

虹软 和 百度 这两者之间的区别在于:

  • 百度直接替你部署好了识别用的服务,你只需要按照文档指引向其特定的 接口发送数据即可获得你的结果,在某些主前端开发轻后端服务的应用来说还是很有优势的。
  • 虹软所走的模式是不管你是离线还是在线都能让你运行,他把搭建服务端的工作留给你自己去做。在这样的模式下,你可以实时把控用户的人脸数据,对其进行自定义操作,从而构造更为灵活的人脸设别方式,对业务场景的开发也可以有更多操作的空间!

二、人脸识别功能流程

在这里插入图片描述


三、微信接口解析

3.1 微信摄像头组件的使用

  • 小程序中要使用到摄像头的功能在于使用<camera>标签:
<camera v-if='isAuthCamera' device-position="front" class="camera" flash="off" resolution='high' />

对于<camera>标签核心的参数如下:

属性类型必填说明可选值默认值
modestring应用模式,只在初始化时有效,不能动态变更normal: 相机模式 】【scanCode:扫码模式normal
属性类型必填说明可选值默认值
resolutionstring分辨率,不支持动态修改low 低 】【medium 中】【high 高medium
属性类型必填说明可选值默认值
device-positionstring摄像头朝向front 前置 】【back 后置back
属性类型必填说明可选值默认值
flashstring闪光灯,值为 auto , on, offauto 自动 】【on 打开】【off 关闭】【torch 常亮auto

3.2 微信小程序实时视频帧获取

对如何调用和开启微信小程序中的摄像头摄像和拍照功能进行学习:微信小程序–摄像头接口详解

  • 首先需要调用CameraContext wx.createCameraContext()方法创建camera操作对象。
  • 然后在camera对象之后调用onCameraFrame(function callback),该方法用于实时获取摄像头所捕捉的相片帧,从而用于与后端人脸识别引擎进行交互。
const context = wx.createCameraContext()
const listener = context.onCameraFrame((frame) => {
 	 console.log(frame.data instanceof ArrayBuffer, frame.width, frame.height)
})
listener.start()

四、工具包的准备

4.1 将帧数据转为Base64

  • 针对onCameraFrame()方法,获取到的是相机所捕捉的图片帧数据,这个时候我们需要将其转为Base64格式的图片数据,这是为了让其传输到后端能够被人脸识别引擎所使用!
  • 在微信所开放的用于将帧数据转化为base 64接口中wx.arrayBufferToBase64(已弃用),项目中需要用摄像头获取人脸并将获取的ArrayBuffer数据转化为base64,就需要经过一下流程:
    在这里插入图片描述
    代码如下:
	let pngData = ToPNG.encode([frame.data], frame.width, frame.height),  
			base64 = Base64Util.arrayBufferToBase64(pngData)
  • 所以先准备ArrayBuffer数据转化为PNG数据的工具包ToPNG.js
import pako from 'pako'
var UPNG = {};



UPNG.toRGBA8 = function (out) {
    var w = out.width,
        h = out.height;
    if (out.tabs.acTL == null) return [UPNG.toRGBA8.decodeImage(out.data, w, h, out).buffer];

    var frms = [];
    if (out.frames[0].data == null) out.frames[0].data = out.data;

    var len = w * h * 4,
        img = new Uint8Array(len),
        empty = new Uint8Array(len),
        prev = new Uint8Array(len);
    for (var i = 0; i < out.frames.length; i++) {
        var frm = out.frames[i];
        var fx = frm.rect.x,
            fy = frm.rect.y,
            fw = frm.rect.width,
            fh = frm.rect.height;
        var fdata = UPNG.toRGBA8.decodeImage(frm.data, fw, fh, out);

        if (i != 0)
            for (var j = 0; j < len; j++) prev[j] = img[j];

        if (frm.blend == 0) UPNG._copyTile(fdata, fw, fh, img, w, h, fx, fy, 0);
        else if (frm.blend == 1) UPNG._copyTile(fdata, fw, fh, img, w, h, fx, fy, 1);

        frms.push(img.buffer.slice(0));

        if (frm.dispose == 0) {} else if (frm.dispose == 1) UPNG._copyTile(empty, fw, fh, img, w, h, fx, fy, 0);
        else if (frm.dispose == 2)
            for (var j = 0; j < len; j++) img[j] = prev[j];
    }
    return frms;
}
UPNG.toRGBA8.decodeImage = function (data, w, h, out) {
    var area = w * h,
        bpp = UPNG.decode._getBPP(out);
    var bpl = Math.ceil(w * bpp / 8); // bytes per line

    var bf = new Uint8Array(area * 4),
        bf32 = new Uint32Array(bf.buffer);
    var ctype = out.ctype,
        depth = out.depth;
    var rs = UPNG._bin.readUshort;

    //console.log(ctype, depth);
    var time = Date.now();

    if (ctype == 6) { // RGB + alpha
        var qarea = area << 2;
        if (depth == 8)
            for (var i = 0; i < qarea; i += 4) {
                bf[i] = data[i];
                bf[i + 1] = data[i + 1];
                bf[i + 2] = data[i + 2];
                bf[i + 3] = data[i + 3];
            }
        if (depth == 16)
            for (var i = 0; i < qarea; i++) {
                bf[i] = data[i << 1];
            }
    } else if (ctype == 2) { // RGB
        var ts = out.tabs["tRNS"];
        if (ts == null) {
            if (depth == 8)
                for (var i = 0; i < area; i++) {
                    var ti = i * 3;
                    bf32[i] = (255 << 24) | (data[ti + 2] << 16) | (data[ti + 1] << 8) | data[ti];
                }
            if (depth == 16)
                for (var i = 0; i < area; i++) {
                    var ti = i * 6;
                    bf32[i] = (255 << 24) | (data[ti + 4] << 16) | (data[ti + 2] << 8) | data[ti];
                }
        } else {
            var tr = ts[0],
                tg = ts[1],
                tb = ts[2];
            if (depth == 8)
                for (var i = 0; i < area; i++) {
                    var qi = i << 2,
                        ti = i * 3;
                    bf32[i] = (255 << 24) | (data[ti + 2] << 16) | (data[ti + 1] << 8) | data[ti];
                    if (data[ti] == tr && data[ti + 1] == tg && data[ti + 2] == tb) bf[qi + 3] = 0;
                }
            if (depth == 16)
                for (var i = 0; i < area; i++) {
                    var qi = i << 2,
                        ti = i * 6;
                    bf32[i] = (255 << 24) | (data[ti + 4] << 16) | (data[ti + 2] << 8) | data[ti];
                    if (rs(data, ti) == tr && rs(data, ti + 2) == tg && rs(data, ti + 4) == tb) bf[qi + 3] = 0;
                }
        }
    } else if (ctype == 3) { // palette
        var p = out.tabs["PLTE"],
            ap = out.tabs["tRNS"],
            tl = ap ? ap.length : 0;
        //console.log(p, ap);
        if (depth == 1)
            for (var y = 0; y < h; y++) {
                var s0 = y * bpl,
                    t0 = y * w;
                for (var i = 0; i < w; i++) {
                    var qi = (t0 + i) << 2,
                        j = ((data[s0 + (i >> 3)] >> (7 - ((i & 7) << 0))) & 1),
                        cj = 3 * j;
                    bf[qi] = p[cj];
                    bf[qi + 1] = p[cj + 1];
                    bf[qi + 2] = p[cj + 2];
                    bf[qi + 3] = (j < tl) ? ap[j] : 255;
                }
            }
        if (depth == 2)
            for (var y = 0; y < h; y++) {
                var s0 = y * bpl,
                    t0 = y * w;
                for (var i = 0; i < w; i++) {
                    var qi = (t0 + i) << 2,
                        j = ((data[s0 + (i >> 2)] >> (6 - ((i & 3) << 1))) & 3),
                        cj = 3 * j;
                    bf[qi] = p[cj];
                    bf[qi + 1] = p[cj + 1];
                    bf[qi + 2] = p[cj + 2];
                    bf[qi + 3] = (j < tl) ? ap[j] : 255;
                }
            }
        if (depth == 4)
            for (var y = 0; y < h; y++) {
                var s0 = y * bpl,
                    t0 = y * w;
                for (var i = 0; i < w; i++) {
                    var qi = (t0 + i) << 2,
                        j = ((data[s0 + (i >> 1)] >> (4 - ((i & 1) << 2))) & 15),
                        cj = 3 * j;
                    bf[qi] = p[cj];
                    bf[qi + 1] = p[cj + 1];
                    bf[qi + 2] = p[cj + 2];
                    bf[qi + 3] = (j < tl) ? ap[j] : 255;
                }
            }
        if (depth == 8)
            for (var i = 0; i < area; i++) {
                var qi = i << 2,
                    j = data[i],
                    cj = 3 * j;
                bf[qi] = p[cj];
                bf[qi + 1] = p[cj + 1];
                bf[qi + 2] = p[cj + 2];
                bf[qi + 3] = (j < tl) ? ap[j] : 255;
            }
    } else if (ctype == 4) { // gray + alpha
        if (depth == 8)
            for (var i = 0; i < area; i++) {
                var qi = i << 2,
                    di = i << 1,
                    gr = data[di];
                bf[qi] = gr;
                bf[qi + 1] = gr;
                bf[qi + 2] = gr;
                bf[qi + 3] = data[di + 1];
            }
        if (depth == 16)
            for (var i = 0; i < area; i++) {
                var qi = i << 2,
                    di = i << 2,
                    gr = data[di];
                bf[qi] = gr;
                bf[qi + 1] = gr;
                bf[qi + 2] = gr;
                bf[qi + 3] = data[di + 2];
            }
    } else if (ctype == 0) { // gray
        var tr = out.tabs["tRNS"] ? out.tabs["tRNS"] : -1;
        for (var y = 0; y < h; y++) {
            var off = y * bpl,
                to = y * w;
            if (depth == 1)
                for (var x = 0; x < w; x++) {
                    var gr = 255 * ((data[off + (x >>> 3)] >>> (7 - ((x & 7)))) & 1),
                        al = (gr == tr * 255) ? 0 : 255;
                    bf32[to + x] = (al << 24) | (gr << 16) | (gr << 8) | gr;
                }
            else if (depth == 2)
                for (var x = 0; x < w; x++) {
                    var gr = 85 * ((data[off + (x >>> 2)] >>> (6 - ((x & 3) << 1))) & 3),
                        al = (gr == tr * 85) ? 0 : 255;
                    bf32[to + x] = (al << 24) | (gr << 16) | (gr << 8) | gr;
                }
            else if (depth == 4)
                for (var x = 0; x < w; x++) {
                    var gr = 17 * ((data[off + (x >>> 1)] >>> (4 - ((x & 1) << 2))) & 15),
                        al = (gr == tr * 17) ? 0 : 255;
                    bf32[to + x] = (al << 24) | (gr << 16) | (gr << 8) | gr;
                }
            else if (depth == 8)
                for (var x = 0; x < w; x++) {
                    var gr = data[off + x],
                        al = (gr == tr) ? 0 : 255;
                    bf32[to + x] = (al << 24) | (gr << 16) | (gr << 8) | gr;
                }
            else if (depth == 16)
                for (var x = 0; x < w; x++) {
                    var gr = data[off + (x << 1)],
                        al = (rs(data, off + (x << 1)) == tr) ? 0 : 255;
                    bf32[to + x] = (al << 24) | (gr << 16) | (gr << 8) | gr;
                }
        }
    }
    //console.log(Date.now()-time);
    return bf;
}



UPNG.decode = function (buff) {
    var data = new Uint8Array(buff),
        offset = 8,
        bin = UPNG._bin,
        rUs = bin.readUshort,
        rUi = bin.readUint;
    var out = {
        tabs: {},
        frames: []
    };
    var dd = new Uint8Array(data.length),
        doff = 0; // put all IDAT data into it
    var fd, foff = 0; // frames

    var mgck = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
    for (var i = 0; i < 8; i++)
        if (data[i] != mgck[i]) throw "The input is not a PNG file!";

    while (offset < data.length) {
        var len = bin.readUint(data, offset);
        offset += 4;
        var type = bin.readASCII(data, offset, 4);
        offset += 4;
        //console.log(type,len);

        if (type == "IHDR") {
            UPNG.decode._IHDR(data, offset, out);
        } else if (type == "CgBI") {
            out.tabs[type] = data.slice(offset, offset + 4);
        } else if (type == "IDAT") {
            for (var i = 0; i < len; i++) dd[doff + i] = data[offset + i];
            doff += len;
        } else if (type == "acTL") {
            out.tabs[type] = {
                num_frames: rUi(data, offset),
                num_plays: rUi(data, offset + 4)
            };
            fd = new Uint8Array(data.length);
        } else if (type == "fcTL") {
            if (foff != 0) {
                var fr = out.frames[out.frames.length - 1];
                fr.data = UPNG.decode._decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height);
                foff = 0;
            }
            var rct = {
                x: rUi(data, offset + 12),
                y: rUi(data, offset + 16),
                width: rUi(data, offset + 4),
                height: rUi(data, offset + 8)
            };
            var del = rUs(data, offset + 22);
            del = rUs(data, offset + 20) / (del == 0 ? 100 : del);
            var frm = {
                rect: rct,
                delay: Math.round(del * 1000),
                dispose: data[offset + 24],
                blend: data[offset + 25]
            };
            //console.log(frm);
            out.frames.push(frm);
        } else if (type == "fdAT") {
            for (var i = 0; i < len - 4; i++) fd[foff + i] = data[offset + i + 4];
            foff += len - 4;
        } else if (type == "pHYs") {
            out.tabs[type] = [bin.readUint(data, offset), bin.readUint(data, offset + 4), data[offset + 8]];
        } else if (type == "cHRM") {
            out.tabs[type] = [];
            for (var i = 0; i < 8; i++) out.tabs[type].push(bin.readUint(data, offset + i * 4));
        } else if (type == "tEXt" || type == "zTXt") {
            if (out.tabs[type] == null) out.tabs[type] = {};
            var nz = bin.nextZero(data, offset);
            var keyw = bin.readASCII(data, offset, nz - offset);
            var text, tl = offset + len - nz - 1;
            if (type == "tEXt") text = bin.readASCII(data, nz + 1, tl);
            else {
                var bfr = UPNG.decode._inflate(data.slice(nz + 2, nz + 2 + tl));
                text = bin.readUTF8(bfr, 0, bfr.length);
            }
            out.tabs[type][keyw] = text;
        } else if (type == "iTXt") {
            if (out.tabs[type] == null) out.tabs[type] = {};
            var nz = 0,
                off = offset;
            nz = bin.nextZero(data, off);
            var keyw = bin.readASCII(data, off, nz - off);
            off = nz + 1;
            var cflag = data[off],
                cmeth = data[off + 1];
            off += 2;
            nz = bin.nextZero(data, off);
            var ltag = bin.readASCII(data, off, nz - off);
            off = nz + 1;
            nz = bin.nextZero(data, off);
            var tkeyw = bin.readUTF8(data, off, nz - off);
            off = nz + 1;
            var text, tl = len - (off - offset);
            if (cflag == 0) text = bin.readUTF8(data, off, tl);
            else {
                var bfr = UPNG.decode._inflate(data.slice(off, off + tl));
                text = bin.readUTF8(bfr, 0, bfr.length);
            }
            out.tabs[type][keyw] = text;
        } else if (type == "PLTE") {
            out.tabs[type] = bin.readBytes(data, offset, len);
        } else if (type == "hIST") {
            var pl = out.tabs["PLTE"].length / 3;
            out.tabs[type] = [];
            for (var i = 0; i < pl; i++) out.tabs[type].push(rUs(data, offset + i * 2));
        } else if (type == "tRNS") {
            if (out.ctype == 3) out.tabs[type] = bin.readBytes(data, offset, len);
            else if (out.ctype == 0) out.tabs[type] = rUs(data, offset);
            else if (out.ctype == 2) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)];
            //else console.log("tRNS for unsupported color type",out.ctype, len);
        } else if (type == "gAMA") out.tabs[type] = bin.readUint(data, offset) / 100000;
        else if (type == "sRGB") out.tabs[type] = data[offset];
        else if (type == "bKGD") {
            if (out.ctype == 0 || out.ctype == 4) out.tabs[type] = [rUs(data, offset)];
            else if (out.ctype == 2 || out.ctype == 6) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)];
            else if (out.ctype == 3) out.tabs[type] = data[offset];
        } else if (type == "IEND") {
            break;
        }
        //else {  console.log("unknown chunk type", type, len);  out.tabs[type]=data.slice(offset,offset+len);  }
        offset += len;
        var crc = bin.readUint(data, offset);
        offset += 4;
    }
    if (foff != 0) {
        var fr = out.frames[out.frames.length - 1];
        fr.data = UPNG.decode._decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height);
    }
    out.data = UPNG.decode._decompress(out, dd, out.width, out.height);

    delete out.compress;
    delete out.interlace;
    delete out.filter;
    return out;
}

UPNG.decode._decompress = function (out, dd, w, h) {
    var time = Date.now();
    var bpp = UPNG.decode._getBPP(out),
        bpl = Math.ceil(w * bpp / 8),
        buff = new Uint8Array((bpl + 1 + out.interlace) * h);
    if (out.tabs["CgBI"]) dd = UPNG.inflateRaw(dd, buff);
    else dd = UPNG.decode._inflate(dd, buff);
    //console.log(dd.length, buff.length);
    //console.log(Date.now()-time);

    var time = Date.now();
    if (out.interlace == 0) dd = UPNG.decode._filterZero(dd, out, 0, w, h);
    else if (out.interlace == 1) dd = UPNG.decode._readInterlace(dd, out);
    //console.log(Date.now()-time);
    return dd;
}

UPNG.decode._inflate = function (data, buff) {
    var out = UPNG["inflateRaw"](new Uint8Array(data.buffer, 2, data.length - 6), buff);
    return out;
}
UPNG.inflateRaw = function () {
    var H = {};
    H.H = {};
    H.H.N = function (N, W) {
        var R = Uint8Array,
            i = 0,
            m = 0,
            J = 0,
            h = 0,
            Q = 0,
            X = 0,
            u = 0,
            w = 0,
            d = 0,
            v, C;
        if (N[0] == 3 && N[1] == 0) return W ? W : new R(0);
        var V = H.H,
            n = V.b,
            A = V.e,
            l = V.R,
            M = V.n,
            I = V.A,
            e = V.Z,
            b = V.m,
            Z = W == null;
        if (Z) W = new R(N.length >>> 2 << 5);
        while (i == 0) {
            i = n(N, d, 1);
            m = n(N, d + 1, 2);
            d += 3;
            if (m == 0) {
                if ((d & 7) != 0) d += 8 - (d & 7);
                var D = (d >>> 3) + 4,
                    q = N[D - 4] | N[D - 3] << 8;
                if (Z) W = H.H.W(W, w + q);
                W.set(new R(N.buffer, N.byteOffset + D, q), w);
                d = D + q << 3;
                w += q;
                continue
            }
            if (Z) W = H.H.W(W, w + (1 << 17));
            if (m == 1) {
                v = b.J;
                C = b.h;
                X = (1 << 9) - 1;
                u = (1 << 5) - 1
            }
            if (m == 2) {
                J = A(N, d, 5) + 257;
                h = A(N, d + 5, 5) + 1;
                Q = A(N, d + 10, 4) + 4;
                d += 14;
                var E = d,
                    j = 1;
                for (var c = 0; c < 38; c += 2) {
                    b.Q[c] = 0;
                    b.Q[c + 1] = 0
                }
                for (var c = 0; c < Q; c++) {
                    var K = A(N, d + c * 3, 3);
                    b.Q[(b.X[c] << 1) + 1] = K;
                    if (K > j) j = K
                }
                d += 3 * Q;
                M(b.Q, j);
                I(b.Q, j, b.u);
                v = b.w;
                C = b.d;
                d = l(b.u, (1 << j) - 1, J + h, N, d, b.v);
                var r = V.V(b.v, 0, J, b.C);
                X = (1 << r) - 1;
                var S = V.V(b.v, J, h, b.D);
                u = (1 << S) - 1;
                M(b.C, r);
                I(b.C, r, v);
                M(b.D, S);
                I(b.D, S, C)
            }
            while (!0) {
                var T = v[e(N, d) & X];
                d += T & 15;
                var p = T >>> 4;
                if (p >>> 8 == 0) {
                    W[w++] = p
                } else if (p == 256) {
                    break
                } else {
                    var z = w + p - 254;
                    if (p > 264) {
                        var _ = b.q[p - 257];
                        z = w + (_ >>> 3) + A(N, d, _ & 7);
                        d += _ & 7
                    }
                    var $ = C[e(N, d) & u];
                    d += $ & 15;
                    var s = $ >>> 4,
                        Y = b.c[s],
                        a = (Y >>> 4) + n(N, d, Y & 15);
                    d += Y & 15;
                    while (w < z) {
                        W[w] = W[w++ - a];
                        W[w] = W[w++ - a];
                        W[w] = W[w++ - a];
                        W[w] = W[w++ - a]
                    }
                    w = z
                }
            }
        }
        return W.length == w ? W : W.slice(0, w)
    };
    H.H.W = function (N, W) {
        var R = N.length;
        if (W <= R) return N;
        var V = new Uint8Array(R << 1);
        V.set(N, 0);
        return V
    };
    H.H.R = function (N, W, R, V, n, A) {
        var l = H.H.e,
            M = H.H.Z,
            I = 0;
        while (I < R) {
            var e = N[M(V, n) & W];
            n += e & 15;
            var b = e >>> 4;
            if (b <= 15) {
                A[I] = b;
                I++
            } else {
                var Z = 0,
                    m = 0;
                if (b == 16) {
                    m = 3 + l(V, n, 2);
                    n += 2;
                    Z = A[I - 1]
                } else if (b == 17) {
                    m = 3 + l(V, n, 3);
                    n += 3
                } else if (b == 18) {
                    m = 11 + l(V, n, 7);
                    n += 7
                }
                var J = I + m;
                while (I < J) {
                    A[I] = Z;
                    I++
                }
            }
        }
        return n
    };
    H.H.V = function (N, W, R, V) {
        var n = 0,
            A = 0,
            l = V.length >>> 1;
        while (A < R) {
            var M = N[A + W];
            V[A << 1] = 0;
            V[(A << 1) + 1] = M;
            if (M > n) n = M;
            A++
        }
        while (A < l) {
            V[A << 1] = 0;
            V[(A << 1) + 1] = 0;
            A++
        }
        return n
    };
    H.H.n = function (N, W) {
        var R = H.H.m,
            V = N.length,
            n, A, l, M, I, e = R.j;
        for (var M = 0; M <= W; M++) e[M] = 0;
        for (M = 1; M < V; M += 2) e[N[M]]++;
        var b = R.K;
        n = 0;
        e[0] = 0;
        for (A = 1; A <= W; A++) {
            n = n + e[A - 1] << 1;
            b[A] = n
        }
        for (l = 0; l < V; l += 2) {
            I = N[l + 1];
            if (I != 0) {
                N[l] = b[I];
                b[I]++
            }
        }
    };
    H.H.A = function (N, W, R) {
        var V = N.length,
            n = H.H.m,
            A = n.r;
        for (var l = 0; l < V; l += 2)
            if (N[l + 1] != 0) {
                var M = l >> 1,
                    I = N[l + 1],
                    e = M << 4 | I,
                    b = W - I,
                    Z = N[l] << b,
                    m = Z + (1 << b);
                while (Z != m) {
                    var J = A[Z] >>> 15 - W;
                    R[J] = e;
                    Z++
                }
            }
    };
    H.H.l = function (N, W) {
        var R = H.H.m.r,
            V = 15 - W;
        for (var n = 0; n < N.length; n += 2) {
            var A = N[n] << W - N[n + 1];
            N[n] = R[A] >>> V
        }
    };
    H.H.M = function (N, W, R) {
        R = R << (W & 7);
        var V = W >>> 3;
        N[V] |= R;
        N[V + 1] |= R >>> 8
    };
    H.H.I = function (N, W, R) {
        R = R << (W & 7);
        var V = W >>> 3;
        N[V] |= R;
        N[V + 1] |= R >>> 8;
        N[V + 2] |= R >>> 16
    };
    H.H.e = function (N, W, R) {
        return (N[W >>> 3] | N[(W >>> 3) + 1] << 8) >>> (W & 7) & (1 << R) - 1
    };
    H.H.b = function (N, W, R) {
        return (N[W >>> 3] | N[(W >>> 3) + 1] << 8 | N[(W >>> 3) + 2] << 16) >>> (W & 7) & (1 << R) - 1
    };
    H.H.Z = function (N, W) {
        return (N[W >>> 3] | N[(W >>> 3) + 1] << 8 | N[(W >>> 3) + 2] << 16) >>> (W & 7)
    };
    H.H.i = function (N, W) {
        return (N[W >>> 3] | N[(W >>> 3) + 1] << 8 | N[(W >>> 3) + 2] << 16 | N[(W >>> 3) + 3] << 24) >>> (W & 7)
    };
    H.H.m = function () {
        var N = Uint16Array,
            W = Uint32Array;
        return {
            K: new N(16),
            j: new N(16),
            X: [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
            S: [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 999, 999, 999],
            T: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0],
            q: new N(32),
            p: [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 65535, 65535],
            z: [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0],
            c: new W(32),
            J: new N(512),
            _: [],
            h: new N(32),
            $: [],
            w: new N(32768),
            C: [],
            v: [],
            d: new N(32768),
            D: [],
            u: new N(512),
            Q: [],
            r: new N(1 << 15),
            s: new W(286),
            Y: new W(30),
            a: new W(19),
            t: new W(15e3),
            k: new N(1 << 16),
            g: new N(1 << 15)
        }
    }();
    (function () {
        var N = H.H.m,
            W = 1 << 15;
        for (var R = 0; R < W; R++) {
            var V = R;
            V = (V & 2863311530) >>> 1 | (V & 1431655765) << 1;
            V = (V & 3435973836) >>> 2 | (V & 858993459) << 2;
            V = (V & 4042322160) >>> 4 | (V & 252645135) << 4;
            V = (V & 4278255360) >>> 8 | (V & 16711935) << 8;
            N.r[R] = (V >>> 16 | V << 16) >>> 17
        }

        function n(A, l, M) {
            while (l-- != 0) A.push(0, M)
        }
        for (var R = 0; R < 32; R++) {
            N.q[R] = N.S[R] << 3 | N.T[R];
            N.c[R] = N.p[R] << 4 | N.z[R]
        }
        n(N._, 144, 8);
        n(N._, 255 - 143, 9);
        n(N._, 279 - 255, 7);
        n(N._, 287 - 279, 8);
        H.H.n(N._, 9);
        H.H.A(N._, 9, N.J);
        H.H.l(N._, 9);
        n(N.$, 32, 5);
        H.H.n(N.$, 5);
        H.H.A(N.$, 5, N.h);
        H.H.l(N.$, 5);
        n(N.Q, 19, 0);
        n(N.C, 286, 0);
        n(N.D, 30, 0);
        n(N.v, 320, 0)
    }());
    return H.H.N
}()


UPNG.decode._readInterlace = function (data, out) {
    var w = out.width,
        h = out.height;
    var bpp = UPNG.decode._getBPP(out),
        cbpp = bpp >> 3,
        bpl = Math.ceil(w * bpp / 8);
    var img = new Uint8Array(h * bpl);
    var di = 0;

    var starting_row = [0, 0, 4, 0, 2, 0, 1];
    var starting_col = [0, 4, 0, 2, 0, 1, 0];
    var row_increment = [8, 8, 8, 4, 4, 2, 2];
    var col_increment = [8, 8, 4, 4, 2, 2, 1];

    var pass = 0;
    while (pass < 7) {
        var ri = row_increment[pass],
            ci = col_increment[pass];
        var sw = 0,
            sh = 0;
        var cr = starting_row[pass];
        while (cr < h) {
            cr += ri;
            sh++;
        }
        var cc = starting_col[pass];
        while (cc < w) {
            cc += ci;
            sw++;
        }
        var bpll = Math.ceil(sw * bpp / 8);
        UPNG.decode._filterZero(data, out, di, sw, sh);

        var y = 0,
            row = starting_row[pass];
        while (row < h) {
            var col = starting_col[pass];
            var cdi = (di + y * bpll) << 3;

            while (col < w) {
                if (bpp == 1) {
                    var val = data[cdi >> 3];
                    val = (val >> (7 - (cdi & 7))) & 1;
                    img[row * bpl + (col >> 3)] |= (val << (7 - ((col & 7) << 0)));
                }
                if (bpp == 2) {
                    var val = data[cdi >> 3];
                    val = (val >> (6 - (cdi & 7))) & 3;
                    img[row * bpl + (col >> 2)] |= (val << (6 - ((col & 3) << 1)));
                }
                if (bpp == 4) {
                    var val = data[cdi >> 3];
                    val = (val >> (4 - (cdi & 7))) & 15;
                    img[row * bpl + (col >> 1)] |= (val << (4 - ((col & 1) << 2)));
                }
                if (bpp >= 8) {
                    var ii = row * bpl + col * cbpp;
                    for (var j = 0; j < cbpp; j++) img[ii + j] = data[(cdi >> 3) + j];
                }
                cdi += bpp;
                col += ci;
            }
            y++;
            row += ri;
        }
        if (sw * sh != 0) di += sh * (1 + bpll);
        pass = pass + 1;
    }
    return img;
}

UPNG.decode._getBPP = function (out) {
    var noc = [1, null, 3, 1, 2, null, 4][out.ctype];
    return noc * out.depth;
}

UPNG.decode._filterZero = function (data, out, off, w, h) {
    var bpp = UPNG.decode._getBPP(out),
        bpl = Math.ceil(w * bpp / 8),
        paeth = UPNG.decode._paeth;
    bpp = Math.ceil(bpp / 8);

    var i, di, type = data[off],
        x = 0;

    if (type > 1) data[off] = [0, 0, 1][type - 2];
    if (type == 3)
        for (x = bpp; x < bpl; x++) data[x + 1] = (data[x + 1] + (data[x + 1 - bpp] >>> 1)) & 255;

    for (var y = 0; y < h; y++) {
        i = off + y * bpl;
        di = i + y + 1;
        type = data[di - 1];
        x = 0;

        if (type == 0)
            for (; x < bpl; x++) data[i + x] = data[di + x];
        else if (type == 1) {
            for (; x < bpp; x++) data[i + x] = data[di + x];
            for (; x < bpl; x++) data[i + x] = (data[di + x] + data[i + x - bpp]);
        } else if (type == 2) {
            for (; x < bpl; x++) data[i + x] = (data[di + x] + data[i + x - bpl]);
        } else if (type == 3) {
            for (; x < bpp; x++) data[i + x] = (data[di + x] + (data[i + x - bpl] >>> 1));
            for (; x < bpl; x++) data[i + x] = (data[di + x] + ((data[i + x - bpl] + data[i + x - bpp]) >>> 1));
        } else {
            for (; x < bpp; x++) data[i + x] = (data[di + x] + paeth(0, data[i + x - bpl], 0));
            for (; x < bpl; x++) data[i + x] = (data[di + x] + paeth(data[i + x - bpp], data[i + x - bpl], data[i + x - bpp - bpl]));
        }
    }
    return data;
}

UPNG.decode._paeth = function (a, b, c) {
    var p = a + b - c,
        pa = (p - a),
        pb = (p - b),
        pc = (p - c);
    if (pa * pa <= pb * pb && pa * pa <= pc * pc) return a;
    else if (pb * pb <= pc * pc) return b;
    return c;
}

UPNG.decode._IHDR = function (data, offset, out) {
    var bin = UPNG._bin;
    out.width = bin.readUint(data, offset);
    offset += 4;
    out.height = bin.readUint(data, offset);
    offset += 4;
    out.depth = data[offset];
    offset++;
    out.ctype = data[offset];
    offset++;
    out.compress = data[offset];
    offset++;
    out.filter = data[offset];
    offset++;
    out.interlace = data[offset];
    offset++;
}

UPNG._bin = {
    nextZero: function (data, p) {
        while (data[p] != 0) p++;
        return p;
    },
    readUshort: function (buff, p) {
        return (buff[p] << 8) | buff[p + 1];
    },
    writeUshort: function (buff, p, n) {
        buff[p] = (n >> 8) & 255;
        buff[p + 1] = n & 255;
    },
    readUint: function (buff, p) {
        return (buff[p] * (256 * 256 * 256)) + ((buff[p + 1] << 16) | (buff[p + 2] << 8) | buff[p + 3]);
    },
    writeUint: function (buff, p, n) {
        buff[p] = (n >> 24) & 255;
        buff[p + 1] = (n >> 16) & 255;
        buff[p + 2] = (n >> 8) & 255;
        buff[p + 3] = n & 255;
    },
    readASCII: function (buff, p, l) {
        var s = "";
        for (var i = 0; i < l; i++) s += String.fromCharCode(buff[p + i]);
        return s;
    },
    writeASCII: function (data, p, s) {
        for (var i = 0; i < s.length; i++) data[p + i] = s.charCodeAt(i);
    },
    readBytes: function (buff, p, l) {
        var arr = [];
        for (var i = 0; i < l; i++) arr.push(buff[p + i]);
        return arr;
    },
    pad: function (n) {
        return n.length < 2 ? "0" + n : n;
    },
    readUTF8: function (buff, p, l) {
        var s = "",
            ns;
        for (var i = 0; i < l; i++) s += "%" + UPNG._bin.pad(buff[p + i].toString(16));
        try {
            ns = decodeURIComponent(s);
        } catch (e) {
            return UPNG._bin.readASCII(buff, p, l);
        }
        return ns;
    }
}
UPNG._copyTile = function (sb, sw, sh, tb, tw, th, xoff, yoff, mode) {
    var w = Math.min(sw, tw),
        h = Math.min(sh, th);
    var si = 0,
        ti = 0;
    for (var y = 0; y < h; y++)
        for (var x = 0; x < w; x++) {
            if (xoff >= 0 && yoff >= 0) {
                si = (y * sw + x) << 2;
                ti = ((yoff + y) * tw + xoff + x) << 2;
            } else {
                si = ((-yoff + y) * sw - xoff + x) << 2;
                ti = (y * tw + x) << 2;
            }

            if (mode == 0) {
                tb[ti] = sb[si];
                tb[ti + 1] = sb[si + 1];
                tb[ti + 2] = sb[si + 2];
                tb[ti + 3] = sb[si + 3];
            } else if (mode == 1) {
                var fa = sb[si + 3] * (1 / 255),
                    fr = sb[si] * fa,
                    fg = sb[si + 1] * fa,
                    fb = sb[si + 2] * fa;
                var ba = tb[ti + 3] * (1 / 255),
                    br = tb[ti] * ba,
                    bg = tb[ti + 1] * ba,
                    bb = tb[ti + 2] * ba;

                var ifa = 1 - fa,
                    oa = fa + ba * ifa,
                    ioa = (oa == 0 ? 0 : 1 / oa);
                tb[ti + 3] = 255 * oa;
                tb[ti + 0] = (fr + br * ifa) * ioa;
                tb[ti + 1] = (fg + bg * ifa) * ioa;
                tb[ti + 2] = (fb + bb * ifa) * ioa;
            } else if (mode == 2) { // copy only differences, otherwise zero
                var fa = sb[si + 3],
                    fr = sb[si],
                    fg = sb[si + 1],
                    fb = sb[si + 2];
                var ba = tb[ti + 3],
                    br = tb[ti],
                    bg = tb[ti + 1],
                    bb = tb[ti + 2];
                if (fa == ba && fr == br && fg == bg && fb == bb) {
                    tb[ti] = 0;
                    tb[ti + 1] = 0;
                    tb[ti + 2] = 0;
                    tb[ti + 3] = 0;
                } else {
                    tb[ti] = fr;
                    tb[ti + 1] = fg;
                    tb[ti + 2] = fb;
                    tb[ti + 3] = fa;
                }
            } else if (mode == 3) { // check if can be blended
                var fa = sb[si + 3],
                    fr = sb[si],
                    fg = sb[si + 1],
                    fb = sb[si + 2];
                var ba = tb[ti + 3],
                    br = tb[ti],
                    bg = tb[ti + 1],
                    bb = tb[ti + 2];
                if (fa == ba && fr == br && fg == bg && fb == bb) continue;
                //if(fa!=255 && ba!=0) return false;
                if (fa < 220 && ba > 20) return false;
            }
        }
    return true;
}





UPNG.encode = function (bufs, w, h, ps, dels, tabs, forbidPlte) {
    if (ps == null) ps = 0;
    if (forbidPlte == null) forbidPlte = false;

    var nimg = UPNG.encode.compress(bufs, w, h, ps, [false, false, false, 0, forbidPlte, false]);
    UPNG.encode.compressPNG(nimg, -1);

    return UPNG.encode._main(nimg, w, h, dels, tabs);
}

UPNG.encodeLL = function (bufs, w, h, cc, ac, depth, dels, tabs) {
    var nimg = {
        ctype: 0 + (cc == 1 ? 0 : 2) + (ac == 0 ? 0 : 4),
        depth: depth,
        frames: []
    };

    var time = Date.now();
    var bipp = (cc + ac) * depth,
        bipl = bipp * w;
    for (var i = 0; i < bufs.length; i++)
        nimg.frames.push({
            rect: {
                x: 0,
                y: 0,
                width: w,
                height: h
            },
            img: new Uint8Array(bufs[i]),
            blend: 0,
            dispose: 1,
            bpp: Math.ceil(bipp / 8),
            bpl: Math.ceil(bipl / 8)
        });

    UPNG.encode.compressPNG(nimg, 0, true);

    var out = UPNG.encode._main(nimg, w, h, dels, tabs);
    return out;
}

UPNG.encode._main = function (nimg, w, h, dels, tabs) {
    if (tabs == null) tabs = {};
    var crc = UPNG.crc.crc,
        wUi = UPNG._bin.writeUint,
        wUs = UPNG._bin.writeUshort,
        wAs = UPNG._bin.writeASCII;
    var offset = 8,
        anim = nimg.frames.length > 1,
        pltAlpha = false;

    var leng = 8 + (16 + 5 + 4) /*+ (9+4)*/ + (anim ? 20 : 0);
    if (tabs["sRGB"] != null) leng += 8 + 1 + 4;
    if (tabs["pHYs"] != null) leng += 8 + 9 + 4;
    if (nimg.ctype == 3) {
        var dl = nimg.plte.length;
        for (var i = 0; i < dl; i++)
            if ((nimg.plte[i] >>> 24) != 255) pltAlpha = true;
        leng += (8 + dl * 3 + 4) + (pltAlpha ? (8 + dl * 1 + 4) : 0);
    }
    for (var j = 0; j < nimg.frames.length; j++) {
        var fr = nimg.frames[j];
        if (anim) leng += 38;
        leng += fr.cimg.length + 12;
        if (j != 0) leng += 4;
    }
    leng += 12;

    var data = new Uint8Array(leng);
    var wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
    for (var i = 0; i < 8; i++) data[i] = wr[i];

    wUi(data, offset, 13);
    offset += 4;
    wAs(data, offset, "IHDR");
    offset += 4;
    wUi(data, offset, w);
    offset += 4;
    wUi(data, offset, h);
    offset += 4;
    data[offset] = nimg.depth;
    offset++; // depth
    data[offset] = nimg.ctype;
    offset++; // ctype
    data[offset] = 0;
    offset++; // compress
    data[offset] = 0;
    offset++; // filter
    data[offset] = 0;
    offset++; // interlace
    wUi(data, offset, crc(data, offset - 17, 17));
    offset += 4; // crc

    // 13 bytes to say, that it is sRGB
    if (tabs["sRGB"] != null) {
        wUi(data, offset, 1);
        offset += 4;
        wAs(data, offset, "sRGB");
        offset += 4;
        data[offset] = tabs["sRGB"];
        offset++;
        wUi(data, offset, crc(data, offset - 5, 5));
        offset += 4; // crc
    }
    if (tabs["pHYs"] != null) {
        wUi(data, offset, 9);
        offset += 4;
        wAs(data, offset, "pHYs");
        offset += 4;
        wUi(data, offset, tabs["pHYs"][0]);
        offset += 4;
        wUi(data, offset, tabs["pHYs"][1]);
        offset += 4;
        data[offset] = tabs["pHYs"][2];
        offset++;
        wUi(data, offset, crc(data, offset - 13, 13));
        offset += 4; // crc
    }

    if (anim) {
        wUi(data, offset, 8);
        offset += 4;
        wAs(data, offset, "acTL");
        offset += 4;
        wUi(data, offset, nimg.frames.length);
        offset += 4;
        wUi(data, offset, tabs["loop"] != null ? tabs["loop"] : 0);
        offset += 4;
        wUi(data, offset, crc(data, offset - 12, 12));
        offset += 4; // crc
    }

    if (nimg.ctype == 3) {
        var dl = nimg.plte.length;
        wUi(data, offset, dl * 3);
        offset += 4;
        wAs(data, offset, "PLTE");
        offset += 4;
        for (var i = 0; i < dl; i++) {
            var ti = i * 3,
                c = nimg.plte[i],
                r = (c) & 255,
                g = (c >>> 8) & 255,
                b = (c >>> 16) & 255;
            data[offset + ti + 0] = r;
            data[offset + ti + 1] = g;
            data[offset + ti + 2] = b;
        }
        offset += dl * 3;
        wUi(data, offset, crc(data, offset - dl * 3 - 4, dl * 3 + 4));
        offset += 4; // crc

        if (pltAlpha) {
            wUi(data, offset, dl);
            offset += 4;
            wAs(data, offset, "tRNS");
            offset += 4;
            for (var i = 0; i < dl; i++) data[offset + i] = (nimg.plte[i] >>> 24) & 255;
            offset += dl;
            wUi(data, offset, crc(data, offset - dl - 4, dl + 4));
            offset += 4; // crc
        }
    }

    var fi = 0;
    for (var j = 0; j < nimg.frames.length; j++) {
        var fr = nimg.frames[j];
        if (anim) {
            wUi(data, offset, 26);
            offset += 4;
            wAs(data, offset, "fcTL");
            offset += 4;
            wUi(data, offset, fi++);
            offset += 4;
            wUi(data, offset, fr.rect.width);
            offset += 4;
            wUi(data, offset, fr.rect.height);
            offset += 4;
            wUi(data, offset, fr.rect.x);
            offset += 4;
            wUi(data, offset, fr.rect.y);
            offset += 4;
            wUs(data, offset, dels[j]);
            offset += 2;
            wUs(data, offset, 1000);
            offset += 2;
            data[offset] = fr.dispose;
            offset++; // dispose
            data[offset] = fr.blend;
            offset++; // blend
            wUi(data, offset, crc(data, offset - 30, 30));
            offset += 4; // crc
        }

        var imgd = fr.cimg,
            dl = imgd.length;
        wUi(data, offset, dl + (j == 0 ? 0 : 4));
        offset += 4;
        var ioff = offset;
        wAs(data, offset, (j == 0) ? "IDAT" : "fdAT");
        offset += 4;
        if (j != 0) {
            wUi(data, offset, fi++);
            offset += 4;
        }
        data.set(imgd, offset);
        offset += dl;
        wUi(data, offset, crc(data, ioff, offset - ioff));
        offset += 4; // crc
    }

    wUi(data, offset, 0);
    offset += 4;
    wAs(data, offset, "IEND");
    offset += 4;
    wUi(data, offset, crc(data, offset - 4, 4));
    offset += 4; // crc

    return data.buffer;
}

UPNG.encode.compressPNG = function (out, filter, levelZero) {
    for (var i = 0; i < out.frames.length; i++) {
        var frm = out.frames[i],
            nw = frm.rect.width,
            nh = frm.rect.height;
        var fdata = new Uint8Array(nh * frm.bpl + nh);
        frm.cimg = UPNG.encode._filterZero(frm.img, nh, frm.bpp, frm.bpl, fdata, filter, levelZero);
    }
}



UPNG.encode.compress = function (bufs, w, h, ps, prms) // prms:  onlyBlend, minBits, forbidPlte
{
    //var time = Date.now();
    var onlyBlend = prms[0],
        evenCrd = prms[1],
        forbidPrev = prms[2],
        minBits = prms[3],
        forbidPlte = prms[4],
        dither = prms[5];

    var ctype = 6,
        depth = 8,
        alphaAnd = 255

    for (var j = 0; j < bufs.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame
        var img = new Uint8Array(bufs[j]),
            ilen = img.length;
        for (var i = 0; i < ilen; i += 4) alphaAnd &= img[i + 3];
    }
    var gotAlpha = (alphaAnd != 255);

    //console.log("alpha check", Date.now()-time);  time = Date.now();

    //var brute = gotAlpha && forGIF;		// brute : frames can only be copied, not "blended"
    var frms = UPNG.encode.framize(bufs, w, h, onlyBlend, evenCrd, forbidPrev);
    //console.log("framize", Date.now()-time);  time = Date.now();

    var cmap = {},
        plte = [],
        inds = [];

    if (ps != 0) {
        var nbufs = [];
        for (var i = 0; i < frms.length; i++) nbufs.push(frms[i].img.buffer);

        var abuf = UPNG.encode.concatRGBA(nbufs),
            qres = UPNG.quantize(abuf, ps);

        for (var i = 0; i < qres.plte.length; i++) plte.push(qres.plte[i].est.rgba);

        var cof = 0;
        for (var i = 0; i < frms.length; i++) {
            var frm = frms[i],
                bln = frm.img.length,
                ind = new Uint8Array(qres.inds.buffer, cof >> 2, bln >> 2);
            inds.push(ind);
            var bb = new Uint8Array(qres.abuf, cof, bln);

            //console.log(frm.img, frm.width, frm.height);
            //var time = Date.now();
            if (dither) UPNG.encode.dither(frm.img, frm.rect.width, frm.rect.height, plte, bb, ind);
            //console.log(Date.now()-time);
            frm.img.set(bb);
            cof += bln;
        }

        //console.log("quantize", Date.now()-time);  time = Date.now();
    } else {
        // what if ps==0, but there are <=256 colors?  we still need to detect, if the palette could be used
        for (var j = 0; j < frms.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame
            var frm = frms[j],
                img32 = new Uint32Array(frm.img.buffer),
                nw = frm.rect.width,
                ilen = img32.length;
            var ind = new Uint8Array(ilen);
            inds.push(ind);
            for (var i = 0; i < ilen; i++) {
                var c = img32[i];
                if (i != 0 && c == img32[i - 1]) ind[i] = ind[i - 1];
                else if (i > nw && c == img32[i - nw]) ind[i] = ind[i - nw];
                else {
                    var cmc = cmap[c];
                    if (cmc == null) {
                        cmap[c] = cmc = plte.length;
                        plte.push(c);
                        if (plte.length >= 300) break;
                    }
                    ind[i] = cmc;
                }
            }
        }
        //console.log("make palette", Date.now()-time);  time = Date.now();
    }

    var cc = plte.length; //console.log("colors:",cc);
    if (cc <= 256 && forbidPlte == false) {
        if (cc <= 2) depth = 1;
        else if (cc <= 4) depth = 2;
        else if (cc <= 16) depth = 4;
        else depth = 8;
        depth = Math.max(depth, minBits);
    }

    for (var j = 0; j < frms.length; j++) {
        var frm = frms[j],
            nx = frm.rect.x,
            ny = frm.rect.y,
            nw = frm.rect.width,
            nh = frm.rect.height;
        var cimg = frm.img,
            cimg32 = new Uint32Array(cimg.buffer);
        var bpl = 4 * nw,
            bpp = 4;
        if (cc <= 256 && forbidPlte == false) {
            bpl = Math.ceil(depth * nw / 8);
            var nimg = new Uint8Array(bpl * nh);
            var inj = inds[j];
            for (var y = 0; y < nh; y++) {
                var i = y * bpl,
                    ii = y * nw;
                if (depth == 8)
                    for (var x = 0; x < nw; x++) nimg[i + (x)] = (inj[ii + x]);
                else if (depth == 4)
                    for (var x = 0; x < nw; x++) nimg[i + (x >> 1)] |= (inj[ii + x] << (4 - (x & 1) * 4));
                else if (depth == 2)
                    for (var x = 0; x < nw; x++) nimg[i + (x >> 2)] |= (inj[ii + x] << (6 - (x & 3) * 2));
                else if (depth == 1)
                    for (var x = 0; x < nw; x++) nimg[i + (x >> 3)] |= (inj[ii + x] << (7 - (x & 7) * 1));
            }
            cimg = nimg;
            ctype = 3;
            bpp = 1;
        } else if (gotAlpha == false && frms.length == 1) { // some next "reduced" frames may contain alpha for blending
            var nimg = new Uint8Array(nw * nh * 3),
                area = nw * nh;
            for (var i = 0; i < area; i++) {
                var ti = i * 3,
                    qi = i * 4;
                nimg[ti] = cimg[qi];
                nimg[ti + 1] = cimg[qi + 1];
                nimg[ti + 2] = cimg[qi + 2];
            }
            cimg = nimg;
            ctype = 2;
            bpp = 3;
            bpl = 3 * nw;
        }
        frm.img = cimg;
        frm.bpl = bpl;
        frm.bpp = bpp;
    }
    //console.log("colors => palette indices", Date.now()-time);  time = Date.now();

    return {
        ctype: ctype,
        depth: depth,
        plte: plte,
        frames: frms
    };
}
UPNG.encode.framize = function (bufs, w, h, alwaysBlend, evenCrd, forbidPrev) {
    /*  DISPOSE
        - 0 : no change
    	- 1 : clear to transparent
    	- 2 : retstore to content before rendering (previous frame disposed)
    	BLEND
    	- 0 : replace
    	- 1 : blend
    */
    var frms = [];
    for (var j = 0; j < bufs.length; j++) {
        var cimg = new Uint8Array(bufs[j]),
            cimg32 = new Uint32Array(cimg.buffer);
        var nimg;

        var nx = 0,
            ny = 0,
            nw = w,
            nh = h,
            blend = alwaysBlend ? 1 : 0;
        if (j != 0) {
            var tlim = (forbidPrev || alwaysBlend || j == 1 || frms[j - 2].dispose != 0) ? 1 : 2,
                tstp = 0,
                tarea = 1e9;
            for (var it = 0; it < tlim; it++) {
                var pimg = new Uint8Array(bufs[j - 1 - it]),
                    p32 = new Uint32Array(bufs[j - 1 - it]);
                var mix = w,
                    miy = h,
                    max = -1,
                    may = -1;
                for (var y = 0; y < h; y++)
                    for (var x = 0; x < w; x++) {
                        var i = y * w + x;
                        if (cimg32[i] != p32[i]) {
                            if (x < mix) mix = x;
                            if (x > max) max = x;
                            if (y < miy) miy = y;
                            if (y > may) may = y;
                        }
                    }
                if (max == -1) mix = miy = max = may = 0;
                if (evenCrd) {
                    if ((mix & 1) == 1) mix--;
                    if ((miy & 1) == 1) miy--;
                }
                var sarea = (max - mix + 1) * (may - miy + 1);
                if (sarea < tarea) {
                    tarea = sarea;
                    tstp = it;
                    nx = mix;
                    ny = miy;
                    nw = max - mix + 1;
                    nh = may - miy + 1;
                }
            }

            // alwaysBlend: pokud zjistím, že blendit nelze, nastavím předchozímu snímku dispose=1. Zajistím, aby obsahoval můj obdélník.
            var pimg = new Uint8Array(bufs[j - 1 - tstp]);
            if (tstp == 1) frms[j - 1].dispose = 2;

            nimg = new Uint8Array(nw * nh * 4);
            UPNG._copyTile(pimg, w, h, nimg, nw, nh, -nx, -ny, 0);

            blend = UPNG._copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 3) ? 1 : 0;
            if (blend == 1) UPNG.encode._prepareDiff(cimg, w, h, nimg, {
                x: nx,
                y: ny,
                width: nw,
                height: nh
            });
            else UPNG._copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 0);
            //UPNG._copyTile(cimg,w,h, nimg,nw,nh, -nx,-ny, blend==1?2:0);
        } else nimg = cimg.slice(0); // img may be rewritten further ... don't rewrite input

        frms.push({
            rect: {
                x: nx,
                y: ny,
                width: nw,
                height: nh
            },
            img: nimg,
            blend: blend,
            dispose: 0
        });
    }


    if (alwaysBlend)
        for (var j = 0; j < frms.length; j++) {
            var frm = frms[j];
            if (frm.blend == 1) continue;
            var r0 = frm.rect,
                r1 = frms[j - 1].rect
            var miX = Math.min(r0.x, r1.x),
                miY = Math.min(r0.y, r1.y);
            var maX = Math.max(r0.x + r0.width, r1.x + r1.width),
                maY = Math.max(r0.y + r0.height, r1.y + r1.height);
            var r = {
                x: miX,
                y: miY,
                width: maX - miX,
                height: maY - miY
            };

            frms[j - 1].dispose = 1;
            if (j - 1 != 0)
                UPNG.encode._updateFrame(bufs, w, h, frms, j - 1, r, evenCrd);
            UPNG.encode._updateFrame(bufs, w, h, frms, j, r, evenCrd);
        }
    var area = 0;
    if (bufs.length != 1)
        for (var i = 0; i < frms.length; i++) {
            var frm = frms[i];
            area += frm.rect.width * frm.rect.height;
            //if(i==0 || frm.blend!=1) continue;
            //var ob = new Uint8Array(
            //console.log(frm.blend, frm.dispose, frm.rect);
        }
    //if(area!=0) console.log(area);
    return frms;
}
UPNG.encode._updateFrame = function (bufs, w, h, frms, i, r, evenCrd) {
    var U8 = Uint8Array,
        U32 = Uint32Array;
    var pimg = new U8(bufs[i - 1]),
        pimg32 = new U32(bufs[i - 1]),
        nimg = i + 1 < bufs.length ? new U8(bufs[i + 1]) : null;
    var cimg = new U8(bufs[i]),
        cimg32 = new U32(cimg.buffer);

    var mix = w,
        miy = h,
        max = -1,
        may = -1;
    for (var y = 0; y < r.height; y++)
        for (var x = 0; x < r.width; x++) {
            var cx = r.x + x,
                cy = r.y + y;
            var j = cy * w + cx,
                cc = cimg32[j];
            // no need to draw transparency, or to dispose it. Or, if writing the same color and the next one does not need transparency.
            if (cc == 0 || (frms[i - 1].dispose == 0 && pimg32[j] == cc && (nimg == null || nimg[j * 4 + 3] != 0)) /**/ ) {} else {
                if (cx < mix) mix = cx;
                if (cx > max) max = cx;
                if (cy < miy) miy = cy;
                if (cy > may) may = cy;
            }
        }
    if (max == -1) mix = miy = max = may = 0;
    if (evenCrd) {
        if ((mix & 1) == 1) mix--;
        if ((miy & 1) == 1) miy--;
    }
    r = {
        x: mix,
        y: miy,
        width: max - mix + 1,
        height: may - miy + 1
    };

    var fr = frms[i];
    fr.rect = r;
    fr.blend = 1;
    fr.img = new Uint8Array(r.width * r.height * 4);
    if (frms[i - 1].dispose == 0) {
        UPNG._copyTile(pimg, w, h, fr.img, r.width, r.height, -r.x, -r.y, 0);
        UPNG.encode._prepareDiff(cimg, w, h, fr.img, r);
        //UPNG._copyTile(cimg,w,h, fr.img,r.width,r.height, -r.x,-r.y, 2);
    } else
        UPNG._copyTile(cimg, w, h, fr.img, r.width, r.height, -r.x, -r.y, 0);
}
UPNG.encode._prepareDiff = function (cimg, w, h, nimg, rec) {
    UPNG._copyTile(cimg, w, h, nimg, rec.width, rec.height, -rec.x, -rec.y, 2);
    /*
    var n32 = new Uint32Array(nimg.buffer);
    var og = new Uint8Array(rec.width*rec.height*4), o32 = new Uint32Array(og.buffer);
    UPNG._copyTile(cimg,w,h, og,rec.width,rec.height, -rec.x,-rec.y, 0);
    for(var i=4; i<nimg.length; i+=4) {
    	if(nimg[i-1]!=0 && nimg[i+3]==0 && o32[i>>>2]==o32[(i>>>2)-1]) {
    		n32[i>>>2]=o32[i>>>2];
    		//var j = i, c=p32[(i>>>2)-1];
    		//while(p32[j>>>2]==c) {  n32[j>>>2]=c;  j+=4;  }
    	}
    }
    for(var i=nimg.length-8; i>0; i-=4) {
    	if(nimg[i+7]!=0 && nimg[i+3]==0 && o32[i>>>2]==o32[(i>>>2)+1]) {
    		n32[i>>>2]=o32[i>>>2];
    		//var j = i, c=p32[(i>>>2)-1];
    		//while(p32[j>>>2]==c) {  n32[j>>>2]=c;  j+=4;  }
    	}
    }*/
}

UPNG.encode._filterZero = function (img, h, bpp, bpl, data, filter, levelZero) {
    var fls = [],
        ftry = [0, 1, 2, 3, 4];
    if (filter != -1) ftry = [filter];
    else if (h * bpl > 500000 || bpp == 1) ftry = [0];
    var opts;
    if (levelZero) opts = {
        level: 0
    };


    var CMPR = (data.length > 10e6 && UZIP != null) ? UZIP : pako;

    var time = Date.now();
    for (var i = 0; i < ftry.length; i++) {
        for (var y = 0; y < h; y++) UPNG.encode._filterLine(data, img, y, bpl, bpp, ftry[i]);
        //var nimg = new Uint8Array(data.length);
        //var sz = UZIP.F.deflate(data, nimg);  fls.push(nimg.slice(0,sz));
        //var dfl = pako["deflate"](data), dl=dfl.length-4;
        //var crc = (dfl[dl+3]<<24)|(dfl[dl+2]<<16)|(dfl[dl+1]<<8)|(dfl[dl+0]<<0);
        //console.log(crc, UZIP.adler(data,2,data.length-6));
        fls.push(CMPR["deflate"](data, opts));
    }

    var ti, tsize = 1e9;
    for (var i = 0; i < fls.length; i++)
        if (fls[i].length < tsize) {
            ti = i;
            tsize = fls[i].length;
        }
    return fls[ti];
}
UPNG.encode._filterLine = function (data, img, y, bpl, bpp, type) {
    var i = y * bpl,
        di = i + y,
        paeth = UPNG.decode._paeth
    data[di] = type;
    di++;

    if (type == 0) {
        if (bpl < 500)
            for (var x = 0; x < bpl; x++) data[di + x] = img[i + x];
        else data.set(new Uint8Array(img.buffer, i, bpl), di);
    } else if (type == 1) {
        for (var x = 0; x < bpp; x++) data[di + x] = img[i + x];
        for (var x = bpp; x < bpl; x++) data[di + x] = (img[i + x] - img[i + x - bpp] + 256) & 255;
    } else if (y == 0) {
        for (var x = 0; x < bpp; x++) data[di + x] = img[i + x];

        if (type == 2)
            for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x];
        if (type == 3)
            for (var x = bpp; x < bpl; x++) data[di + x] = (img[i + x] - (img[i + x - bpp] >> 1) + 256) & 255;
        if (type == 4)
            for (var x = bpp; x < bpl; x++) data[di + x] = (img[i + x] - paeth(img[i + x - bpp], 0, 0) + 256) & 255;
    } else {
        if (type == 2) {
            for (var x = 0; x < bpl; x++) data[di + x] = (img[i + x] + 256 - img[i + x - bpl]) & 255;
        }
        if (type == 3) {
            for (var x = 0; x < bpp; x++) data[di + x] = (img[i + x] + 256 - (img[i + x - bpl] >> 1)) & 255;
            for (var x = bpp; x < bpl; x++) data[di + x] = (img[i + x] + 256 - ((img[i + x - bpl] + img[i + x - bpp]) >> 1)) & 255;
        }
        if (type == 4) {
            for (var x = 0; x < bpp; x++) data[di + x] = (img[i + x] + 256 - paeth(0, img[i + x - bpl], 0)) & 255;
            for (var x = bpp; x < bpl; x++) data[di + x] = (img[i + x] + 256 - paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl])) & 255;
        }
    }
}

UPNG.crc = {
    table: (function () {
        var tab = new Uint32Array(256);
        for (var n = 0; n < 256; n++) {
            var c = n;
            for (var k = 0; k < 8; k++) {
                if (c & 1) c = 0xedb88320 ^ (c >>> 1);
                else c = c >>> 1;
            }
            tab[n] = c;
        }
        return tab;
    })(),
    update: function (c, buf, off, len) {
        for (var i = 0; i < len; i++) c = UPNG.crc.table[(c ^ buf[off + i]) & 0xff] ^ (c >>> 8);
        return c;
    },
    crc: function (b, o, l) {
        return UPNG.crc.update(0xffffffff, b, o, l) ^ 0xffffffff;
    }
}


UPNG.quantize = function (abuf, ps) {
    var sb = new Uint8Array(abuf),
        tb = sb.slice(0),
        tb32 = new Uint32Array(tb.buffer);

    var KD = UPNG.quantize.getKDtree(tb, ps);
    var root = KD[0],
        leafs = KD[1];

    var planeDst = UPNG.quantize.planeDst;
    var len = sb.length;

    var inds = new Uint8Array(len >> 2),
        nd;
    if (sb.length < 20e6) // precise, but slow :(
        for (var i = 0; i < len; i += 4) {
            var r = sb[i] * (1 / 255),
                g = sb[i + 1] * (1 / 255),
                b = sb[i + 2] * (1 / 255),
                a = sb[i + 3] * (1 / 255);

            nd = UPNG.quantize.getNearest(root, r, g, b, a);
            inds[i >> 2] = nd.ind;
            tb32[i >> 2] = nd.est.rgba;
        }
    else
        for (var i = 0; i < len; i += 4) {
            var r = sb[i] * (1 / 255),
                g = sb[i + 1] * (1 / 255),
                b = sb[i + 2] * (1 / 255),
                a = sb[i + 3] * (1 / 255);

            nd = root;
            while (nd.left) nd = (planeDst(nd.est, r, g, b, a) <= 0) ? nd.left : nd.right;
            inds[i >> 2] = nd.ind;
            tb32[i >> 2] = nd.est.rgba;
        }
    return {
        abuf: tb.buffer,
        inds: inds,
        plte: leafs
    };
}

UPNG.quantize.getKDtree = function (nimg, ps, err) {
    if (err == null) err = 0.0001;
    var nimg32 = new Uint32Array(nimg.buffer);

    var root = {
        i0: 0,
        i1: nimg.length,
        bst: null,
        est: null,
        tdst: 0,
        left: null,
        right: null
    }; // basic statistic, extra statistic
    root.bst = UPNG.quantize.stats(nimg, root.i0, root.i1);
    root.est = UPNG.quantize.estats(root.bst);
    var leafs = [root];

    while (leafs.length < ps) {
        var maxL = 0,
            mi = 0;
        for (var i = 0; i < leafs.length; i++)
            if (leafs[i].est.L > maxL) {
                maxL = leafs[i].est.L;
                mi = i;
            }
        if (maxL < err) break;
        var node = leafs[mi];

        var s0 = UPNG.quantize.splitPixels(nimg, nimg32, node.i0, node.i1, node.est.e, node.est.eMq255);
        var s0wrong = (node.i0 >= s0 || node.i1 <= s0);
        //console.log(maxL, leafs.length, mi);
        if (s0wrong) {
            node.est.L = 0;
            continue;
        }


        var ln = {
            i0: node.i0,
            i1: s0,
            bst: null,
            est: null,
            tdst: 0,
            left: null,
            right: null
        };
        ln.bst = UPNG.quantize.stats(nimg, ln.i0, ln.i1);
        ln.est = UPNG.quantize.estats(ln.bst);
        var rn = {
            i0: s0,
            i1: node.i1,
            bst: null,
            est: null,
            tdst: 0,
            left: null,
            right: null
        };
        rn.bst = {
            R: [],
            m: [],
            N: node.bst.N - ln.bst.N
        };
        for (var i = 0; i < 16; i++) rn.bst.R[i] = node.bst.R[i] - ln.bst.R[i];
        for (var i = 0; i < 4; i++) rn.bst.m[i] = node.bst.m[i] - ln.bst.m[i];
        rn.est = UPNG.quantize.estats(rn.bst);

        node.left = ln;
        node.right = rn;
        leafs[mi] = ln;
        leafs.push(rn);
    }
    leafs.sort(function (a, b) {
        return b.bst.N - a.bst.N;
    });
    for (var i = 0; i < leafs.length; i++) leafs[i].ind = i;
    return [root, leafs];
}

UPNG.quantize.getNearest = function (nd, r, g, b, a) {
    if (nd.left == null) {
        nd.tdst = UPNG.quantize.dist(nd.est.q, r, g, b, a);
        return nd;
    }
    var planeDst = UPNG.quantize.planeDst(nd.est, r, g, b, a);

    var node0 = nd.left,
        node1 = nd.right;
    if (planeDst > 0) {
        node0 = nd.right;
        node1 = nd.left;
    }

    var ln = UPNG.quantize.getNearest(node0, r, g, b, a);
    if (ln.tdst <= planeDst * planeDst) return ln;
    var rn = UPNG.quantize.getNearest(node1, r, g, b, a);
    return rn.tdst < ln.tdst ? rn : ln;
}
UPNG.quantize.planeDst = function (est, r, g, b, a) {
    var e = est.e;
    return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq;
}
UPNG.quantize.dist = function (q, r, g, b, a) {
    var d0 = r - q[0],
        d1 = g - q[1],
        d2 = b - q[2],
        d3 = a - q[3];
    return d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;
}

UPNG.quantize.splitPixels = function (nimg, nimg32, i0, i1, e, eMq) {
    var vecDot = UPNG.quantize.vecDot;
    i1 -= 4;
    var shfs = 0;
    while (i0 < i1) {
        while (vecDot(nimg, i0, e) <= eMq) i0 += 4;
        while (vecDot(nimg, i1, e) > eMq) i1 -= 4;
        if (i0 >= i1) break;

        var t = nimg32[i0 >> 2];
        nimg32[i0 >> 2] = nimg32[i1 >> 2];
        nimg32[i1 >> 2] = t;

        i0 += 4;
        i1 -= 4;
    }
    while (vecDot(nimg, i0, e) > eMq) i0 -= 4;
    return i0 + 4;
}
UPNG.quantize.vecDot = function (nimg, i, e) {
    return nimg[i] * e[0] + nimg[i + 1] * e[1] + nimg[i + 2] * e[2] + nimg[i + 3] * e[3];
}
UPNG.quantize.stats = function (nimg, i0, i1) {
    var R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    var m = [0, 0, 0, 0];
    var N = (i1 - i0) >> 2;
    for (var i = i0; i < i1; i += 4) {
        var r = nimg[i] * (1 / 255),
            g = nimg[i + 1] * (1 / 255),
            b = nimg[i + 2] * (1 / 255),
            a = nimg[i + 3] * (1 / 255);
        //var r = nimg[i], g = nimg[i+1], b = nimg[i+2], a = nimg[i+3];
        m[0] += r;
        m[1] += g;
        m[2] += b;
        m[3] += a;

        R[0] += r * r;
        R[1] += r * g;
        R[2] += r * b;
        R[3] += r * a;
        R[5] += g * g;
        R[6] += g * b;
        R[7] += g * a;
        R[10] += b * b;
        R[11] += b * a;
        R[15] += a * a;
    }
    R[4] = R[1];
    R[8] = R[2];
    R[9] = R[6];
    R[12] = R[3];
    R[13] = R[7];
    R[14] = R[11];

    return {
        R: R,
        m: m,
        N: N
    };
}
UPNG.quantize.estats = function (stats) {
    var R = stats.R,
        m = stats.m,
        N = stats.N;

    // when all samples are equal, but N is large (millions), the Rj can be non-zero ( 0.0003.... - precission error)
    var m0 = m[0],
        m1 = m[1],
        m2 = m[2],
        m3 = m[3],
        iN = (N == 0 ? 0 : 1 / N);
    var Rj = [
        R[0] - m0 * m0 * iN, R[1] - m0 * m1 * iN, R[2] - m0 * m2 * iN, R[3] - m0 * m3 * iN,
        R[4] - m1 * m0 * iN, R[5] - m1 * m1 * iN, R[6] - m1 * m2 * iN, R[7] - m1 * m3 * iN,
        R[8] - m2 * m0 * iN, R[9] - m2 * m1 * iN, R[10] - m2 * m2 * iN, R[11] - m2 * m3 * iN,
        R[12] - m3 * m0 * iN, R[13] - m3 * m1 * iN, R[14] - m3 * m2 * iN, R[15] - m3 * m3 * iN
    ];

    var A = Rj,
        M = UPNG.M4;
    var b = [Math.random(), Math.random(), Math.random(), Math.random()],
        mi = 0,
        tmi = 0;

    if (N != 0)
        for (var i = 0; i < 16; i++) {
            b = M.multVec(A, b);
            tmi = Math.sqrt(M.dot(b, b));
            b = M.sml(1 / tmi, b);
            if (i != 0 && Math.abs(tmi - mi) < 1e-9) break;
            mi = tmi;
        }
    //b = [0,0,1,0];  mi=N;
    var q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN];
    var eMq255 = M.dot(M.sml(255, q), b);

    return {
        Cov: Rj,
        q: q,
        e: b,
        L: mi,
        eMq255: eMq255,
        eMq: M.dot(b, q),
        rgba: (((Math.round(255 * q[3]) << 24) | (Math.round(255 * q[2]) << 16) | (Math.round(255 * q[1]) << 8) | (Math.round(255 * q[0]) << 0)) >>> 0)
    };
}
UPNG.M4 = {
    multVec: function (m, v) {
        return [
            m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3],
            m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3],
            m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3],
            m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]
        ];
    },
    dot: function (x, y) {
        return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3];
    },
    sml: function (a, y) {
        return [a * y[0], a * y[1], a * y[2], a * y[3]];
    }
}

UPNG.encode.concatRGBA = function (bufs) {
    var tlen = 0;
    for (var i = 0; i < bufs.length; i++) tlen += bufs[i].byteLength;
    var nimg = new Uint8Array(tlen),
        noff = 0;
    for (var i = 0; i < bufs.length; i++) {
        var img = new Uint8Array(bufs[i]),
            il = img.length;
        for (var j = 0; j < il; j += 4) {
            var r = img[j],
                g = img[j + 1],
                b = img[j + 2],
                a = img[j + 3];
            if (a == 0) r = g = b = 0;
            nimg[noff + j] = r;
            nimg[noff + j + 1] = g;
            nimg[noff + j + 2] = b;
            nimg[noff + j + 3] = a;
        }
        noff += il;
    }
    return nimg.buffer;
}

UPNG.encode.dither = function (sb, w, h, plte, tb, oind) {

    function addErr(er, tg, ti, f) {
        tg[ti] += (er[0] * f) >> 4;
        tg[ti + 1] += (er[1] * f) >> 4;
        tg[ti + 2] += (er[2] * f) >> 4;
        tg[ti + 3] += (er[3] * f) >> 4;
    }

    function N(x) {
        return Math.max(0, Math.min(255, x));
    }

    function D(a, b) {
        var dr = a[0] - b[0],
            dg = a[1] - b[1],
            db = a[2] - b[2],
            da = a[3] - b[3];
        return (dr * dr + dg * dg + db * db + da * da);
    }


    var pc = plte.length,
        nplt = [],
        rads = [];
    for (var i = 0; i < pc; i++) {
        var c = plte[i];
        nplt.push([((c >>> 0) & 255), ((c >>> 8) & 255), ((c >>> 16) & 255), ((c >>> 24) & 255)]);
    }
    for (var i = 0; i < pc; i++) {
        var ne = 0xffffffff,
            ni = 0;
        for (var j = 0; j < pc; j++) {
            var ce = D(nplt[i], nplt[j]);
            if (j != i && ce < ne) {
                ne = ce;
                ni = j;
            }
        }
        var hd = Math.sqrt(ne) / 2;
        rads[i] = ~~(hd * hd);
    }

    var tb32 = new Uint32Array(tb.buffer);
    var err = new Int16Array(w * h * 4);

    for (var y = 0; y < h; y++) {
        for (var x = 0; x < w; x++) {
            var i = (y * w + x) * 4;

            var cc = [N(sb[i] + err[i]), N(sb[i + 1] + err[i + 1]), N(sb[i + 2] + err[i + 2]), N(sb[i + 3] + err[i + 3])];

            var ni = 0,
                nd = 0xffffff;
            for (var j = 0; j < pc; j++) {
                var cd = D(cc, nplt[j]);
                if (cd < nd) {
                    nd = cd;
                    ni = j;
                }
            }

            //ni = oind[i>>2];
            var nc = nplt[ni];
            var er = [cc[0] - nc[0], cc[1] - nc[1], cc[2] - nc[2], cc[3] - nc[3]];

            //addErr(er, err, i+4, 16);

            //*
            if (x != w - 1) addErr(er, err, i + 4, 7);
            if (y != h - 1) {
                if (x != 0) addErr(er, err, i + 4 * w - 4, 3);
                addErr(er, err, i + 4 * w, 5);
                if (x != w - 1) addErr(er, err, i + 4 * w + 4, 1); //*/
            }

            oind[i >> 2] = ni;
            tb32[i >> 2] = plte[ni];
        }
    }
}


module.exports = UPNG

  • 再将png数据转化为Base64数据,所使用的工具包Base64Util:
 export default {
 
arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
	binary += String.fromCharCode(bytes[i]);
}


var btoa = function(string) {
	var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    string = String(string);
    var bitmap, a, b, c,
        result = "", i = 0,
        rest = string.length % 3; // To determine the final padding

    for (; i < string.length;) {
        if ((a = string.charCodeAt(i++)) > 255
                || (b = string.charCodeAt(i++)) > 255
                || (c = string.charCodeAt(i++)) > 255)
            throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");

        bitmap = (a << 16) | (b << 8) | c;
        result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63)
                + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
    }

    // If there's need of padding, replace the last 'A's with equal signs
    return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
}


return "data:image/png;base64," + btoa(binary);
}

}

4.2 控制帧数据采集频率

  • 特别注意:对于微信的这个数据帧采集接口onCameraFrame,它是处于一个以60ms采集一次用户的数据的频率运行的,很多时候我们用不到这么高的频率!我们可以通过setInteerval定时器来控制摄像头采集用户数据的频率。
let task = setInterval(function() {
var timeStart = Date.now();
//在此处处理store[0](图像的数据);
// store.shift();
var frame = that.frameQueue.shift()
console.log("开始运行===",frame,that.flag);
that.flag = true;

if(frame != undefined){
	
	let pngData = UPNG.encode([frame.data], frame.width, frame.height),  
		base64 = Base64Util.arrayBufferToBase64(pngData)
														
	uni.request({
		url: 'http://127.0.0.1:8000/miniapp/faceEngine/faceLogin' ,
		method: 'post',
		data: {openId:uni.getStorageSync("openId"),base64Img:base64} ,
		dataType:'json',
		header: {
			   'content-type':'application/json'//自定义请求头信息
			},
		success:(res)=>{
				console.log("====执行成功===",res)
				if(res.statusCode != undefined){
					clearInterval(task)
					that.isAuthCamera = false
					uni.navigateTo({
						url:'./login'
					})
				}
		},
		fail:(err)=>{
				console.log("====执行失败===",err)
				clearInterval(task)
				that.isAuthCamera = false
				uni.navigateTo({
					url:'./login'
				})
		}
	})
}

五、实时人脸采集功能实现

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cookie 和 Session的区别

文章目录时间&#xff1a;2023年3月23日第一&#xff1a;什么是 Cookie 和 Session ?什么是 Cookie什么是 Session第二&#xff1a;Cookie 和 Session 有什么不同&#xff1f;第三&#xff1a;为什么需要 Cookie 和 Session&#xff0c;他们有什么关联&#xff1f;第四&#x…

【Java基础】一个Java文件可以有多个类(外部类、内部类)

目录一、在一个Java文件中定义多个类&#xff08;外部类&#xff09;1.问题描述及解答2.总结二、在一个Java文件定义多个类&#xff08;内部类&#xff09;1.成员内部类和匿名内部类1.1 成员内部类1.2 匿名内部类2.深入理解内部类3.内部类的使用场景和好处一、在一个Java文件中…

小米商城官网(登录页,首页,详情页,我的购物车页,我的订单页,确认订单页)HTML+CSS+JS

文章目录前言一、登录页二、首页三、我的购物车页四、我的订单页五、确认订单页六、详情页七、整体结构和效果图总结前言 仿小米商城官网项目是本人实训内容&#xff0c;实训老师带着做的首页和登录页&#xff0c;本人在此基础上加入了我的购物车页&#xff0c;我的订单页&…

vue兼容ie11(@babel/polyfill、core-js@3两种方式)

Babel介绍&#xff1a; Babel是一个JavaScript编译器&#xff0c;主要用于将ECMAScript 2015代码转换为当前和旧浏览器或环境中向后兼容的JavaScript版本。要是通过核心依赖core-js2&#xff0c;主要是通过核心依赖core-js2来完成对应浏览器不支持的新的全局和实例api的添加 …

Vue2 实现图片的拖拽、缩放、旋转

本文是基于vue2 实现图片的拖拽、旋转、鼠标滚动放大缩小等功能。 效果图 分步骤实现 在这里看下 拖拽、旋转、缩放的几个方法 1.获取图片的实际宽高 getImgSize(url) {return new Promise((resolve, reject) > {let imgObj new Image();imgObj.src url;imgObj.onload…

插槽(slot)的基本使用

插槽&#xff08;slot&#xff09;的基本使用 一、插槽的作用 在开发中&#xff0c;我们会经常封装一个个可复用的组件&#xff1a; 前面我们会通过props传递给组件一些数据&#xff0c;让组件来进行展示&#xff1b; 但是为了让这个组件具备更强的通用性&#xff0c;我们不能…

Ubuntu搭建web站点并发布公网访问【内网穿透】

文章目录前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名4.3 测试访问公网固定二级子域名前言 网&#xff1a;我们通常说的是互联网&#xff1b;站…

Vue3中使用vant(踩坑日记....)

我照着视频中老师教的方式去使用vant(和官网教程一样)&#xff0c;发现样式根本不起作用&#xff08;想截个图来着&#xff0c;但是vite热更新太厉害了&#xff0c;找不到了哈哈哈&#xff09;&#xff0c;然后又反复看了视频一遍&#xff0c;检查自己插件啥的而又没有安装好&a…

【TypeScript基础】TypeScript之常用类型(上)

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;【TypeScript专栏】 上篇文章大概了解了一下TypeScript&#xff0c;&#x1f449;&#x1f3fb;详细内容请阅读【TypeScript介绍】一文带你初步了解TypeScript 今天开始学习学习Type…

html实现用户注册页面(生日实现年月日的三级联动)

注册页面 期待效果如下&#xff1a; 初始代码实现&#xff1a; <!DOCTYPE html> <html> <head><title>注册页面</title> </head> <body> <!-- 表单&#xff0c;用于提交按钮&#xff0c;隐式提交 --> <form action"…

后台管理系统 - 页面布局设计

前端的中后台管理系统相比于其他普通项目&#xff0c;从开发设计的角度来说有几点比较特殊&#xff1a; 一个是权限设计&#xff0c;具体实现可参考&#xff1a;传送门。一个是页面布局的设计&#xff0c;也是本文要说的。一个好的页面布局设计&#xff0c;无论是对于页面结构…

Java Web实现登录注册(超详细附代码)

Java Web实现登录注册&#xff08;超详细附代码&#xff09; 文章目录Java Web实现登录注册&#xff08;超详细附代码&#xff09;1.前言2.登录注册设计流程3.注册的数据流程4.登录的数据流程5.部分代码的展示5.1注册5.2登录6.总结1.前言 相信刚学Javaweb的小伙伴第一个接触的…

30个极致实用的谷歌浏览器插件,让你开发事半功倍

浏览器是开发人员最强大的工具。99% 的人不会 100% 地利用他们的浏览器。这里有 30 个浏览器扩展&#xff0c;如果你能合理充分利用将大大提升你的开发效率。1. WhatFont"WhatFont"&#xff0c;是一款帮助用户快速识别网页中字体信息的工具。当你在浏览网页时&#x…

vue|axios发送post请求详解

axios发送Post请求 首先我们要知道发送一个请求体的时候我们要明确发送的类型是什么&#xff0c;常见的类型有三种 form-dataapplication/jsonapplication/x-www-form-urlencoded 以上三种类型是进行数据传输的数据类型&#xff0c;我们知道在js代码中的对象都不是以上的三种…

react实现路由跳转

react通过路由实现页面跳转&#xff1a; ​ 函数式路由(withRouter)使用原生js方法实现路由功能。 ​ eg&#xff1a;export default withRouter(Home) Home是组件名称。 示例&#xff1a;class Home extends Component{<button onClick{this.goForward}>下一级</bu…

猿创征文|【C++游戏引擎Easy2D】炫酷动画来这学,位移动画构造函数让节点执行动画

&#x1f9db;‍♂️iecne个人主页&#xff1a;&#xff1a;iecne的学习日志 &#x1f4a1;每天关注iecne的作品&#xff0c;一起进步 &#x1f4aa;学C必看iecne 本文专栏&#xff1a;【C游戏引擎】. &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; ✨前…

from origin ‘null‘ has been blocked by CORS policy: Cross origin requests are only supported for ...

跨源资源请求问题解决方案 问题描述&#xff1a;当我们在vsCode中使用 open in brower插件打开html文件文件时&#xff0c;就会报错 xxx已被CORS策略阻止&#xff0c;引入的资源还会失效。 解决办法1 :Live Server插件 在vsCode的插件市场中搜索并安装Live Server插件&#x…

Axios

Axios是Ajax的一个框架&#xff0c;简化Ajax操作 一、基本操作 Axios执行Ajax的操作&#xff1a; 1. 添加axios的js文件 <script language"JavaScript" src"script/axios.min.js"></script> 2.1 客户端向服务端异步发送普通参数值 格式&am…

vue-router报错: uncaught error during route navigation

bug场景&#xff1a; H5项目中,浏览器流程走的通,ios流程走得通,安卓机有的可以有的不行 报错信息&#xff1a; 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 例如&#xff1a;数据传输过程中数据不时出现丢失的情况&#xff0c;偶尔会丢失一部分数据 APP 中接收数…

尚融宝06-ECMAScript基本介绍和使用

目录 一、ECMAScript 1、ECMA 2、ECMAScript 3、什么是 ECMA-262 4、ECMA-262 历史 5、ECMAScript 和 JavaScript 的关系 二、基本语法 1、let声明变量 2、const声明常量 3、解构赋值 4、模板字符串 5、声明对象简写 6、定义方法简写 7、参数的默认值 8、对象拓…