mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-07 17:00:02 +01:00
Compare commits
49 Commits
gitpod-mai
...
bump-lockf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dad6d8b33 | ||
|
|
a2d654321d | ||
|
|
ad2f63ce4f | ||
|
|
82601ecbea | ||
|
|
c750958add | ||
|
|
e1c8a5f218 | ||
|
|
8c461bbeee | ||
|
|
11478ce421 | ||
|
|
fa02c9f1c4 | ||
|
|
f4baebd7c7 | ||
|
|
d0172f91ff | ||
|
|
c0cf8798a7 | ||
|
|
d33afe09da | ||
|
|
1713751728 | ||
|
|
a7993d5f7b | ||
|
|
31eaba8912 | ||
|
|
c372b7d950 | ||
|
|
765dbc5c9c | ||
|
|
8e3abcccd6 | ||
|
|
68a0995997 | ||
|
|
47fd8fe6db | ||
|
|
2914aaef70 | ||
|
|
e2dd5a7309 | ||
|
|
eb497e1a62 | ||
|
|
8a929c4b4d | ||
|
|
b95af7672c | ||
|
|
eb5672c433 | ||
|
|
c0b9f88818 | ||
|
|
4810a3b975 | ||
|
|
07c6d1497a | ||
|
|
8ddf05f0bc | ||
|
|
456a22a845 | ||
|
|
740693b655 | ||
|
|
23ddf1fce0 | ||
|
|
b7eb9cfc45 | ||
|
|
da9b167258 | ||
|
|
3b6eac4a96 | ||
|
|
8367f4bc26 | ||
|
|
a5abbeaf76 | ||
|
|
d55752827d | ||
|
|
1658220198 | ||
|
|
54722a35f7 | ||
|
|
eb381555c4 | ||
|
|
917979267a | ||
|
|
fb303a8ca9 | ||
|
|
102b82bd34 | ||
|
|
222d217d6d | ||
|
|
ef3afc00e2 | ||
|
|
325a5660d5 |
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 -->
|
||||
17
.github/config.yml
vendored
17
.github/config.yml
vendored
@@ -4,22 +4,27 @@
|
||||
|
||||
# Comment to be posted to on first time issues
|
||||
newIssueWelcomeComment: |
|
||||
Thanks for opening your first issue here! Please follow the issue template to help us help you 👍🎉😄
|
||||
If you have screenshots to share demonstrating the issue, that's really helpful! 📸 You can [make a gif](https://www.cockos.com/licecap/) too!
|
||||
Thanks for opening your first issue here! This space is [protected by our Code of Conduct](https://publiclab.org/conduct) - and we're here to help.
|
||||
Please follow the issue template to help us help you 👍🎉😄
|
||||
If you have screenshots to share demonstrating the issue, that's really helpful! 📸 You can [make a gif](https://www.cockos.com/licecap/) too!
|
||||
Don't forget to join our [PublicLab Gitter channel](https://gitter.im/publiclab/publiclab) and our [ImageSequencer Gitter Channel](https://gitter.im/publiclab/image-sequencer) for some brainstorming discussions.
|
||||
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
|
||||
|
||||
# Comment to be posted to on PRs from first time contributors in your repository
|
||||
newPRWelcomeComment: |
|
||||
Thanks for opening this pull request!
|
||||
Thanks for opening this pull request! This space is [protected by our Code of Conduct](https://publiclab.org/conduct).
|
||||
There may be some errors, **but don't worry!** We're here to help! 👍🎉😄
|
||||
Also please refer (https://github.com/publiclab/image-sequencer/blob/main/README.md) for installation help.
|
||||
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
|
||||
|
||||
# Comment to be posted to on pull requests merged by a first time user
|
||||
firstPRMergeComment: |
|
||||
Congrats on merging your first pull request! 🙌🎉⚡️
|
||||
Your code will be published to https://beta.sequencer.publiclab.org in a day or two.
|
||||
In the meantime, can you tell us your Twitter handle so we can thank you properly?
|
||||
Now that you've completed this, you can help someone else take their first step!
|
||||
Your code will be published to https://beta.sequencer.publiclab.org in a day or two. Please test out your work on this testing server and report back with a comment that all has gone well!
|
||||
In the meantime, can you tell us your Twitter handle so we can thank you properly also do join our weekly check-in to share your this week goal and the awesome work you did 😃.
|
||||
Please find the link **pinned in the issue section**
|
||||
Now that you've completed this, you can help someone else take their first step! Try looking at this list of `first-timers-only` issues, and see if someone else is waiting for feedback, or even stuck! 😕
|
||||
People often get stuck at the same steps, so you might be able to help someone get unstuck, or help lead them to some documentation that'd help. Reach out and be encouraging and friendly! 😄 🎉
|
||||
See: [Public Lab's coding community!](https://code.publiclab.org)
|
||||
|
||||
# It is recommended to include as many gifs and emojis as possible
|
||||
|
||||
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:
|
||||
- init: npm run setup
|
||||
command: npm start
|
||||
@@ -7,7 +9,6 @@ ports:
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
main: true
|
||||
branches: true
|
||||
pullRequests: true
|
||||
pullRequestsFromForks: true
|
||||
|
||||
27
.travis.yml
27
.travis.yml
@@ -1,8 +1,8 @@
|
||||
sudo: required
|
||||
language: node_js
|
||||
node_js:
|
||||
- '8'
|
||||
- '10'
|
||||
- '12'
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
before_script:
|
||||
@@ -10,15 +10,22 @@ before_script:
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
script:
|
||||
- npm test
|
||||
- npm run benchmark
|
||||
- npm run gif-test
|
||||
- grunt tests
|
||||
- npm run core-tests
|
||||
- npm run test-ui
|
||||
- npm run test-ui-2
|
||||
- grunt build
|
||||
jobs:
|
||||
include:
|
||||
- name: "Base istanbul/tape node tests"
|
||||
script: npm test
|
||||
- name: "Benchmark tests"
|
||||
script: npm run benchmark
|
||||
- name: "Gif tests"
|
||||
script: npm run gif-test
|
||||
- name: "Browserify core tests and run"
|
||||
script: grunt tests && npm run core-tests
|
||||
- name: "Jasmine UI tests (mocked browser env)"
|
||||
script: npm run test-ui
|
||||
- name: "jest-puppeteer UI tests (full browser env)"
|
||||
script: npm run test-ui-2
|
||||
- name: "Grunt build test of dev environment"
|
||||
script: grunt build
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
after_script:
|
||||
|
||||
@@ -44,7 +44,14 @@ module.exports = function(grunt) {
|
||||
dest: 'dist/image-sequencer-ui.brow.js'
|
||||
},
|
||||
tests: {
|
||||
src: ['test/core/sequencer/meta-modules.js', 'test/core/sequencer/image-sequencer.js', 'test/core/sequencer/chain.js', 'test/core/sequencer/replace.js', 'test/core/sequencer/import-export.js', 'test/core/sequencer/run.js', 'test/core/sequencer/dynamic-imports.js', 'test/core/util/*.js'],
|
||||
src: ['test/core/sequencer/meta-modules.js',
|
||||
'test/core/sequencer/image-sequencer.js',
|
||||
'test/core/sequencer/chain.js',
|
||||
'test/core/sequencer/replace.js',
|
||||
'test/core/sequencer/import-export.js',
|
||||
'test/core/sequencer/run.js',
|
||||
'test/core/sequencer/dynamic-imports.js',
|
||||
'test/core/util/*.js'],
|
||||
dest: './output/core-tests.js'
|
||||
}
|
||||
},
|
||||
|
||||
16
README.md
16
README.md
@@ -2,13 +2,18 @@ Image Sequencer
|
||||
====
|
||||
|
||||
[](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://gitpod.io/from-referrer/)
|
||||
[](https://gitpod.io/#https://github.com/publiclab/image-sequencer/)
|
||||
|
||||
- **Latest Stable Demo**: https://sequencer.publiclab.org
|
||||
- **Latest Beta Demo**: https://beta.sequencer.publiclab.org
|
||||
- **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
|
||||
|
||||
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:
|
||||
@@ -615,8 +620,7 @@ The final frames are then converted back to a GIF but in the process, the time d
|
||||
Modules that do not work:
|
||||
1. ColorBar (Will get fixed upon fixing overlay as this is a meta module which uses overlay)
|
||||
2. FisheyeGL
|
||||
4. Overlay
|
||||
5. Text Overlay (Almost fixed)
|
||||
6. Blend
|
||||
7. Histogram
|
||||
8. WebGL Distort
|
||||
3. Overlay
|
||||
4. Blend
|
||||
5. Histogram
|
||||
6. WebGL Distort
|
||||
@@ -44,7 +44,6 @@ body > .container-fluid {
|
||||
.panel {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
min-width:400px;
|
||||
}
|
||||
|
||||
.mouse {
|
||||
@@ -66,7 +65,6 @@ body > .container-fluid {
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
color: #444;
|
||||
min-width:300px;
|
||||
}
|
||||
|
||||
.dropzone input {
|
||||
@@ -156,6 +154,13 @@ body > .container-fluid {
|
||||
margin: 0px 0px 0px 10px;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
@media(max-width: 768px) {
|
||||
#dropzone {
|
||||
margin: 0 0% 30px;
|
||||
}
|
||||
}
|
||||
|
||||
#dwnld {
|
||||
max-width: 500px;
|
||||
margin: 20px auto;
|
||||
@@ -324,7 +329,7 @@ a.name-header{
|
||||
position: fixed;
|
||||
right: 2%;
|
||||
top: 5%;
|
||||
color: lightgray;
|
||||
color: gray;
|
||||
}
|
||||
/* Non float rightward alignment*/
|
||||
.right {
|
||||
|
||||
@@ -340,6 +340,7 @@ window.onload = function () {
|
||||
else
|
||||
step.imgElement.src = url;
|
||||
insertPreview.updatePreviews(url, document.querySelector('#addStep'));
|
||||
DefaultHtmlStepUi(sequencer).updateDimensions(step);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<header class="text-center" style="min-width: 450px">
|
||||
<header class="text-center">
|
||||
<h1><a href="/" target='_blank' class="name-header">Image Sequencer</a></h1>
|
||||
<p>
|
||||
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original
|
||||
@@ -218,7 +218,7 @@
|
||||
<h2>Need Help?</h2>
|
||||
<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://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>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
|
||||
@@ -28,7 +28,7 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
function selectNewStepUi() {
|
||||
var m = $(addStepSel + ' select').val();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const staticCacheName = 'image-sequencer-static-v3.5.1';
|
||||
const staticCacheName = 'image-sequencer-static-v3.6.0';
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Attempting to install service worker');
|
||||
});
|
||||
|
||||
5690
package-lock.json
generated
5690
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "image-sequencer",
|
||||
"version": "3.5.1",
|
||||
"version": "3.6.0",
|
||||
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
||||
"main": "src/ImageSequencer.js",
|
||||
"scripts": {
|
||||
"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;",
|
||||
"benchmark" : "node test/core/sequencer/benchmark.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\"",
|
||||
"benchmark": "node test/core/sequencer/benchmark.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\"",
|
||||
"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-2": "node ./node_modules/.bin/jest",
|
||||
@@ -55,14 +55,14 @@
|
||||
"image-sequencer-invert": "^1.0.0",
|
||||
"imagejs": "0.0.9",
|
||||
"imagemin": "^7.0.1",
|
||||
"imagemin-jpegtran": "^6.0.0",
|
||||
"imagemin-pngquant": "^8.0.0",
|
||||
"imagemin-jpegtran": "^7.0.0",
|
||||
"imagemin-pngquant": "^9.0.1",
|
||||
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
|
||||
"istanbul": "^0.4.5",
|
||||
"jasmine": "^3.4.0",
|
||||
"jpegtran-bin": "^4.0.0",
|
||||
"jpegtran-bin": "^5.0.2",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^15.0.0",
|
||||
"jsdom": "^16.3.0",
|
||||
"jspdf": "^1.5.3",
|
||||
"jsqr": "^1.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
@@ -100,14 +100,14 @@
|
||||
"jasmine-core": "^3.3.0",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
"jasmine-spec-reporter": "^4.2.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest": "^26.1.0",
|
||||
"jest-puppeteer": "^4.3.0",
|
||||
"lint-staged": "^10.0.3",
|
||||
"looks-same": "^7.0.0",
|
||||
"matchdep": "^2.0.0",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^4.9.2",
|
||||
"tape-run": "^6.0.0",
|
||||
"tape-run": "^8.0.0",
|
||||
"uglify-es": "^3.3.7"
|
||||
},
|
||||
"husky": {
|
||||
@@ -119,4 +119,4 @@
|
||||
"bin": {
|
||||
"sequencer": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ module.exports = require('../../util/createMetaModule.js')(
|
||||
{ 'name': 'gradient', 'options': {} },
|
||||
{ 'name': 'colormap', 'options': { colormap: options.colormap || defaults.colormap } },
|
||||
{ 'name': 'crop', 'options': { 'y': 0, 'w': '100%', 'h': options.h || defaults.h } },
|
||||
{ 'name': 'overlay', 'options': { 'x': options.x || defaults.h, 'y': options.y || defaults.y, 'offset': -4 } }
|
||||
{ 'name': 'overlay', 'options': { 'x': options.x || defaults.x, 'y': options.y || defaults.y, 'offset': -4 } }
|
||||
];
|
||||
}, {
|
||||
infoJson: require('./info.json')
|
||||
|
||||
@@ -29,11 +29,13 @@ module.exports = function CropModule(options, UI) {
|
||||
|
||||
options.step.input = input.src;
|
||||
|
||||
// We should do this via event/listener:
|
||||
if (ui && ui.hide) ui.hide();
|
||||
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
const newPixels = require('./Crop')(pixels, options, function() {
|
||||
// We should do this via event/listener:
|
||||
if (ui && ui.hide) ui.hide();
|
||||
|
||||
|
||||
// Start custom UI setup (draggable UI)
|
||||
// Only once we have an input image
|
||||
if (setupComplete === false && options.step.inBrowser && !options.noUI) {
|
||||
|
||||
@@ -14,11 +14,9 @@ const kernelx = [
|
||||
[ 1, 2, 1]
|
||||
];
|
||||
|
||||
let pixelsToBeSupressed = [];
|
||||
|
||||
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++) {
|
||||
grads.push([]);
|
||||
angles.push([]);
|
||||
@@ -34,8 +32,8 @@ module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, useHyst
|
||||
angles.slice(-1)[0].push(result.angle);
|
||||
}
|
||||
}
|
||||
nonMaxSupress(pixels, grads, angles); // 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.
|
||||
nonMaxSupress(pixels, grads, angles, pixelsToBeSupressed); // Non Maximum Suppression: Filter fine edges.
|
||||
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.
|
||||
|
||||
strongEdgePixels.forEach(pixel => preserve(pixels, pixel)); // Makes the strong edges White.
|
||||
@@ -112,10 +110,13 @@ function sobelFilter(pixels, x, y) {
|
||||
* @returns {Number} Category number of the given angle
|
||||
*/
|
||||
function categorizeAngle(angle){
|
||||
if ((angle >= -22.5 && angle <= 22.5) || (angle < -157.5 && angle >= -180)) return 1;
|
||||
else if ((angle >= 22.5 && angle <= 67.5) || (angle < -112.5 && angle >= -157.5)) return 2;
|
||||
else if ((angle >= 67.5 && angle <= 112.5) || (angle < -67.5 && angle >= -112.5)) return 3;
|
||||
else if ((angle >= 112.5 && angle <= 157.5) || (angle < -22.5 && angle >= -67.5)) return 4;
|
||||
const pi = Math.PI;
|
||||
angle = angle > 0 ? angle : pi - Math.abs(angle); // Diagonally flip the angle if it is negative (since edge remains the same)
|
||||
|
||||
if (angle <= pi / 8 || angle > 7 * pi / 8) return 1;
|
||||
else if (angle > pi / 8 && angle <= 3 * pi / 8) return 2;
|
||||
else if (angle > 3 * pi / 8 && angle <= 5 * pi / 8) return 3;
|
||||
else if (angle > 5 * pi / 8 && angle <= 7 * pi / 8) return 4;
|
||||
|
||||
/* Category Map
|
||||
* 1 => E-W
|
||||
@@ -144,9 +145,7 @@ const removeElem = (arr = [], elem) => { // Removes the specified element from t
|
||||
};
|
||||
|
||||
// Non Maximum Supression without interpolation.
|
||||
function nonMaxSupress(pixels, grads, angles) {
|
||||
angles = angles.map((arr) => arr.map(convertToDegrees));
|
||||
|
||||
function nonMaxSupress(pixels, grads, angles, pixelsToBeSupressed) {
|
||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||
|
||||
@@ -159,7 +158,7 @@ function nonMaxSupress(pixels, grads, angles) {
|
||||
pixelsToBeSupressed.push([x, y]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 2:
|
||||
if (!((grads[x][y] >= grads[x + 1][y + 1]) && (grads[x][y] >= grads[x - 1][y - 1]))){
|
||||
pixelsToBeSupressed.push([x, y]);
|
||||
@@ -183,20 +182,11 @@ function nonMaxSupress(pixels, grads, angles) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @method convertToDegrees
|
||||
* @description Converts the given angle(in radians) to degrees.
|
||||
* @param {Number} radians Angle in radians
|
||||
* @returns {Number} Angle in degrees
|
||||
*/
|
||||
var convertToDegrees = radians => (radians * 180) / Math.PI;
|
||||
|
||||
// Finds the max value in a 2d array like grads.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
const imagejs = require('imagejs'),
|
||||
pixelSetter = require('../../util/pixelSetter'),
|
||||
ndarray = require('ndarray');
|
||||
/*
|
||||
* Resize the image by given percentage value
|
||||
*/
|
||||
@@ -19,44 +16,7 @@ module.exports = function Resize(options, UI) {
|
||||
const step = this;
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
// Value above 100% scales up, and below 100% scales down
|
||||
const resize_value = parseInt(options.resize.slice(0, -1));
|
||||
|
||||
if (resize_value == 100) return pixels;
|
||||
|
||||
|
||||
const new_width = Math.round(pixels.shape[0] * (resize_value / 100)),
|
||||
new_height = Math.round(pixels.shape[1] * (resize_value / 100));
|
||||
|
||||
const bitmap = new imagejs.Bitmap({ width: pixels.shape[0], height: pixels.shape[1] });
|
||||
|
||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||
let r = pixels.get(x, y, 0),
|
||||
g = pixels.get(x, y, 1),
|
||||
b = pixels.get(x, y, 2),
|
||||
a = pixels.get(x, y, 3);
|
||||
|
||||
bitmap.setPixel(x, y, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
const resized = bitmap.resize({
|
||||
width: new_width,
|
||||
height: new_height,
|
||||
algorithm: 'bicubicInterpolation'
|
||||
});
|
||||
|
||||
const newPix = new ndarray([], [new_width, new_height, 4]);
|
||||
|
||||
for (let x = 0; x < new_width; x++) {
|
||||
for (let y = 0; y < new_height; y++) {
|
||||
const {r, g, b, a} = resized.getPixel(x, y);
|
||||
pixelSetter(x, y, [r, g, b, a], newPix);
|
||||
}
|
||||
}
|
||||
|
||||
return newPix;
|
||||
return require('./Resize')(pixels, options);
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
|
||||
44
src/modules/Resize/Resize.js
Normal file
44
src/modules/Resize/Resize.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const imagejs = require('imagejs'),
|
||||
pixelSetter = require('../../util/pixelSetter'),
|
||||
ndarray = require('ndarray');
|
||||
module.exports = function Resize(pixels, options) {
|
||||
const resize_value = parseInt(options.resize.slice(0, -1));
|
||||
|
||||
if (resize_value == 100) return pixels;
|
||||
|
||||
const new_width = Math.round(pixels.shape[0] * (resize_value / 100)),
|
||||
new_height = Math.round(pixels.shape[1] * (resize_value / 100));
|
||||
|
||||
const bitmap = new imagejs.Bitmap({
|
||||
width: pixels.shape[0],
|
||||
height: pixels.shape[1]
|
||||
});
|
||||
|
||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||
let r = pixels.get(x, y, 0),
|
||||
g = pixels.get(x, y, 1),
|
||||
b = pixels.get(x, y, 2),
|
||||
a = pixels.get(x, y, 3);
|
||||
|
||||
bitmap.setPixel(x, y, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
const resized = bitmap.resize({
|
||||
width: new_width,
|
||||
height: new_height,
|
||||
algorithm: 'bicubicInterpolation'
|
||||
});
|
||||
|
||||
const newPix = new ndarray([], [new_width, new_height, 4]);
|
||||
|
||||
for (let x = 0; x < new_width; x++) {
|
||||
for (let y = 0; y < new_height; y++) {
|
||||
const { r, g, b, a } = resized.getPixel(x, y);
|
||||
pixelSetter(x, y, [r, g, b, a], newPix);
|
||||
}
|
||||
}
|
||||
|
||||
return newPix;
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
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