下拉刷新
OneFile
源码
pico200 行实现的面部识别库
作者 Nenad Markuš·主语言 JavaScript·有依赖·2.9k 次查看
访问复制
/* This library is released under the MIT license, see https://github.com/nenadmarkus/picojs */ pico = {} pico.unpack_cascade = function(bytes) { // const dview = new DataView(new ArrayBuffer(4)); /* we skip the first 8 bytes of the cascade file (cascade version number and some data used during the learning process) */ let p = 8; /* read the depth (size) of each tree first: a 32-bit signed integer */ dview.setUint8(0, bytes[p+0]), dview.setUint8(1, bytes[p+1]), dview.setUint8(2, bytes[p+2]), dview.setUint8(3, bytes[p+3]); const tdepth = dview.getInt32(0, true); p = p + 4 /* next, read the number of trees in the cascade: another 32-bit signed integer */ dview.setUint8(0, bytes[p+0]), dview.setUint8(1, bytes[p+1]), dview.setUint8(2, bytes[p+2]), dview.setUint8(3, bytes[p+3]); const ntrees = dview.getInt32(0, true); p = p + 4 /* read the actual trees and cascade thresholds */ const tcodes_ls = []; const tpreds_ls = []; const thresh_ls = []; for(let t=0; t<ntrees; ++t) { // read the binary tests placed in internal tree nodes Array.prototype.push.apply(tcodes_ls, [0, 0, 0, 0]); Array.prototype.push.apply(tcodes_ls, bytes.slice(p, p+4*Math.pow(2, tdepth)-4)); p = p + 4*Math.pow(2, tdepth)-4; // read the prediction in the leaf nodes of the tree for(let i=0; i<Math.pow(2, tdepth); ++i) { dview.setUint8(0, bytes[p+0]), dview.setUint8(1, bytes[p+1]), dview.setUint8(2, bytes[p+2]), dview.setUint8(3, bytes[p+3]); tpreds_ls.push(dview.getFloat32(0, true)); p = p + 4; } // read the threshold dview.setUint8(0, bytes[p+0]), dview.setUint8(1, bytes[p+1]), dview.setUint8(2, bytes[p+2]), dview.setUint8(3, bytes[p+3]); thresh_ls.push(dview.getFloat32(0, true)); p = p + 4; } const tcodes = new Int8Array(tcodes_ls); const tpreds = new Float32Array(tpreds_ls); const thresh = new Float32Array(thresh_ls); /* construct the classification function from the read data */ function classify_region(r, c, s, pixels, ldim) { r = 256*r; c = 256*c; let root = 0; let o = 0.0; const pow2tdepth = Math.pow(2, tdepth) >> 0; // '>>0' transforms this number to int for(let i=0; i<ntrees; ++i) { let idx = 1; for(let j=0; j<tdepth; ++j) // we use '>> 8' here to perform an integer division: this seems important for performance idx = 2*idx + (pixels[((r+tcodes[root + 4*idx + 0]*s) >> 8)*ldim+((c+tcodes[root + 4*idx + 1]*s) >> 8)]<=pixels[((r+tcodes[root + 4*idx + 2]*s) >> 8)*ldim+((c+tcodes[root + 4*idx + 3]*s) >> 8)]); o = o + tpreds[pow2tdepth*i + idx-pow2tdepth]; if(o<=thresh[i]) return -1; root += 4*pow2tdepth; } return o - thresh[ntrees-1]; } /* we're done */ return classify_region; } pico.run_cascade = function(image, classify_region, params) { const pixels = image.pixels; const nrows = image.nrows; const ncols = image.ncols; const ldim = image.ldim; const shiftfactor = params.shiftfactor; const minsize = params.minsize; const maxsize = params.maxsize; const scalefactor = params.scalefactor; let scale = minsize; const detections = []; while(scale<=maxsize) { const step = Math.max(shiftfactor*scale, 1) >> 0; // '>>0' transforms this number to int const offset = (scale/2 + 1) >> 0; for(let r=offset; r<=nrows-offset; r+=step) for(let c=offset; c<=ncols-offset; c+=step) { const q = classify_region(r, c, scale, pixels, ldim); if (q > 0.0) detections.push([r, c, scale, q]); } scale = scale*scalefactor; } return detections; } pico.cluster_detections = function(dets, iouthreshold) { /* sort detections by their score */ dets = dets.sort(function(a, b) { return b[3] - a[3]; }); /* this helper function calculates the intersection over union for two detections */ function calculate_iou(det1, det2) { // unpack the position and size of each detection const r1=det1[0], c1=det1[1], s1=det1[2]; const r2=det2[0], c2=det2[1], s2=det2[2]; // calculate detection overlap in each dimension const overr = Math.max(0, Math.min(r1+s1/2, r2+s2/2) - Math.max(r1-s1/2, r2-s2/2)); const overc = Math.max(0, Math.min(c1+s1/2, c2+s2/2) - Math.max(c1-s1/2, c2-s2/2)); // calculate and return IoU return overr*overc/(s1*s1+s2*s2-overr*overc); } /* do clustering through non-maximum suppression */ const assignments = new Array(dets.length).fill(0); const clusters = []; for(let i=0; i<dets.length; ++i) { // is this detection assigned to a cluster? if(assignments[i]==0) { // it is not: // now we make a cluster out of it and see whether some other detections belong to it let r=0.0, c=0.0, s=0.0, q=0.0, n=0; for(let j=i; j<dets.length; ++j) if(calculate_iou(dets[i], dets[j])>iouthreshold) { assignments[j] = 1; r = r + dets[j][0]; c = c + dets[j][1]; s = s + dets[j][2]; q = q + dets[j][3]; n = n + 1; } // make a cluster representative clusters.push([r/n, c/n, s/n, q]); } } return clusters; } pico.instantiate_detection_memory = function(size) { /* initialize a circular buffer of `size` elements */ let n = 0; const memory = []; for(let i=0; i<size; ++i) memory.push([]); /* build a function that: (1) inserts the current frame's detections into the buffer; (2) merges all detections from the last `size` frames and returns them */ function update_memory(dets) { memory[n] = dets; n = (n+1)%memory.length; dets = []; for(i=0; i<memory.length; ++i) dets = dets.concat(memory[i]); // return dets; } /* we're done */ return update_memory; }