mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-16 13:20:01 +01:00
Compare commits
32 Commits
dependabot
...
codeowners
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b4bb6edc | ||
|
|
68a0995997 | ||
|
|
28679c18b2 | ||
|
|
47fd8fe6db | ||
|
|
2914aaef70 | ||
|
|
e2dd5a7309 | ||
|
|
5b85e44f42 | ||
|
|
713caf2d7e | ||
|
|
b425d059cb | ||
|
|
0976282a46 | ||
|
|
eb497e1a62 | ||
|
|
8a929c4b4d | ||
|
|
b95af7672c | ||
|
|
eb5672c433 | ||
|
|
c0b9f88818 | ||
|
|
4810a3b975 | ||
|
|
07c6d1497a | ||
|
|
8ddf05f0bc | ||
|
|
456a22a845 | ||
|
|
740693b655 | ||
|
|
23ddf1fce0 | ||
|
|
b7eb9cfc45 | ||
|
|
da9b167258 | ||
|
|
3b6eac4a96 | ||
|
|
8367f4bc26 | ||
|
|
a5abbeaf76 | ||
|
|
d55752827d | ||
|
|
1658220198 | ||
|
|
54722a35f7 | ||
|
|
eb381555c4 | ||
|
|
917979267a | ||
|
|
fb303a8ca9 |
74
.github/CODEOWNERS
vendored
Normal file
74
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# <-- DOCS FOR THIS FILE -->
|
||||||
|
# This is a comment.
|
||||||
|
# Each line is a file pattern followed by one or more owners.
|
||||||
|
|
||||||
|
# These owners will be the default owners for everything in
|
||||||
|
# the repo. Unless a later match takes precedence,
|
||||||
|
# @global-owner1 and @global-owner2 will be requested for
|
||||||
|
# review when someone opens a pull request.
|
||||||
|
# * @global-owner1 @global-owner2
|
||||||
|
|
||||||
|
# Order is important; the last matching pattern takes the most
|
||||||
|
# precedence. When someone opens a pull request that only
|
||||||
|
# modifies JS files, only @js-owner and not the global
|
||||||
|
# owner(s) will be requested for a review.
|
||||||
|
# *.js @js-owner
|
||||||
|
|
||||||
|
# You can also use email addresses if you prefer. They'll be
|
||||||
|
# used to look up users just like we do for commit author
|
||||||
|
# emails.
|
||||||
|
# *.go docs@example.com
|
||||||
|
|
||||||
|
# In this example, @doctocat owns any files in the build/logs
|
||||||
|
# directory at the root of the repository and any of its
|
||||||
|
# subdirectories.
|
||||||
|
# /build/logs/ @doctocat
|
||||||
|
|
||||||
|
# The `docs/*` pattern will match files like
|
||||||
|
# `docs/getting-started.md` but not further nested files like
|
||||||
|
# `docs/build-app/troubleshooting.md`.
|
||||||
|
# docs/* docs@example.com
|
||||||
|
|
||||||
|
# In this example, @octocat owns any file in an apps directory
|
||||||
|
# anywhere in your repository.
|
||||||
|
# apps/ @octocat
|
||||||
|
|
||||||
|
# In this example, @doctocat owns any file in the `/docs`
|
||||||
|
# directory in the root of your repository.
|
||||||
|
# /docs/ @doctocat
|
||||||
|
# <-- /DOCS FOR THIS FILE -->
|
||||||
|
|
||||||
|
# <-- COMMON TO ALL MAINTAINERS -->
|
||||||
|
/*.json @publiclab/is-maintainers
|
||||||
|
/*.md @publiclab/is-maintainers
|
||||||
|
/*.lock @ubliclab/is-maintainers
|
||||||
|
/Gruntfile.js @publiclab/is-maintainers
|
||||||
|
/.github/ @publiclab/is-maintainers
|
||||||
|
# <-- /COMMON TO ALL MAINTAINERS -->
|
||||||
|
|
||||||
|
# <-- SPECIFIC MAINTAINERS -->
|
||||||
|
/index.js @publiclab/is-cli-maintainers
|
||||||
|
/src/cli/ @publiclab/is-cli-maintainers
|
||||||
|
/src/CliUtils.js @publiclab/is-cli-maintainers
|
||||||
|
|
||||||
|
/src/*.js @publiclab/is-core-maintainers
|
||||||
|
/src/*.json @publiclab/is-core-maintainers
|
||||||
|
/src/util/ @publiclab/is-core-maintainers
|
||||||
|
/test/core/* @publiclab/is-core-maintainers
|
||||||
|
|
||||||
|
/src/modules/ @publiclab/is-module-maintainers
|
||||||
|
/docs/ @publiclab/is-module-maintainers
|
||||||
|
|
||||||
|
/test/ @publiclab/is-tests-maintainers
|
||||||
|
/jest* @publiclab/is-tests-maintainers
|
||||||
|
/eslint* @publiclab/is-tests-maintainers
|
||||||
|
/travis* @publiclab/is-tests-maintainers
|
||||||
|
.travis.yml @publiclab/is-tests-maintainers
|
||||||
|
.gitpod* @publiclab/is-tests-maintainers
|
||||||
|
/gitpod* @publiclab/is-tests-maintainers
|
||||||
|
|
||||||
|
/examples/ @publiclab/is-ui-maintainers
|
||||||
|
/icons/ @publiclab/is-ui-maintainers
|
||||||
|
/test/ui-2/test/* @publiclab/is-ui-maintainers
|
||||||
|
/test/ui/spec/* @publiclab/is-ui-maintainers
|
||||||
|
# <-- /SPECIFIC MAINTAINERS -->
|
||||||
7
.gitpod.dockerfile
Normal file
7
.gitpod.dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM gitpod/workspace-full
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN sudo apt-get update && apt-get install -y apt-transport-https \
|
||||||
|
&& sudo apt-get install -y \
|
||||||
|
xserver-xorg-dev libxext-dev libxi-dev build-essential libxi-dev libglu1-mesa-dev libglew-dev pkg-config libglu1-mesa-dev freeglut3-dev mesa-common-dev \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
image:
|
||||||
|
file: .gitpod.dockerfile
|
||||||
tasks:
|
tasks:
|
||||||
- init: npm run setup
|
- init: npm run setup
|
||||||
command: npm start
|
command: npm start
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
sudo: required
|
sudo: required
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- '8'
|
|
||||||
- '10'
|
- '10'
|
||||||
|
- '12'
|
||||||
env:
|
env:
|
||||||
- CXX=g++-4.8
|
- CXX=g++-4.8
|
||||||
before_script:
|
before_script:
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ Image Sequencer
|
|||||||
====
|
====
|
||||||
|
|
||||||
[](https://publiclab.org/conduct)
|
[](https://publiclab.org/conduct)
|
||||||
|
[](https://badge.fury.io/js/image-sequencer)
|
||||||
[](https://travis-ci.org/publiclab/image-sequencer) [](https://codeclimate.com/github/publiclab/image-sequencer/maintainability) [](https://codecov.io/gh/publiclab/image-sequencer)
|
[](https://travis-ci.org/publiclab/image-sequencer) [](https://codeclimate.com/github/publiclab/image-sequencer/maintainability) [](https://codecov.io/gh/publiclab/image-sequencer)
|
||||||
[](https://gitpod.io/from-referrer/)
|
[](https://gitpod.io/#https://github.com/publiclab/image-sequencer/)
|
||||||
|
|
||||||
- **Latest Stable Demo**: https://sequencer.publiclab.org
|
- **Latest Stable Demo**: https://sequencer.publiclab.org
|
||||||
- **Latest Beta Demo**: https://beta.sequencer.publiclab.org
|
- **Latest Beta Demo**: https://beta.sequencer.publiclab.org
|
||||||
- **Stable Branch**: https://github.com/publiclab/image-sequencer/tree/stable/
|
- **Stable Branch**: https://github.com/publiclab/image-sequencer/tree/stable/
|
||||||
|
|
||||||
|
Begin running (and contributing to) this codebase immediately with [GitPod](https://gitpod.io) (this also opens the latest `main` branch code):
|
||||||
|
|
||||||
|
[](https://gitpod.io/#https://github.com/publiclab/image-sequencer)
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
Image Sequencer is different from other image processing systems because it's _non-destructive_: instead of modifying the original image, it **creates a new image at each step in a sequence**. This is because it:
|
Image Sequencer is different from other image processing systems because it's _non-destructive_: instead of modifying the original image, it **creates a new image at each step in a sequence**. This is because it:
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
<h2>Need Help?</h2>
|
<h2>Need Help?</h2>
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-default" href="https://github.com/publiclab/image-sequencer/issues">Ask a question</a>
|
<a class="btn btn-default" href="https://github.com/publiclab/image-sequencer/issues">Ask a question</a>
|
||||||
<a class="btn btn-default" href="https://publiclab.org/chat">Ask in our chatroom</a>
|
<a class="btn btn-default" href="https://gitter.im/publiclab/image-sequencer" target="_blank">Ask in our chatroom </a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
|
|||||||
function selectNewStepUi() {
|
function selectNewStepUi() {
|
||||||
var m = $(addStepSel + ' select').val();
|
var m = $(addStepSel + ' select').val();
|
||||||
if(!m) m = arguments[0];
|
if(!m) m = arguments[0];
|
||||||
$(addStepSel + ' .info').html(_sequencer.modulesInfo(m).description);
|
else $(addStepSel + ' .info').html(_sequencer.modulesInfo(m).description);
|
||||||
$(addStepSel + ' #add-step-btn').prop('disabled', false);
|
$(addStepSel + ' #add-step-btn').prop('disabled', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5149
package-lock.json
generated
5149
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -6,9 +6,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
|
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
|
||||||
"test": "TEST=true istanbul cover tape test/core/*.js test/core/ui/user-interface.js test/core/modules/*.js | tap-spec;",
|
"test": "TEST=true istanbul cover tape test/core/*.js test/core/ui/user-interface.js test/core/modules/*.js | tap-spec;",
|
||||||
"benchmark" : "node test/core/sequencer/benchmark.js | tap-spec;",
|
"benchmark": "node test/core/sequencer/benchmark.js | tap-spec;",
|
||||||
"gif-test" : "node test/core/gifs/gif-test.js | tap-spec;",
|
"gif-test": "node test/core/gifs/gif-test.js | tap-spec;",
|
||||||
"core-tests" : "cat ./output/core-tests.js | tape-run --render=\"tap-spec\"",
|
"core-tests": "cat ./output/core-tests.js | tape-run --render=\"tap-spec\"",
|
||||||
"test-all": "npm run test && npm run benchmark && npm run gif-test && grunt tests && npm run core-tests",
|
"test-all": "npm run test && npm run benchmark && npm run gif-test && grunt tests && npm run core-tests",
|
||||||
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
||||||
"test-ui-2": "node ./node_modules/.bin/jest",
|
"test-ui-2": "node ./node_modules/.bin/jest",
|
||||||
@@ -55,14 +55,14 @@
|
|||||||
"image-sequencer-invert": "^1.0.0",
|
"image-sequencer-invert": "^1.0.0",
|
||||||
"imagejs": "0.0.9",
|
"imagejs": "0.0.9",
|
||||||
"imagemin": "^7.0.1",
|
"imagemin": "^7.0.1",
|
||||||
"imagemin-jpegtran": "^6.0.0",
|
"imagemin-jpegtran": "^7.0.0",
|
||||||
"imagemin-pngquant": "^8.0.0",
|
"imagemin-pngquant": "^9.0.1",
|
||||||
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
|
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
|
||||||
"istanbul": "^0.4.5",
|
"istanbul": "^0.4.5",
|
||||||
"jasmine": "^3.4.0",
|
"jasmine": "^3.4.0",
|
||||||
"jpegtran-bin": "^4.0.0",
|
"jpegtran-bin": "^5.0.2",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"jsdom": "^15.0.0",
|
"jsdom": "^16.3.0",
|
||||||
"jspdf": "^1.5.3",
|
"jspdf": "^1.5.3",
|
||||||
"jsqr": "^1.1.1",
|
"jsqr": "^1.1.1",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
@@ -100,14 +100,14 @@
|
|||||||
"jasmine-core": "^3.3.0",
|
"jasmine-core": "^3.3.0",
|
||||||
"jasmine-jquery": "^2.1.1",
|
"jasmine-jquery": "^2.1.1",
|
||||||
"jasmine-spec-reporter": "^4.2.1",
|
"jasmine-spec-reporter": "^4.2.1",
|
||||||
"jest": "^25.1.0",
|
"jest": "^26.1.0",
|
||||||
"jest-puppeteer": "^4.3.0",
|
"jest-puppeteer": "^4.3.0",
|
||||||
"lint-staged": "^10.0.3",
|
"lint-staged": "^10.0.3",
|
||||||
"looks-same": "^7.0.0",
|
"looks-same": "^7.0.0",
|
||||||
"matchdep": "^2.0.0",
|
"matchdep": "^2.0.0",
|
||||||
"tap-spec": "^5.0.0",
|
"tap-spec": "^5.0.0",
|
||||||
"tape": "^4.9.2",
|
"tape": "^4.9.2",
|
||||||
"tape-run": "^7.0.0",
|
"tape-run": "^8.0.0",
|
||||||
"uglify-es": "^3.3.7"
|
"uglify-es": "^3.3.7"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ const kernelx = [
|
|||||||
[ 1, 2, 1]
|
[ 1, 2, 1]
|
||||||
];
|
];
|
||||||
|
|
||||||
let pixelsToBeSupressed = [];
|
|
||||||
|
|
||||||
module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, useHysteresis) {
|
module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, useHysteresis) {
|
||||||
let angles = [], grads = [], strongEdgePixels = [], weakEdgePixels = [];
|
let angles = [], grads = [], strongEdgePixels = [], weakEdgePixels = [], pixelsToBeSupressed = [];
|
||||||
|
|
||||||
for (var x = 0; x < pixels.shape[0]; x++) {
|
for (var x = 0; x < pixels.shape[0]; x++) {
|
||||||
grads.push([]);
|
grads.push([]);
|
||||||
@@ -34,8 +32,8 @@ module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, useHyst
|
|||||||
angles.slice(-1)[0].push(result.angle);
|
angles.slice(-1)[0].push(result.angle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nonMaxSupress(pixels, grads, angles); // Non Maximum Suppression: Filter fine edges.
|
nonMaxSupress(pixels, grads, angles, pixelsToBeSupressed); // Non Maximum Suppression: Filter fine edges.
|
||||||
doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels); // Double Threshold: Categorizes edges into strong and weak edges based on two thresholds.
|
doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels, pixelsToBeSupressed); // Double Threshold: Categorizes edges into strong and weak edges based on two thresholds.
|
||||||
if(useHysteresis.toLowerCase() == 'true') hysteresis(strongEdgePixels, weakEdgePixels); // Optional Hysteresis (very slow) to minimize edges generated due to noise.
|
if(useHysteresis.toLowerCase() == 'true') hysteresis(strongEdgePixels, weakEdgePixels); // Optional Hysteresis (very slow) to minimize edges generated due to noise.
|
||||||
|
|
||||||
strongEdgePixels.forEach(pixel => preserve(pixels, pixel)); // Makes the strong edges White.
|
strongEdgePixels.forEach(pixel => preserve(pixels, pixel)); // Makes the strong edges White.
|
||||||
@@ -144,7 +142,7 @@ const removeElem = (arr = [], elem) => { // Removes the specified element from t
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Non Maximum Supression without interpolation.
|
// Non Maximum Supression without interpolation.
|
||||||
function nonMaxSupress(pixels, grads, angles) {
|
function nonMaxSupress(pixels, grads, angles, pixelsToBeSupressed) {
|
||||||
angles = angles.map((arr) => arr.map(convertToDegrees));
|
angles = angles.map((arr) => arr.map(convertToDegrees));
|
||||||
|
|
||||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||||
@@ -196,7 +194,7 @@ var convertToDegrees = radians => (radians * 180) / Math.PI;
|
|||||||
var findMaxInMatrix = arr => Math.max(...arr.map(el => el.map(val => val ? val : 0)).map(el => Math.max(...el)));
|
var findMaxInMatrix = arr => Math.max(...arr.map(el => el.map(val => val ? val : 0)).map(el => Math.max(...el)));
|
||||||
|
|
||||||
// Applies the double threshold to the image.
|
// Applies the double threshold to the image.
|
||||||
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels) {
|
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels, pixelsToBeSupressed) {
|
||||||
|
|
||||||
const highThreshold = findMaxInMatrix(grads) * highThresholdRatio, // High Threshold relative to the strongest edge
|
const highThreshold = findMaxInMatrix(grads) * highThresholdRatio, // High Threshold relative to the strongest edge
|
||||||
lowThreshold = highThreshold * lowThresholdRatio; // Low threshold relative to high threshold
|
lowThreshold = highThreshold * lowThresholdRatio; // Low threshold relative to high threshold
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ module.exports = function edgeDetect(options, UI) {
|
|||||||
|
|
||||||
// The function which is called on every draw.
|
// The function which is called on every draw.
|
||||||
function draw(input, callback, progressObj) {
|
function draw(input, callback, progressObj) {
|
||||||
|
|
||||||
progressObj.stop(true);
|
progressObj.stop(true);
|
||||||
progressObj.overrideFlag = true;
|
progressObj.overrideFlag = true;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
35
test/ui-2/test/apply-button.test.js
Normal file
35
test/ui-2/test/apply-button.test.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const timeout = process.env.SLOWMO ? 30000 : 10000;
|
||||||
|
const fs = require('fs');
|
||||||
|
beforeAll(async () => {
|
||||||
|
path = fs.realpathSync('file://../examples/index.html');
|
||||||
|
await page.goto('file://' + path, {waitUntil: 'domcontentloaded'});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Apply Button Works', () => {
|
||||||
|
test('Apply Button is clicked', async () => {
|
||||||
|
await page.waitForSelector('.step');
|
||||||
|
|
||||||
|
|
||||||
|
await page.click('[data-value=\'resize\']');
|
||||||
|
const Length1 = await page.evaluate(() => document.querySelectorAll('.step').length);
|
||||||
|
//Lets change the default value
|
||||||
|
expect(Length1).toBe(2);
|
||||||
|
// Let's check the source of the image output by the default values
|
||||||
|
const src1 = await page.evaluate(() => document.querySelectorAll('.step img')[1].src);
|
||||||
|
//Lets change the default value
|
||||||
|
await page.evaluate(() => document.querySelector('div[name="resize"] input').value = '');
|
||||||
|
await page.type('div[name="resize"] input', '50%');
|
||||||
|
//Wait for the apply button to get enabled then click
|
||||||
|
await page.waitForFunction('document.querySelector(".btn-save").disabled == false');
|
||||||
|
await page.$eval('.btn-save', elem => elem.click());
|
||||||
|
//Wait for the image to process with the new value
|
||||||
|
await page.waitForSelector('.load', {visible: false});
|
||||||
|
|
||||||
|
//Let's check the source of the image output by the new values
|
||||||
|
const src2 = await page.evaluate(() => document.querySelectorAll('.step img')[1].src);
|
||||||
|
//Expect default and new image to be changed
|
||||||
|
expect(src1).not.toEqual(src2);
|
||||||
|
|
||||||
|
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user