mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-05 16:00:01 +01:00
Compare commits
83 Commits
7474088ba2
...
e66e949ea5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e66e949ea5 | ||
|
|
b78390c2ab | ||
|
|
4d4cca8618 | ||
|
|
244f4b1f6b | ||
|
|
ea469bb2fe | ||
|
|
4a7b702b95 | ||
|
|
2b2a2d2877 | ||
|
|
1618a97b82 | ||
|
|
fcb860880a | ||
|
|
ca0467f0ed | ||
|
|
dc4a0066bc | ||
|
|
8e0fe8b991 | ||
|
|
327ea2a4db | ||
|
|
c487f27199 | ||
|
|
3f3ce37d79 | ||
|
|
e89e58dd19 | ||
|
|
5ee7d36382 | ||
|
|
4f4e626642 | ||
|
|
6ec47b4f24 | ||
|
|
9082c36279 | ||
|
|
0c638a09cf | ||
|
|
a93b6988fa | ||
|
|
1bd3c3077d | ||
|
|
9c59330273 | ||
|
|
d3ca1b48bc | ||
|
|
cb2788f289 | ||
|
|
e7311f2cd7 | ||
|
|
4709f61ef1 | ||
|
|
403d2a66ec | ||
|
|
e5993dcca0 | ||
|
|
ebac90e567 | ||
|
|
9bcf21cb29 | ||
|
|
b6e2e7dd2e | ||
|
|
98ad0bb8cc | ||
|
|
6a4662e7b5 | ||
|
|
34e05bfaf1 | ||
|
|
7ffb3ea156 | ||
|
|
62a293b253 | ||
|
|
546733a345 | ||
|
|
61c04b8b88 | ||
|
|
04f1a4e5f3 | ||
|
|
95053fde54 | ||
|
|
b65bcb0e82 | ||
|
|
34123bcbbe | ||
|
|
1336e1d429 | ||
|
|
7045ef5f6c | ||
|
|
b578a2dd29 | ||
|
|
4f363f3cd1 | ||
|
|
1f4dbe939c | ||
|
|
1a6b679252 | ||
|
|
373b5c0c09 | ||
|
|
6221b9b02f | ||
|
|
e99c25357a | ||
|
|
fc5a0dd162 | ||
|
|
ab81615164 | ||
|
|
2117c7adcf | ||
|
|
2e0e842bc7 | ||
|
|
bf7b951410 | ||
|
|
c197e75fdc | ||
|
|
fe8d159722 | ||
|
|
1cfbe39ffc | ||
|
|
0015660318 | ||
|
|
12bdcbe59e | ||
|
|
a7eea14a16 | ||
|
|
df160386fc | ||
|
|
879791a57a | ||
|
|
dfa517ab51 | ||
|
|
d7341b0f30 | ||
|
|
ff923b28e2 | ||
|
|
81dcd9dcf0 | ||
|
|
06ee86b967 | ||
|
|
72e6d466e3 | ||
|
|
c48156a771 | ||
|
|
d3783b994e | ||
|
|
4e02d594e1 | ||
|
|
0e8b481aa3 | ||
|
|
163ea4fea9 | ||
|
|
0ce08941ad | ||
|
|
ba2bea6096 | ||
|
|
916e127e71 | ||
|
|
a6cc0c0e58 | ||
|
|
4840809e3d | ||
|
|
7896fc5e2c |
16
.github/workflows/tests.yml
vendored
16
.github/workflows/tests.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
|
||||
@@ -118,9 +118,9 @@ function ModuleName(options,UI) {
|
||||
];
|
||||
```
|
||||
### Running a browser-only module in node
|
||||
If your module has browser specific code or you are consuming a dependency which does the `gl-context` api. We designed this api especially for webl based modules but since it runs the module in a headless browser, ti supports all browser specific APIs.
|
||||
If your module has browser specific code or you are consuming a dependency which does the `gl-context` API. We designed this api especially for web-based modules but since it runs the module in a headless browser, it supports all browser specific APIs.
|
||||
|
||||
The api must be used in the following format
|
||||
The API must be used in the following format
|
||||
```js
|
||||
var step = this;
|
||||
|
||||
@@ -197,7 +197,7 @@ There are four events in all:
|
||||
* `UI.onComplete(options.step)` must be emitted whenever the output of a draw call
|
||||
is ready. An argument, that is the DataURL of the output image must be passed in.
|
||||
* `UI.onRemove(options.step)` is emitted automatically and the module should not emit it.
|
||||
* `UI.notify(msg,id)` must be emmited when a notification has to be produced.
|
||||
* `UI.notify(msg,id)` must be emitted when a notification has to be produced.
|
||||
|
||||
### Name and description
|
||||
|
||||
@@ -244,7 +244,7 @@ Also, A module may have output values. These must be defined as shown above.
|
||||
|
||||
### Progress reporting
|
||||
|
||||
The default "loading spinner" can be optionally overriden with a custom progress object to draw progress on the CLI, following is a basic module format for the same:
|
||||
The default "loading spinner" can be optionally overridden with a custom progress object to draw progress on the CLI, following is a basic module format for the same:
|
||||
|
||||
```js
|
||||
module.exports = function ModuleName(options,UI) {
|
||||
@@ -279,7 +279,7 @@ module.exports = function ModuleName(options,UI) {
|
||||
}
|
||||
```
|
||||
|
||||
The `progressObj` parameter of `draw()` is not consumed unless a custom progress bar needs to be drawn, for which this default spinner should be stopped with `progressObj.stop()` and image-sequencer is informed about the custom progress bar with `progressObj.overrideFlag = true;` following which this object can be overriden with custom progress object.
|
||||
The `progressObj` parameter of `draw()` is not consumed unless a custom progress bar needs to be drawn, for which this default spinner should be stopped with `progressObj.stop()` and image-sequencer is informed about the custom progress bar with `progressObj.overrideFlag = true;` following which this object can be overridden with custom progress object.
|
||||
|
||||
|
||||
### Module example
|
||||
@@ -292,7 +292,7 @@ For help integrating, please open an issue.
|
||||
|
||||
## Meta Module
|
||||
|
||||
IMAGE SEQUENCER supports "meta modules" -- modules made of other modules. The syntax and structure of these meta modules is very similar to standard modules. Sequencer can also genarate meta modules dynamically with the function `createMetaModule` which can be called in the following ways
|
||||
IMAGE SEQUENCER supports "meta modules" -- modules made of other modules. The syntax and structure of these meta modules is very similar to standard modules. Sequencer can also generate meta modules dynamically with the function `createMetaModule` which can be called in the following ways
|
||||
|
||||
```js
|
||||
|
||||
@@ -387,7 +387,7 @@ npx eslint <file path> --fix
|
||||
```
|
||||
Be sure to not include the angular brackets(<>).
|
||||
|
||||
Husky ensures automation of the above steps with git-hooks(eg. git add,git commit..). However we don't want to check and fix changes of the entire codebase with each commit and that the fixes made by eslint appear unstaged and require us to commit them again and that is where lint-staged helps.
|
||||
Husky ensures automation of the above steps with git-hooks(eg. git add, git commit..). However we don't want to check and fix changes of the entire codebase with each commit and that the fixes made by eslint appear unstaged and require us to commit them again and that is where lint-staged helps.
|
||||
|
||||
If we want `husky` to not verify the commit and push it anyway, use `git commit -m "message" --no-verify.`
|
||||
|
||||
@@ -479,3 +479,13 @@ The following shell scripts are present in the `scripts/` directory.
|
||||
This script is safe to use directly because it separately clones the repo in a temporary directory.
|
||||
|
||||
Arguments: None since it is a an *interactive* script, ie it asks the user for input.
|
||||
|
||||
****
|
||||
|
||||
# Comments
|
||||
|
||||
1. Methods must be described using [JSDoc comments](https://devdocs.io/jsdoc/)
|
||||
2. Misc code comments should be inline unless it is a long sentence.
|
||||
3. No use of continuous tenses, no pronouns.
|
||||
4. No redundant comments.
|
||||
5. Each comment should start with an uppercase letter and end with a full stop.
|
||||
|
||||
@@ -221,7 +221,6 @@
|
||||
|
||||
<footer>
|
||||
<hr style="margin:20px;">
|
||||
<center><button class="btn btn-default btn-sm" id="clear-cache">Clear offline cache</button></center>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Need Help?</h2>
|
||||
@@ -242,6 +241,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<p id="version-number-text">Loading Version Number</p>
|
||||
<center><button class="btn btn-primary btn-sm" id="clear-cache">Clear offline cache</button></center>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
10630
package-lock.json
generated
10630
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -49,7 +49,6 @@
|
||||
"expr-eval": "^2.0.2",
|
||||
"fisheyegl": "^0.1.2",
|
||||
"font-awesome": "~4.7.0",
|
||||
"geotiff": "^1.0.0-beta.6",
|
||||
"get-pixels": "~3.3.0",
|
||||
"gifshot": "^0.4.5",
|
||||
"glfx": "0.0.4",
|
||||
@@ -62,9 +61,9 @@
|
||||
"imagemin-pngquant": "^9.0.1",
|
||||
"istanbul": "^0.4.5",
|
||||
"jasmine": "^4.0.2",
|
||||
"jpegtran-bin": "^6.0.1",
|
||||
"jpegtran-bin": "^7.0.0",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsdom": "^20.0.0",
|
||||
"jspdf": "^2.1.1",
|
||||
"jsqr": "^1.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
@@ -97,21 +96,21 @@
|
||||
"grunt-contrib-uglify-es": "^3.3.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-text-replace": "^0.4.0",
|
||||
"husky": "^7.0.0",
|
||||
"husky": "^8.0.1",
|
||||
"image-filter-core": "~2.0.2",
|
||||
"image-filter-threshold": "~2.0.1",
|
||||
"jasmine-core": "^4.0.0",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
"jasmine-spec-reporter": "^7.0.0",
|
||||
"jest": "^27.0.1",
|
||||
"jest": "^29.0.0",
|
||||
"jest-puppeteer": "^6.0.0",
|
||||
"lint-staged": "^12.1.3",
|
||||
"lint-staged": "^13.0.0",
|
||||
"looks-same": "^7.0.0",
|
||||
"matchdep": "^2.0.0",
|
||||
"resemblejs": "^4.0.1",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^5.2.0",
|
||||
"tape-run": "^9.0.0",
|
||||
"tape-run": "^10.0.0",
|
||||
"uglify-es": "^3.3.7"
|
||||
},
|
||||
"husky": {
|
||||
|
||||
@@ -24,6 +24,7 @@ module.exports = {
|
||||
'dynamic': require('./modules/Dynamic'),
|
||||
'edge-detect': require('./modules/EdgeDetect'),
|
||||
'exposure': require('./modules/Exposure'),
|
||||
'face-detection': require('./modules/FaceDetection'),
|
||||
'flip-image': require('./modules/FlipImage'),
|
||||
'fisheye-gl': require('./modules/FisheyeGl'),
|
||||
'histogram': require('./modules/Histogram'),
|
||||
|
||||
278
src/modules/FaceDetection/FaceDetection.js
Normal file
278
src/modules/FaceDetection/FaceDetection.js
Normal file
@@ -0,0 +1,278 @@
|
||||
/* This library is released under the MIT license, see https://github.com/tehnokv/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)
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
function rgba_to_grayscale(rgba, nrows, ncols) {
|
||||
var gray = new Uint8Array(nrows * ncols);
|
||||
for(var r = 0; r < nrows; ++r)
|
||||
for(var c = 0; c < ncols; ++c)
|
||||
// gray = 0.2*red + 0.7*green + 0.1*blue
|
||||
gray[r * ncols + c] = (2 * rgba[r * 4 * ncols + 4 * c + 0] + 7 * rgba[r * 4 * ncols + 4 * c + 1] + 1 * rgba[r * 4 * ncols + 4 * c + 2]) / 10;
|
||||
return gray;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
module.exports = exports = function(pixels, cb){
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
var facefinder_classify_region = function(r, c, s, pixels, ldim) {return -1.0;};
|
||||
var drawSide = function(startX, startY, endX, endY){
|
||||
for (var n = startX; n <= endX + 1; n++){
|
||||
for (var k = startY; k <= endY + 1; k++){
|
||||
|
||||
pixelSetter(n, k, [25, 25, 25, 255], pixels); // To remove 4th channel - pixels.set(n, k, 3, color[3]);
|
||||
}
|
||||
}
|
||||
};
|
||||
var cascadeurl = 'https://raw.githubusercontent.com/nenadmarkus/pico/c2e81f9d23cc11d1a612fd21e4f9de0921a5d0d9/rnt/cascades/facefinder';
|
||||
color = 'rgba(20,20,20,1)';
|
||||
color = color.substring(color.indexOf('(') + 1, color.length - 1); // Extract only the values from rgba(_,_,_,_)
|
||||
color = color.split(',');
|
||||
fetch(cascadeurl).then(function(response) {
|
||||
response.arrayBuffer().then(function(buffer) {
|
||||
var bytes = new Int8Array(buffer);
|
||||
facefinder_classify_region = pico.unpack_cascade(bytes);
|
||||
console.log('* cascade loaded');
|
||||
image = {
|
||||
'pixels': rgba_to_grayscale(pixels.data, pixels.shape[1], pixels.shape[0]),
|
||||
'nrows': pixels.shape[1],
|
||||
'ncols': pixels.shape[0],
|
||||
'ldim': pixels.shape[0]
|
||||
};
|
||||
params = {
|
||||
'shiftfactor': 0.1, // move the detection window by 10% of its size
|
||||
'minsize': 20, // minimum size of a face (not suitable for real-time detection, set it to 100 in that case)
|
||||
'maxsize': 1000, // maximum size of a face
|
||||
'scalefactor': 1.1 // for multiscale processing: resize the detection window by 10% when moving to the higher scale
|
||||
};
|
||||
dets = pico.run_cascade(image, facefinder_classify_region, params);
|
||||
dets = pico.cluster_detections(dets, 0.2); // set IoU threshold to 0.2
|
||||
qthresh = 5.0; // this constant is empirical: other cascades might require a different one
|
||||
|
||||
for(i = 0; i < dets.length; ++i){
|
||||
// check the detection score
|
||||
// if it's above the threshold, draw it
|
||||
if(dets[i][3] > qthresh)
|
||||
{
|
||||
var ox = parseInt(dets[i][1] - dets[i][2] / 2);
|
||||
var oy = parseInt(dets[i][0] - dets[i][2] / 2);
|
||||
var ex = parseInt(dets[i][1] + dets[i][2] / 2) - 1;
|
||||
var ey = parseInt(dets[i][0] + dets[i][2] / 2) - 1;
|
||||
|
||||
drawSide(ox, oy, ox, ey); // Left
|
||||
drawSide(ex, oy, ex, ey); // Right
|
||||
drawSide(ox, oy, ex, oy); // Top
|
||||
drawSide(ox, ey, ex, ey); // Bottom
|
||||
}
|
||||
}
|
||||
|
||||
if (cb) cb();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
44
src/modules/FaceDetection/Module.js
Normal file
44
src/modules/FaceDetection/Module.js
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
module.exports = function FaceDetection(options, UI){
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function extraManipulation(pixels, setRenderState, generateOutput){
|
||||
setRenderState(false);
|
||||
require('./FaceDetection')(pixels, () => {
|
||||
// alert("yo")
|
||||
setRenderState(true);
|
||||
generateOutput();
|
||||
});
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
|
||||
4
src/modules/FaceDetection/index.js
Normal file
4
src/modules/FaceDetection/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
6
src/modules/FaceDetection/info.json
Normal file
6
src/modules/FaceDetection/info.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Face Detection",
|
||||
"description": "Detect faces in given image",
|
||||
"inputs": {},
|
||||
"docs-link":""
|
||||
}
|
||||
Reference in New Issue
Block a user