mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-11 10:49:59 +01:00
Compare commits
2 Commits
dependabot
...
sashadev-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8f2f1e8b4 | ||
|
|
b7e97f315c |
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,7 +2,7 @@ Fixes #0000 (<=== Replace `0000` with the Issue Number)
|
||||
|
||||
Make sure these boxes are checked before your pull request (PR) is ready to be reviewed and merged. Thanks!
|
||||
|
||||
* [ ] tests pass -- look for a green checkbox ✔️ a few minutes after opening your PR -- or run tests locally with `npm run test-all`
|
||||
* [ ] tests pass -- look for a green checkbox ✔️ a few minutes after opening your PR -- or run tests locally with `npm test`
|
||||
* [ ] code is in uniquely-named feature branch and has no merge conflicts
|
||||
* [ ] PR is descriptively titled
|
||||
* [ ] ask `@publiclab/is-reviewers` for help, in a comment below
|
||||
|
||||
16
.gitpod.yml
16
.gitpod.yml
@@ -1,16 +0,0 @@
|
||||
tasks:
|
||||
- init: npm run setup
|
||||
command: npm start
|
||||
ports:
|
||||
- port: 3000
|
||||
onOpen: open-preview
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
branches: true
|
||||
pullRequests: true
|
||||
pullRequestsFromForks: true
|
||||
addCheck: true
|
||||
addComment: true
|
||||
addBadge: false
|
||||
addLabel: false
|
||||
20
.travis.yml
20
.travis.yml
@@ -10,22 +10,10 @@ 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
|
||||
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
|
||||
script:
|
||||
- npm test
|
||||
- npm run test-ui
|
||||
- grunt build
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
after_script:
|
||||
|
||||
36
Gruntfile.js
36
Gruntfile.js
@@ -2,7 +2,6 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-browserify');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify-es');
|
||||
grunt.loadNpmTasks('grunt-browser-sync');
|
||||
grunt.loadNpmTasks('grunt-text-replace');
|
||||
|
||||
require('matchdep')
|
||||
.filterDev('grunt-*')
|
||||
@@ -22,11 +21,6 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
browserify: {
|
||||
options: {
|
||||
alias: {
|
||||
'gpu.js': './node_modules/gpu.js/src/index.js'
|
||||
}
|
||||
},
|
||||
core: {
|
||||
src: ['src/ImageSequencer.js'],
|
||||
dest: 'dist/image-sequencer.js'
|
||||
@@ -42,28 +36,6 @@ module.exports = function(grunt) {
|
||||
produi: {
|
||||
src: ['examples/demo.js'],
|
||||
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'],
|
||||
dest: './output/core-tests.js'
|
||||
}
|
||||
},
|
||||
|
||||
replace: {
|
||||
version: {
|
||||
src: ['examples/sw.js'],
|
||||
overwrite: true,
|
||||
replacements: [{
|
||||
from: /image-sequencer-static-v.*/g,
|
||||
to: "image-sequencer-static-v<%= pkg.version %>';"
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -97,10 +69,8 @@ module.exports = function(grunt) {
|
||||
|
||||
/* Default (development): Watch files and build on change. */
|
||||
grunt.registerTask('default', ['watch']);
|
||||
grunt.registerTask('build', ['browserify:core', 'browserify:ui', 'replace:version', 'uglify:core', 'uglify:ui']);
|
||||
grunt.registerTask('serve', ['browserify:core', 'browserify:ui', 'replace:version', 'browserSync', 'watch']);
|
||||
grunt.registerTask('build', ['browserify:core', 'browserify:ui', 'uglify:core', 'uglify:ui']);
|
||||
grunt.registerTask('serve', ['browserify:core', 'browserify:ui', 'browserSync', 'watch']);
|
||||
grunt.registerTask('compile', ['browserify:core', 'browserify:ui']);
|
||||
grunt.registerTask('production', ['browserify:prodcore', 'browserify:produi', 'replace:version', 'uglify:prodcore', 'uglify:produi']);
|
||||
|
||||
grunt.registerTask('tests', ['browserify:tests']);
|
||||
grunt.registerTask('production', ['browserify:prodcore', 'browserify:produi', 'uglify:prodcore', 'uglify:produi']);
|
||||
};
|
||||
|
||||
25
README.md
25
README.md
@@ -1,9 +1,8 @@
|
||||
Image Sequencer
|
||||
====
|
||||
|
||||
[](https://publiclab.org/conduct)
|
||||
|
||||
[](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/)
|
||||
|
||||
- **Latest Stable Demo**: https://sequencer.publiclab.org
|
||||
- **Latest Beta Demo**: https://beta.sequencer.publiclab.org
|
||||
@@ -72,12 +71,6 @@ In case of a port conflict please run the following
|
||||
npm i -g http-server ; http-server -p 3000
|
||||
```
|
||||
|
||||
### Online one-click setup for contributing
|
||||
|
||||
Contribute to ImageSequencer using a fully featured online development environment that will automatically: clone the repo, install the dependencies and start the webserver.
|
||||
|
||||
[](https://gitpod.io/from-referrer/)
|
||||
|
||||
### Browser
|
||||
|
||||
Just include [image-sequencer.min.js](https://github.com/publiclab/image-sequencer/blob/stable/dist/image-sequencer.min.js) in the Head section of your web page. See the [demo here](https://sequencer.publiclab.org)!
|
||||
@@ -605,18 +598,4 @@ let sequencer = ImageSequencer() // also for wasm mode i.e. default mode
|
||||
|
||||
let sequencer = ImageSequencer({useWasm:false}) //for non-wasm mode
|
||||
|
||||
```
|
||||
|
||||
## Experimental GIF processing support
|
||||
|
||||
ImageSequencer currently can process GIFs but only for most of the modules. Every frame of the GIF is manipulated sequentially (parallel processing would be preferable in the future).
|
||||
The final frames are then converted back to a GIF but in the process, the time duration of each frame is lost and defaults to `0.1s`.
|
||||
|
||||
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
|
||||
```
|
||||
151
docs/MODULES.md
151
docs/MODULES.md
@@ -8,44 +8,40 @@ List of Module Documentations
|
||||
3. [Blend](#blend-module)
|
||||
4. [Blur](#blur-module)
|
||||
5. [Brightness](#brightness-module)
|
||||
6. [Canvas-Resize](#canvas-resize-module)
|
||||
7. [Channel](#channel-module)
|
||||
8. [Colorbar](#colorbar-module)
|
||||
9. [Colormap](#colormap-module)
|
||||
10. [ColorTemperature](#color-temperature)
|
||||
11. [Contrast](#contrast-module)
|
||||
12. [Convolution](#convolution-module)
|
||||
13. [Crop](#crop-module)
|
||||
14. [DecodeQr](#decodeQr-module)
|
||||
15. [Dither](#dither-module)
|
||||
16. [DrawRectangle](#draw-rectangle-module)
|
||||
17. [Dynamic](#dynamic-module)
|
||||
18. [Edge-Detect](#edge-detect-module)
|
||||
19. [Exposure](#exposure-module)
|
||||
20. [FisheyeGl](#fisheyeGl-module)
|
||||
21. [FlipImage](#flipimage-module)
|
||||
22. [Gamma-Correction](#gamma-correction-module)
|
||||
23. [Gradient](#gradient-module)
|
||||
24. [Grid-Overlay](#grid-overlay)
|
||||
25. [Histogram](#histogram-module)
|
||||
26. [Import-image](#import-image-module)
|
||||
27. [Invert](#invert-module)
|
||||
28. [MinifyImage](#minify-image)
|
||||
29. [Ndvi](#ndvi-module)
|
||||
30. [Ndvi-Colormap](#ndvi-colormap-module)
|
||||
31. [NoiseReduction](#noise-reduction)
|
||||
32. [Overlay](#overlay-module)
|
||||
33. [PaintBucket](#paint-bucket-module)
|
||||
34. [ReplaceColor](#replacecolor-module)
|
||||
35. [Resize](#resize-module)
|
||||
36. [Rotate](#rotate-module)
|
||||
37. [Saturation](#saturation-module)
|
||||
38. [Segmented-Colormap](#segmented-colormap-module)
|
||||
39. [Text-Overlay](#text-overlay)
|
||||
40. [Threshold](#threshold)
|
||||
41. [Tint](#tint)
|
||||
42. [WebGL-Distort](#webgl-distort-module)
|
||||
43. [White-Balance](#white-balance-module)
|
||||
6. [Channel](#channel-module)
|
||||
7. [Colorbar](#colorbar-module)
|
||||
8. [Colormap](#colormap-module)
|
||||
9. [ColorTemperature](#color-temperature)
|
||||
10. [Contrast](#contrast-module)
|
||||
11. [Convolution](#convolution-module)
|
||||
12. [Crop](#crop-module)
|
||||
13. [DecodeQr](#decodeQr-module)
|
||||
14. [Dither](#dither-module)
|
||||
15. [DrawRectangle](#draw-rectangle-module)
|
||||
16. [Dynamic](#dynamic-module)
|
||||
17. [Edge-Detect](#edge-detect-module)
|
||||
18. [FisheyeGl](#fisheyeGl-module)
|
||||
19. [FlipImage](#flipimage-module)
|
||||
20. [Gamma-Correction](#gamma-correction-module)
|
||||
21. [Gradient](#gradient-module)
|
||||
22. [Grid-Overlay](#grid-overlay)
|
||||
23. [Histogram](#histogram-module)
|
||||
24. [Import-image](#import-image-module)
|
||||
25. [Invert](#invert-module)
|
||||
26. [MinifyImage](#minify-image)
|
||||
27. [Ndvi](#ndvi-module)
|
||||
28. [Ndvi-Colormap](#ndvi-colormap-module)
|
||||
29. [NoiseReduction](#noise-reduction)
|
||||
30. [Overlay](#overlay-module)
|
||||
31. [PaintBucket](#paint-bucket-module)
|
||||
32. [ReplaceColor](#replacecolor-module)
|
||||
33. [Resize](#resize-module)
|
||||
34. [Rotate](#rotate-module)
|
||||
35. [Saturation](#saturation-module)
|
||||
36. [Segmented-Colormap](#segmented-colormap-module)
|
||||
37. [Text-Overlay](#text-overlay)
|
||||
38. [Threshold](#threshold)
|
||||
39. [Tint](#tint)
|
||||
|
||||
|
||||
## add-qr-module
|
||||
@@ -78,8 +74,7 @@ This module is used for averaging all the pixels of the image.
|
||||
|
||||
## blend-module
|
||||
|
||||
This module is used for blending two images. For More info read: _[wiki](https://en.wikipedia.org/wiki/Blend_modes)_
|
||||
|
||||
This module is used for blending two images .
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
@@ -89,12 +84,8 @@ This module is used for blending two images. For More info read: _[wiki](https:
|
||||
```
|
||||
|
||||
where `options` is an object with the following properties:
|
||||
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
||||
* blendMode: Blending mode to use for blending two images by default it uses the given function
|
||||
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
||||
|
||||
[More info for different blend modes can be found here](http://docs.gimp.org/en/gimp-concepts-layer-modes.html)
|
||||
|
||||
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
||||
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
||||
|
||||
## Blob Analysis
|
||||
|
||||
@@ -140,24 +131,6 @@ where `options` is an object with the following property:
|
||||
* brightness : brightness of the image in percentage (0 to 100; default 100)
|
||||
|
||||
|
||||
## canvas-resize-module
|
||||
|
||||
This module is used for resizing the canvas of the image.
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
sequencer.loadImage('PATH')
|
||||
.addSteps('canvas-resize',options)
|
||||
.run()
|
||||
```
|
||||
|
||||
where `options` is an object with the following property:
|
||||
* width: final width of the canvas (default 1000)
|
||||
* height: final height of the canvas (default 1000)
|
||||
* x: x-coordinate of the top left of the image on the canvas (default 500)
|
||||
* y: y-coordinate of the top left of the image on the canvas (default 500)
|
||||
|
||||
|
||||
## channel-module
|
||||
|
||||
This module is used for forming a grayscale image by applying one of the three primary colors.
|
||||
@@ -354,21 +327,6 @@ where `options` is an object with the following properties:
|
||||
* lowThresholdratio : Lower Threshold Ratio ( default : 0.2)
|
||||
|
||||
|
||||
## exposure-module
|
||||
|
||||
This module is used for changing the exposure of the image.
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
sequencer.loadImage('PATH')
|
||||
.addSteps('exposure',options)
|
||||
.run()
|
||||
```
|
||||
|
||||
where `options` is an object with the following property:
|
||||
* exposure: exposure value for the new image (-3 to 4; default 1)
|
||||
|
||||
|
||||
## fisheyeGl-module
|
||||
|
||||
This module is used for correcting Fisheye or Lens Distortion
|
||||
@@ -709,38 +667,3 @@ It adds color tint to an image
|
||||
where `options` is an object with the following property:
|
||||
* color : RGB values seperated by a space (default "0 0 255")
|
||||
* factor : amount of tint (default 0.5)
|
||||
|
||||
|
||||
## webgl-distort-module
|
||||
|
||||
This module is used for transforming the perspective of images based on corner coordinates.
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
sequencer.loadImage('PATH')
|
||||
.addSteps('webgl-distort',options)
|
||||
.run()
|
||||
```
|
||||
|
||||
where `options` is an object with the following property:
|
||||
* nw: top-left corner x and y coordinates separated by a comma (default "0,100")
|
||||
* ne: top-right corner x and y coordinates separated by a comma (default "1023,-50")
|
||||
* se: bottom-right corner x and y coordinates separated by a comma (default "1223,867")
|
||||
* sw: bottom-left corner x and y coordinates separated by a comma (default "100,767")
|
||||
|
||||
|
||||
## white-balance-module
|
||||
|
||||
This module is used for rendering neutral colors of an image correctly based on the whitest pixel in the image.
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
sequencer.loadImage('PATH')
|
||||
.addSteps('white-balance',options)
|
||||
.run()
|
||||
```
|
||||
|
||||
where `options` is an object with the following property:
|
||||
* red: red component of the whitest pixel (default 255)
|
||||
* green: green component of the whitest pixel (default 255)
|
||||
* blue: blue component of the whitest pixel (default 255)
|
||||
|
||||
@@ -33,7 +33,7 @@ body > .container-fluid {
|
||||
|
||||
.center-align {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
justify-content: center;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
@@ -69,24 +69,14 @@ body > .container-fluid {
|
||||
min-width:300px;
|
||||
}
|
||||
|
||||
.dropzone input {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.import-image-zone {
|
||||
margin: 10px auto 30px auto;
|
||||
max-width: 250px;
|
||||
min-width: 230px;
|
||||
}
|
||||
|
||||
.import-image-zone input {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.dropzone input {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.step {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -262,7 +252,7 @@ a.name-header{
|
||||
}
|
||||
|
||||
.step-column{
|
||||
display:flex;
|
||||
display:flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -286,9 +276,9 @@ a.name-header{
|
||||
width:100%;
|
||||
}
|
||||
.save-button{
|
||||
margin-top:20px;
|
||||
margin-top:20px;
|
||||
margin-bottom:0px;
|
||||
align:center;
|
||||
align:center;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
@@ -296,74 +286,21 @@ a.name-header{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.general-tooltip:hover{
|
||||
text-decoration: none;
|
||||
.dimension-tooltip:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.general-tooltip:focus{
|
||||
.dimension-tooltip:focus{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.general-tooltip:focus-within{
|
||||
.dimension-tooltip:focus-within{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.general-tooltip{
|
||||
.dimension-tooltip{
|
||||
position: relative;
|
||||
bottom: 7px;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#version-number-text {
|
||||
text-align: center;
|
||||
padding-top: 100px;
|
||||
color: gray;
|
||||
}
|
||||
#version-number-top-right {
|
||||
position: fixed;
|
||||
right: 2%;
|
||||
top: 5%;
|
||||
color: lightgray;
|
||||
}
|
||||
/* Non float rightward alignment*/
|
||||
.right {
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#update-prompt-modal {
|
||||
visibility: hidden;
|
||||
min-width: 250px;
|
||||
margin-left: -125px;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
padding: 16px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 10%;
|
||||
top: 30px;
|
||||
}
|
||||
#update-prompt-modal.show {
|
||||
visibility: visible;
|
||||
-webkit-animation: fadein 0.5s;
|
||||
animation: fadein 0.5s;
|
||||
}
|
||||
@-webkit-keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
175
examples/demo.js
175
examples/demo.js
@@ -3,48 +3,12 @@ var defaultHtmlSequencerUi = require('./lib/defaultHtmlSequencerUi.js'),
|
||||
intermediateHtmlStepUi = require('./lib/intermediateHtmlStepUi.js'),
|
||||
DefaultHtmlStepUi = require('./lib/defaultHtmlStepUi.js'),
|
||||
urlHash = require('./lib/urlHash.js'),
|
||||
insertPreview = require('./lib/insertPreview.js'),
|
||||
versionManagement = require('./lib/versionManagement.js'),
|
||||
isGIF = require('../src/util/isGif');
|
||||
|
||||
insertPreview = require('./lib/insertPreview.js');
|
||||
|
||||
window.onload = function () {
|
||||
sequencer = ImageSequencer(); // Set the global sequencer variable
|
||||
|
||||
options = {
|
||||
sortField: 'text',
|
||||
openOnFocus: false,
|
||||
onInitialize: function () {
|
||||
this.$control.on('click', () => {
|
||||
this.ignoreFocusOpen = true;
|
||||
setTimeout(() => {
|
||||
// Trigger onFocus and open dropdown.
|
||||
this.ignoreFocusOpen = false;
|
||||
}, 50);
|
||||
});
|
||||
},
|
||||
// Open dropdown after timeout of onClick.
|
||||
onFocus: function () {
|
||||
if (!this.ignoreFocusOpen) {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
};
|
||||
sequencer = ImageSequencer();
|
||||
|
||||
versionManagement.getLatestVersionNumber(function(versionNumber) {
|
||||
console.log('The latest NPM version number for Image Sequencer (from GitHub) is v' + versionNumber);
|
||||
});
|
||||
console.log('The local version number for Image Sequencer is v' + versionManagement.getLocalVersionNumber());
|
||||
|
||||
function displayVersionNumber() {
|
||||
$('#version-number-text').text('Image Sequencer v' + versionManagement.getLocalVersionNumber());
|
||||
$('#version-number-top-right').text('v' + versionManagement.getLocalVersionNumber());
|
||||
}
|
||||
displayVersionNumber();
|
||||
|
||||
function refreshOptions(options) {
|
||||
// Default options if parameter is empty.
|
||||
if (options == undefined) options = { sortField: 'text' };
|
||||
function refreshOptions() {
|
||||
// Load information of all modules (Name, Inputs, Outputs)
|
||||
var modulesInfo = sequencer.modulesInfo();
|
||||
|
||||
@@ -60,16 +24,15 @@ window.onload = function () {
|
||||
}
|
||||
// Null option
|
||||
addStepSelect.append('<option value="" disabled selected>Select a Module</option>');
|
||||
addStepSelect.selectize(options);
|
||||
addStepSelect.selectize({
|
||||
sortField: 'text'
|
||||
});
|
||||
}
|
||||
refreshOptions(options);
|
||||
refreshOptions();
|
||||
|
||||
$(window).on('scroll', scrollFunction);
|
||||
|
||||
/**
|
||||
* @description Method to toggle the scroll-up arrow.
|
||||
*/
|
||||
function scrollFunction(A, B) {
|
||||
function scrollFunction() {
|
||||
var shouldDisplay = $('body').scrollTop() > 20 || $(':root').scrollTop() > 20;
|
||||
|
||||
$('#move-up').css({
|
||||
@@ -77,9 +40,7 @@ window.onload = function () {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Method to scroll to the top of the page.
|
||||
*/
|
||||
|
||||
function topFunction() {
|
||||
$('body').animate({scrollTop: 0});
|
||||
$(':root').animate({scrollTop: 0});
|
||||
@@ -94,7 +55,7 @@ window.onload = function () {
|
||||
// UI for the overall demo:
|
||||
var ui = defaultHtmlSequencerUi(sequencer);
|
||||
|
||||
// Load image data from URL `src` parameter.
|
||||
// find any `src` parameters in URL hash and attempt to source image from them and run the sequencer
|
||||
if (urlHash.getUrlHashParameter('src')) {
|
||||
sequencer.loadImage(urlHash.getUrlHashParameter('src'), ui.onLoad);
|
||||
} else {
|
||||
@@ -104,33 +65,25 @@ window.onload = function () {
|
||||
var resetSequence = function () {
|
||||
var r = confirm('Do you want to reset the sequence?');
|
||||
if (r)
|
||||
{
|
||||
window.location.hash = '';
|
||||
location.reload();
|
||||
}
|
||||
window.location = '/';
|
||||
};
|
||||
|
||||
$('#addStep select').on('change', ui.selectNewStepUi);
|
||||
$('#addStep #add-step-btn').on('click', ui.addStepUi);
|
||||
$('#resetButton').on('click', resetSequence);
|
||||
|
||||
// Module Selector quick buttons click handler.
|
||||
//Module button radio selection
|
||||
$('.radio-group .radio').on('click', function () {
|
||||
$(this).parent().find('.radio').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
newStep = $(this).attr('data-value');
|
||||
|
||||
//$("#addStep option[value=" + newStep + "]").attr('selected', 'selected');
|
||||
$('#addStep select').val(newStep);
|
||||
ui.selectNewStepUi(newStep);
|
||||
ui.addStepUi(newStep);
|
||||
$(this).removeClass('selected');
|
||||
});
|
||||
|
||||
/**
|
||||
* @method displayMessageOnSaveSequence
|
||||
* @description When a sequence is saved to a browser, notification is displayed.
|
||||
* @returns {Null}
|
||||
*/
|
||||
function displayMessageOnSaveSequence() {
|
||||
$('.savesequencemsg').fadeIn();
|
||||
setTimeout(function () {
|
||||
@@ -150,7 +103,7 @@ window.onload = function () {
|
||||
}
|
||||
}
|
||||
$('#saveButton').on('click', function () {
|
||||
// Different handlers triggered for different dropdown options.
|
||||
// different handlers triggered for different dropdown options
|
||||
|
||||
let dropDownValue = $('#selectSaveOption option:selected').val();
|
||||
|
||||
@@ -162,18 +115,14 @@ window.onload = function () {
|
||||
}
|
||||
else if (dropDownValue == 'save-seq') {
|
||||
saveSequence();
|
||||
} else if(dropDownValue == 'save-pdf') {
|
||||
savePDF(getLastImage());
|
||||
}
|
||||
else if (dropDownValue == 'save-to-publiclab.org' ){
|
||||
SaveToPubliclab();
|
||||
}
|
||||
});
|
||||
|
||||
let isWorkingOnGifGeneration = false;
|
||||
|
||||
$('.js-view-as-gif').on('click', function (event) { // GIF generation and display
|
||||
if (isWorkingOnGifGeneration) return; // Prevent multiple button clicks
|
||||
$('.js-view-as-gif').on('click', function (event) {
|
||||
/* Prevent user from triggering generation multiple times*/
|
||||
if (isWorkingOnGifGeneration) return;
|
||||
|
||||
isWorkingOnGifGeneration = true;
|
||||
|
||||
@@ -182,12 +131,12 @@ window.onload = function () {
|
||||
button.innerHTML = '<i class="fa fa-circle-o-notch fa-spin"></i>';
|
||||
|
||||
try {
|
||||
// Get GIF resources from previous steps
|
||||
/* Get gif resources of previous steps */
|
||||
let options = getGifResources();
|
||||
|
||||
gifshot.createGIF(options, function (obj) { // GIF generation
|
||||
gifshot.createGIF(options, function (obj) { // gif generation
|
||||
if (!obj.error) {
|
||||
// Final GIF encoded with base64 format
|
||||
// Final gif encoded with base64 format
|
||||
var image = obj.image;
|
||||
var animatedImage = document.createElement('img');
|
||||
|
||||
@@ -197,7 +146,9 @@ window.onload = function () {
|
||||
let modal = $('#js-download-gif-modal');
|
||||
|
||||
$('#js-download-as-gif-button').one('click', function () {
|
||||
downloadGif(image); // Trigger GIF download
|
||||
// Trigger download
|
||||
downloadGif(image);
|
||||
// Close modal
|
||||
modal.modal('hide');
|
||||
});
|
||||
|
||||
@@ -209,6 +160,7 @@ window.onload = function () {
|
||||
// Insert image
|
||||
gifContainer.appendChild(animatedImage);
|
||||
|
||||
|
||||
// Open modal
|
||||
modal.modal();
|
||||
|
||||
@@ -227,16 +179,16 @@ window.onload = function () {
|
||||
});
|
||||
|
||||
function getGifResources() {
|
||||
// Returns an object with specific gif options
|
||||
/* Returns an object with specific gif options */
|
||||
let imgs = document.getElementsByClassName('step-thumbnail');
|
||||
var imgSrcs = [];
|
||||
|
||||
// Pushes image sources of all the modules in the DOM
|
||||
// Pushes image sources of all the modules in dom
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
imgSrcs.push(imgs[i].src);
|
||||
}
|
||||
|
||||
var options = { // GIF frame options
|
||||
var options = { // gif frame options
|
||||
'gifWidth': imgs[0].width,
|
||||
'gifHeight': imgs[0].height,
|
||||
'images': imgSrcs,
|
||||
@@ -255,64 +207,11 @@ window.onload = function () {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data URL for the last image in the sequence.
|
||||
* @return {string} The data URL for the last image in the sequence.
|
||||
*/
|
||||
function getLastImage() {
|
||||
// Get the image from the last step.
|
||||
let imgs = document.getElementsByClassName('step-thumbnail');
|
||||
let lastStepImage = imgs[imgs.length - 1];
|
||||
return lastStepImage.getAttribute('src');
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the given image URL as a PDF file.
|
||||
* @param {string} imageDataURL - The data URL for the image.
|
||||
*/
|
||||
function savePDF(imageDataURL) {
|
||||
sequencer.getImageDimensions(imageDataURL, function(dimensions) {
|
||||
if (isGIF(imageDataURL)) {
|
||||
// Get the dimensions of the image.
|
||||
let pageWidth = dimensions.width;
|
||||
let pageHeight = dimensions.height;
|
||||
|
||||
// Create a new pdf with the same dimensions as the image.
|
||||
const pdf = new jsPDF({
|
||||
orientation: pageHeight > pageWidth ? 'portrait' : 'landscape',
|
||||
unit: 'px',
|
||||
format: [pageHeight, pageWidth]
|
||||
});
|
||||
|
||||
// Add the image to the pdf with dimensions equal to the internal dimensions of the page.
|
||||
pdf.addImage(imageDataURL, 0, 0, pdf.internal.pageSize.getWidth(), pdf.internal.pageSize.getHeight());
|
||||
|
||||
// Save the pdf with the filename specified here:
|
||||
pdf.save('index.pdf');
|
||||
}
|
||||
else console.log('GIFs cannot be converted to PDF');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function downloadGif(image) {
|
||||
download(image, 'index.gif', 'image/gif'); // Downloadjs library function
|
||||
download(image, 'index.gif', 'image/gif');// downloadjs library function
|
||||
}
|
||||
|
||||
function SaveToPubliclab() {
|
||||
function postToPL(imgSrc) {
|
||||
var uniq = Date.now();
|
||||
$('body').append('<form method="post" id="postToPL' + uniq + '" action="https://publiclab.org/post" target="postToPLWindow"><input type="hidden" name="datauri_main_image" /></form>');
|
||||
f = $('#postToPL' + uniq)[0];
|
||||
f.datauri_main_image.value = imgSrc;
|
||||
window.open('', 'postToPLWindow');
|
||||
f.submit();
|
||||
}
|
||||
postToPL($('img')[sequencer.steps.length - 1].src);
|
||||
}
|
||||
|
||||
// Image selection and drag/drop handling from examples/lib/imageSelection.js
|
||||
// image selection and drag/drop handling from examples/lib/imageSelection.js
|
||||
sequencer.setInputStep({
|
||||
dropZoneSelector: '#dropzone',
|
||||
fileInputSelector: '#fileInput',
|
||||
@@ -327,9 +226,8 @@ window.onload = function () {
|
||||
step.options.step.imgElement.src = reader.result;
|
||||
else
|
||||
step.imgElement.src = reader.result;
|
||||
|
||||
insertPreview.updatePreviews(reader.result, document.querySelector('#addStep'));
|
||||
DefaultHtmlStepUi(sequencer).updateDimensions(step);
|
||||
insertPreview.updatePreviews(reader.result, '#addStep');
|
||||
insertPreview.updatePreviews(sequencer.steps[0].imgElement.src, '.insertDiv');
|
||||
},
|
||||
onTakePhoto: function (url) {
|
||||
var step = sequencer.steps[0];
|
||||
@@ -339,15 +237,16 @@ window.onload = function () {
|
||||
step.options.step.imgElement.src = url;
|
||||
else
|
||||
step.imgElement.src = url;
|
||||
insertPreview.updatePreviews(url, document.querySelector('#addStep'));
|
||||
insertPreview.updatePreviews(url, '#addStep');
|
||||
insertPreview.updatePreviews(sequencer.steps[0].imgElement.src, '.insertDiv');
|
||||
}
|
||||
});
|
||||
|
||||
setupCache();
|
||||
|
||||
if (urlHash.getUrlHashParameter('src')) { // Gets the sequence from the URL
|
||||
insertPreview.updatePreviews(urlHash.getUrlHashParameter('src'), document.querySelector('#addStep'));
|
||||
if (urlHash.getUrlHashParameter('src')) {
|
||||
insertPreview.updatePreviews(urlHash.getUrlHashParameter('src'), '#addStep');
|
||||
} else {
|
||||
insertPreview.updatePreviews('images/tulips.png', document.querySelector('#addStep'));
|
||||
insertPreview.updatePreviews('images/tulips.png', '#addStep');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -33,12 +33,9 @@
|
||||
<script src="../node_modules/gifshot/dist/gifshot.min.js" type="text/javascript"></script>
|
||||
|
||||
<!-- Download.js for large files -->
|
||||
<script src="../node_modules/downloadjs/download.min.js" type="text/javascript" ></script>
|
||||
<script src="../node_modules/downloadjs/download.min.js" type="text/javascript" />
|
||||
|
||||
<!-- jspdf to enable save image as pdf -->
|
||||
<script src="../node_modules/jspdf/dist/jspdf.min.js" type="text/javascript" ></script>
|
||||
|
||||
<!-- <script src="lib/scrollToTop.js"></script> -->
|
||||
<script src="lib/scrollToTop.js"></script>
|
||||
<script src="../node_modules/selectize/dist/js/standalone/selectize.min.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -56,8 +53,6 @@
|
||||
<link href="./selectize.default.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
|
||||
<div id="update-prompt-modal">A new version of image sequencer is available. Click <a href="#" id="reload">here</a> to update.</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<header class="text-center" style="min-width: 450px">
|
||||
@@ -66,7 +61,7 @@
|
||||
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original
|
||||
image, it
|
||||
creates a new image at each step in a sequence.
|
||||
<a href="https://github.com/publiclab/image-sequencer/blob/main/README.md">Learn more</a>
|
||||
<a href="https://publiclab.org/image-sequencer">Learn more</a>
|
||||
</p>
|
||||
<p>
|
||||
Open Source
|
||||
@@ -75,11 +70,10 @@
|
||||
</a>
|
||||
by <a href="https://publiclab.org" title="Publiclab Website"><i class="fa fa-globe"></i> Publiclab</a>
|
||||
</p>
|
||||
<span id="version-number-top-right"></span>
|
||||
</header>
|
||||
|
||||
<div id="dropzone" class="dropzone">
|
||||
<p id="dropzone-text">
|
||||
<p>
|
||||
<i>Select or drag in an image to start!</i>
|
||||
</p>
|
||||
<center>
|
||||
@@ -154,7 +148,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<button class="btn btn-primary btn-lg" name="add" id="add-step-btn">Add Step</button></div>
|
||||
<button class="btn btn-success btn-lg" name="add" id="add-step-btn">Add Step</button></div>
|
||||
</div>
|
||||
<div class="row center-align">
|
||||
<button id="resetButton" class="btn btn-default btn-lg"
|
||||
@@ -196,10 +190,8 @@
|
||||
<select class="form-control input-md mouse" id="selectSaveOption" style="margin-top:20px">
|
||||
<option value="save-image">Save as PNG</option>
|
||||
<option value="save-gif">Save as GIF (all steps)</option>
|
||||
<option value="save-pdf">Save as PDF</option>
|
||||
<option value="save-seq">Save sequence</option>
|
||||
<option value="save-seq-string">Save sequence string</option>
|
||||
<option value="save-to-publiclab.org">Save to PublicLab.org</option>>
|
||||
</select>
|
||||
<p><button id="saveButton" class="btn btn-primary btn-lg save-button">Save</button></p>
|
||||
<p><button class="btn btn-default btn-lg js-view-as-gif" id="gif">Preview GIF</button></p>
|
||||
@@ -231,9 +223,6 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p id="version-number-text">Unable to load version number</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<button id="move-up"><i class="fa fa-arrow-circle-o-up"></i></button>
|
||||
|
||||
@@ -1,41 +1,7 @@
|
||||
var setupCache = function() {
|
||||
let newWorker; // When sw.js is changed, this is the new service worker generated.
|
||||
|
||||
// Toggle a CSS class to display a popup prompting the user to fetch a new version.
|
||||
function showUpdateModal() {
|
||||
$('#update-prompt-modal').addClass('show');
|
||||
}
|
||||
|
||||
/**
|
||||
* When a new service worker has been loaded, the button in the update prompt
|
||||
* modal should trigger the skipWaiting event to replace the current
|
||||
* service worker with the new one.
|
||||
*/
|
||||
$('#reload').on('click', function() {
|
||||
newWorker.postMessage({ action: 'skipWaiting' });
|
||||
});
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
// Register the service worker.
|
||||
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
|
||||
.then(function(registration) {
|
||||
registration.addEventListener('updatefound', () => {
|
||||
// When sw.js has been changed, get a reference to the new service worker.
|
||||
newWorker = registration.installing;
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
// Check if service worker state has changed.
|
||||
switch(newWorker.state) {
|
||||
case 'installed':
|
||||
if(navigator.serviceWorker.controller) {
|
||||
// New service worker available; prompt the user to update.
|
||||
showUpdateModal();
|
||||
}
|
||||
// No updates available; do nothing.
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
console.log(installingWorker);
|
||||
@@ -48,17 +14,6 @@ var setupCache = function() {
|
||||
.catch(function(error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
});
|
||||
|
||||
/**
|
||||
* This is the event listener for when the service worker updates.
|
||||
* When the service worker updates, reload the page.
|
||||
*/
|
||||
let refreshing;
|
||||
navigator.serviceWorker.addEventListener('controllerchange', function() {
|
||||
if(refreshing) return;
|
||||
window.location.reload();
|
||||
refreshing = true;
|
||||
});
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
@@ -79,11 +34,6 @@ var setupCache = function() {
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
module.exports = setupCache;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
var urlHash = require('./urlHash.js');
|
||||
insertPreview = require('./insertPreview.js');
|
||||
function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
|
||||
options = options || {};
|
||||
@@ -34,11 +33,6 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
|
||||
function removeStepUi() {
|
||||
var index = $(removeStepSel).index(this) + 1;
|
||||
// If last step is removed.
|
||||
if(sequencer.steps.length==index+1){
|
||||
console.log("inside")
|
||||
insertPreview.updatePreviews(sequencer.steps[index-1].output.src, document.querySelector('#addStep'));
|
||||
}
|
||||
sequencer.removeSteps(index).run({ index: index - 1 });
|
||||
// remove from URL hash too
|
||||
urlHash.setUrlHashParameter('steps', sequencer.toString());
|
||||
|
||||
@@ -11,33 +11,27 @@
|
||||
const intermediateHtmlStepUi = require('./intermediateHtmlStepUi.js'),
|
||||
urlHash = require('./urlHash.js'),
|
||||
_ = require('lodash'),
|
||||
insertPreview = require('./insertPreview.js');
|
||||
mapHtmlTypes = require('./mapHtmltypes'),
|
||||
scopeQuery = require('./scopeQuery'),
|
||||
isGIF = require('../../src/util/isGif');
|
||||
scopeQuery = require('./scopeQuery');
|
||||
|
||||
function DefaultHtmlStepUi(_sequencer, options) {
|
||||
let $step, $stepAll;
|
||||
|
||||
options = options || {};
|
||||
var stepsEl = options.stepsEl || document.querySelector('#steps');
|
||||
var selectStepSel = options.selectStepSel = options.selectStepSel || '#selectStep';
|
||||
|
||||
function onSetup(step, stepOptions) {
|
||||
|
||||
if (step.options && step.options.description)
|
||||
step.description = step.options.description;
|
||||
|
||||
step.ui = // Basic UI markup for the step
|
||||
step.ui =
|
||||
'\
|
||||
<div class="container-fluid step-container">\
|
||||
<div class="panel panel-default">\
|
||||
<div class="panel-heading">\
|
||||
<div class="trash-container pull-right">\
|
||||
<a type="button" target="_blank" href="https://developer.mozilla.org/en-US/docs/WebAssembly" style="display: none;" class="btn btn-link general-tooltip wasm-tooltip" data-toggle="tooltip" data-html="true" data-original-title="<div style=\'text-align: center\'><p>This step is Web Assembly accelerated. Click to Read More</div>">\
|
||||
<i class="fa fa-bolt"></i>\
|
||||
</a>\
|
||||
<button type="button" class="btn btn-link ' + step.name + ' general-tooltip dimension-tooltip" data-toggle="tooltip" data-html="true" data-original-title="">\
|
||||
<i class="fa fa-info-circle"></i>\
|
||||
</button>\
|
||||
</div>\
|
||||
<div class="trash-container pull-right"><button type="button" class="btn btn-link ' + step.name + ' dimension-tooltip" data-toggle="tooltip" data-html="true" title="" data-original-title=""><i class="fa fa-info-circle"></i></button></div>\
|
||||
<h3 class="panel-title">' +
|
||||
'<span class="toggle mouse">' + step.name + ' <span class="caret toggleIcon rotated"></span>\
|
||||
<span class="load-spin pull-right" style="display:none;padding:1px 8px;"><i class="fa fa-circle-o-notch fa-spin"></i></span>\
|
||||
@@ -48,7 +42,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
<div class="row step">\
|
||||
<div class="col-md-4 details container-fluid">\
|
||||
<div class="cal collapse in"><p>' +
|
||||
'<a href="https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#' + step.name + '-module">' + (step.description || '') + '</a>' +
|
||||
'<i>' + (step.description || '') + '</i>' +
|
||||
'</p></div>\
|
||||
</div>\
|
||||
<div class="col-md-8 cal collapse in step-column">\
|
||||
@@ -74,20 +68,21 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
var util = intermediateHtmlStepUi(_sequencer, step);
|
||||
|
||||
var parser = new DOMParser();
|
||||
step.ui = parser.parseFromString(step.ui, 'text/html'); // Convert the markup string to a DOM node.
|
||||
step.ui = parser.parseFromString(step.ui, 'text/html');
|
||||
step.ui = step.ui.querySelector('div.container-fluid');
|
||||
|
||||
step.$step = scopeQuery.scopeSelector(step.ui); // Shorthand methods for scoped DOM queries. Read the docs [CONTRIBUTING.md](https://github.com/publiclab/image-sequencer/blob/main/CONTRIBUTING.md) for more info.
|
||||
step.$stepAll = scopeQuery.scopeSelectorAll(step.ui);
|
||||
let {$step, $stepAll} = step;
|
||||
$step = scopeQuery.scopeSelector(step.ui);
|
||||
$stepAll = scopeQuery.scopeSelectorAll(step.ui);
|
||||
step.ui.$step = $step;
|
||||
step.ui.$stepAll = $stepAll;
|
||||
|
||||
step.linkElements = step.ui.querySelectorAll('a'); // All the anchor tags in the step UI
|
||||
step.imgElement = $step('a img.img-thumbnail')[0]; // The output image
|
||||
step.linkElements = step.ui.querySelectorAll('a');
|
||||
step.imgElement = $step('a img.img-thumbnail')[0];
|
||||
|
||||
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = _sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = _sequencer.modulesInfo(step.name).outputs;
|
||||
var merged = Object.assign(inputs, outputs); // Combine outputs with inputs
|
||||
var merged = Object.assign(inputs, outputs); // combine outputs w inputs
|
||||
|
||||
for (var paramName in merged) {
|
||||
var isInput = inputs.hasOwnProperty(paramName);
|
||||
@@ -107,10 +102,10 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
else {
|
||||
let paramVal = step.options[paramName] || inputDesc.default;
|
||||
|
||||
if (inputDesc.id == 'color-picker') { // Separate input field for color-picker
|
||||
if (inputDesc.id == 'color-picker') { // separate input field for color-picker
|
||||
html +=
|
||||
'<div id="color-picker" class="input-group colorpicker-component">' +
|
||||
'<input class="form-control color-picker-target" type="' +
|
||||
'<input class="form-control target" type="' +
|
||||
inputDesc.type +
|
||||
'" name="' +
|
||||
paramName +
|
||||
@@ -118,7 +113,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
paramVal + '">' + '<span class="input-group-addon"><i></i></span>' +
|
||||
'</div>';
|
||||
}
|
||||
else { // Non color-picker input types
|
||||
else { // use this if the the field isn't color-picker
|
||||
html =
|
||||
'<input class="form-control target" type="' +
|
||||
inputDesc.type +
|
||||
@@ -128,7 +123,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
paramVal +
|
||||
'" placeholder ="' +
|
||||
(inputDesc.placeholder || '');
|
||||
|
||||
|
||||
if (inputDesc.type.toLowerCase() == 'range') {
|
||||
html +=
|
||||
'"min="' +
|
||||
@@ -160,10 +155,10 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
</div>';
|
||||
$step('div.details').append(div);
|
||||
}
|
||||
$step('div.panel-footer').append( // Save button
|
||||
$step('div.panel-footer').append(
|
||||
'<div class="cal collapse in"><button type="submit" class="btn btn-sm btn-default btn-save" disabled = "true" >Apply</button> <small style="padding-top:2px;">Press apply to see changes</small></div>'
|
||||
);
|
||||
$step('div.panel-footer').prepend( // Markup for tools: download and insert step buttons
|
||||
$step('div.panel-footer').prepend(
|
||||
'<button class="pull-right btn btn-default btn-sm insert-step" >\
|
||||
<span class="insert-text"><i class="fa fa-plus"></i> Insert Step</span><span class="no-insert-text" style="display:none">Close</span></button>\
|
||||
<button class="pull-right btn btn-default btn-sm download-btn" style="margin-right:2px" >\
|
||||
@@ -178,10 +173,9 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
parser.parseFromString(tools, 'text/html').querySelector('div')
|
||||
);
|
||||
|
||||
$stepAll('.remove').on('click', function() {notify('Step Removed', 'remove-notification');}); // Notification on removal of a step
|
||||
$step('.insert-step').on('click', function() { util.insertStep(step.ID); }); // Insert a step in between the sequence
|
||||
$stepAll('.remove').on('click', function() {notify('Step Removed', 'remove-notification');});
|
||||
$stepAll('.insert-step').on('click', function() { util.insertStep(step.ID); });
|
||||
// Insert the step's UI in the right place
|
||||
|
||||
if (stepOptions.index == _sequencer.steps.length) {
|
||||
stepsEl.appendChild(step.ui);
|
||||
$('#steps .step-container:nth-last-child(1) .insert-step').prop('disabled', true);
|
||||
@@ -191,30 +185,16 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
else {
|
||||
stepsEl.insertBefore(step.ui, $(stepsEl).children()[stepOptions.index]);
|
||||
}
|
||||
|
||||
// Enable the load-image insert-step button when there are steps after load-image
|
||||
// The logical operator is `> 0` because the number of steps is found before adding the step, actual logic is `steps.length + 1 > 1` which is later simplified.
|
||||
if (_sequencer.steps.length > 0) $('#load-image .insert-step').prop('disabled', false);
|
||||
else $('#load-image .insert-step').prop('disabled', true);
|
||||
}
|
||||
else {
|
||||
$('#load-image').append(step.ui); // Default UI without extra tools for the first step(load image)
|
||||
|
||||
$step('div.panel-footer').prepend( `
|
||||
<button class="right btn btn-default btn-sm insert-step" disabled="true">
|
||||
<span class="insert-text"><i class="fa fa-plus"></i> Insert Step</span>
|
||||
<span class="no-insert-text" style="display:none">Close</span>
|
||||
</button>`
|
||||
);
|
||||
|
||||
$step('.insert-step').on('click', function() { util.insertStep(step.ID); });
|
||||
$('#load-image').append(step.ui);
|
||||
}
|
||||
$step('.toggle').on('click', () => { // Step container dropdown
|
||||
$step('.toggle').on('click', () => {
|
||||
$step('.toggleIcon').toggleClass('rotated');
|
||||
$stepAll('.cal').collapse('toggle');
|
||||
});
|
||||
|
||||
$(step.imgElement).on('mousemove', _.debounce(() => imageHover(step), 150)); // Shows the pixel coordinates on hover
|
||||
|
||||
$(step.imgElement).on('mousemove', _.debounce(() => imageHover(step), 150));
|
||||
$(step.imgElement).on('click', (e) => {e.preventDefault(); });
|
||||
$stepAll('#color-picker').colorpicker();
|
||||
|
||||
@@ -231,23 +211,15 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
});
|
||||
_sequencer.run({ index: step.index - 1 });
|
||||
|
||||
// Modify the URL hash
|
||||
// modify the url hash
|
||||
urlHash.setUrlHashParameter('steps', _sequencer.toString());
|
||||
// Disable the save button
|
||||
// disable the save button
|
||||
$step('.btn-save').prop('disabled', true);
|
||||
optionsChanged = false;
|
||||
changedInputs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method handleInputValueChange
|
||||
* @description Enables the save button on input change
|
||||
* @param {*} currentValue The current value of the input
|
||||
* @param {*} initValue The initial/old value of the input
|
||||
* @param {Boolean} hasChangedBefore Whether the input was changed before
|
||||
* @returns {Boolean} True if the value has changed
|
||||
*/
|
||||
function handleInputValueChange(currentValue, initValue, hasChangedBefore) {
|
||||
var inputChanged = !(isNaN(initValue) || isNaN(currentValue) ? currentValue === initValue : currentValue - initValue === 0);
|
||||
changedInputs += hasChangedBefore ? inputChanged ? 0 : -1 : inputChanged ? 1 : 0;
|
||||
@@ -278,21 +250,6 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
});
|
||||
});
|
||||
|
||||
$stepAll('.color-picker-target').each(function(i, input) {
|
||||
$(input)
|
||||
.data('initValue', $(input).val())
|
||||
.data('hasChangedBefore', false)
|
||||
.on('input change', function() {
|
||||
$(this)
|
||||
.data('hasChangedBefore',
|
||||
handleInputValueChange(
|
||||
$(this).val(),
|
||||
$(this).data('initValue'),
|
||||
$(this).data('hasChangedBefore')
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('input[type="range"]').on('input', function() {
|
||||
@@ -301,20 +258,17 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
}
|
||||
|
||||
|
||||
function onDraw({$step, $stepAll}) {
|
||||
function onDraw() {
|
||||
$step('.load').show();
|
||||
$step('img').hide();
|
||||
$stepAll('.load-spin').show();
|
||||
}
|
||||
|
||||
function onComplete(step) {
|
||||
let {$step, $stepAll} = step;
|
||||
$step('img').show();
|
||||
$stepAll('.load-spin').hide();
|
||||
$step('.load').hide();
|
||||
|
||||
$stepAll('.download-btn').off('click');
|
||||
|
||||
step.imgElement.src = (step.name == 'load-image') ? step.output.src : step.output;
|
||||
var imgthumbnail = $step('.img-thumbnail').getDomElem();
|
||||
for (let index = 0; index < step.linkElements.length; index++) {
|
||||
@@ -322,23 +276,28 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
step.linkElements[index].href = step.imgElement.src;
|
||||
}
|
||||
|
||||
// TODO: use a generalized version of this.
|
||||
// TODO: use a generalized version of this
|
||||
function fileExtension(output) {
|
||||
return output.split('/')[1].split(';')[0];
|
||||
}
|
||||
|
||||
$stepAll('.download-btn').on('click', () => {
|
||||
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', step.output);
|
||||
element.setAttribute('download', step.name + '.' + fileExtension(step.imgElement.src));
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
for (let index = 0; index < step.linkElements.length; index++){
|
||||
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', step.linkElements[index].href);
|
||||
element.setAttribute('download', step.name + '.' + fileExtension(step.imgElement.src));
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
element.click();
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
});
|
||||
|
||||
// Fill inputs with stored step options
|
||||
// fill inputs with stored step options
|
||||
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
|
||||
var inputs = _sequencer.modulesInfo(step.name).inputs;
|
||||
var outputs = _sequencer.modulesInfo(step.name).outputs;
|
||||
@@ -350,7 +309,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
.data('initValue', step.options[i]);
|
||||
if (inputs[i].type.toLowerCase() === 'select')
|
||||
$step('div[name="' + i + '"] select')
|
||||
.val(String(step.options[i]))
|
||||
.val(step.options[i])
|
||||
.data('initValue', step.options[i]);
|
||||
}
|
||||
}
|
||||
@@ -363,38 +322,12 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
updateDimensions(step);
|
||||
});
|
||||
|
||||
if (step.name === 'load-image') insertPreview.updatePreviews(step.output.src, document.querySelector('#addStep'));
|
||||
else insertPreview.updatePreviews(step.output, document.querySelector('#addStep'));
|
||||
|
||||
// Handle the wasm bolt display
|
||||
|
||||
if (step.useWasm) {
|
||||
if (step.wasmSuccess) $step('.wasm-tooltip').fadeIn();
|
||||
else $step('.wasm-tooltip').fadeOut();
|
||||
}
|
||||
else $step('.wasm-tooltip').fadeOut();
|
||||
}
|
||||
/**
|
||||
* @description Updates Dimension of the image
|
||||
* @param {Object} step - Current Step
|
||||
* @returns {void}
|
||||
*
|
||||
*/
|
||||
function updateDimensions(step){
|
||||
_sequencer.getImageDimensions(step.imgElement.src, function (dim) {
|
||||
step.ui.querySelector('.' + step.name).attributes['data-original-title'].value = `<div style="text-align: center"><p>Image Width: ${dim.width}<br>Image Height: ${dim.height}</br>${isGIF(step.output) ? `Frames: ${dim.frames}` : ''}</div>`;
|
||||
_sequencer.getImageDimensions(step.imgElement.src, function (dim) {
|
||||
step.ui.querySelector('.' + step.name).attributes['data-original-title'].value = `<div style="text-align: center"><p>Image Width: ${dim.width}<br>Image Height: ${dim.height}</br></div>`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @method imageHover
|
||||
* @description Handler to display image coordinates on hover.
|
||||
* @param {Object} step Current step variable
|
||||
* @returns {Null}
|
||||
*/
|
||||
function imageHover(step){
|
||||
|
||||
var img = $(step.imgElement);
|
||||
@@ -417,42 +350,27 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
function onRemove(step) {
|
||||
step.ui.remove();
|
||||
$('#steps .step-container:nth-last-child(1) .insert-step').prop('disabled', true);
|
||||
|
||||
// Enable the load-image insert-step button when there are steps after load-image
|
||||
// The logical operator is `> 2` because the number of steps is found before removing the step, actual logic is `steps.length - 1 > 1` which is later simplified.
|
||||
if (_sequencer.steps.length - 1 > 1) $('#load-image .insert-step').prop('disabled', false);
|
||||
else $('#load-image .insert-step').prop('disabled', true);
|
||||
|
||||
$(step.imgElement).imgAreaSelect({
|
||||
remove: true
|
||||
});
|
||||
$('div[class*=imgareaselect-]').remove();
|
||||
}
|
||||
|
||||
function getPreview() {
|
||||
return step.imgElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method notify
|
||||
* @description General purpose DOM toast notification
|
||||
* @param {String} msg Message to be displayed
|
||||
* @param {String} id A unique identifier for the notification
|
||||
* @returns {Null}
|
||||
*/
|
||||
function notify(msg, id){
|
||||
if ($('#' + id).length == 0) {
|
||||
var notification = document.createElement('span');
|
||||
notification.innerHTML = ' <i class="fa fa-info-circle" aria-hidden="true"></i> ' + msg ;
|
||||
notification.id = id;
|
||||
notification.classList.add('notification');
|
||||
|
||||
|
||||
$('body').append(notification);
|
||||
}
|
||||
|
||||
|
||||
$('#' + id).fadeIn(500).delay(200).fadeOut(500);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return {
|
||||
getPreview: getPreview,
|
||||
onSetup: onSetup,
|
||||
@@ -460,8 +378,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
onRemove: onRemove,
|
||||
onDraw: onDraw,
|
||||
notify: notify,
|
||||
imageHover: imageHover,
|
||||
updateDimensions: updateDimensions
|
||||
imageHover: imageHover
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Generate downscaled preview images for quick buttons.
|
||||
function generatePreview(previewStepName, customValues, path, DomNode) {
|
||||
function generatePreview(previewStepName, customValues, path, selector) {
|
||||
|
||||
var previewSequencer = ImageSequencer();
|
||||
function insertPreview(src) {
|
||||
var img = document.createElement('img');
|
||||
@@ -8,10 +8,9 @@ function generatePreview(previewStepName, customValues, path, DomNode) {
|
||||
img.src = src;
|
||||
$(img).css('max-width', '200%');
|
||||
$(img).css('transform', 'translateX(-20%)');
|
||||
$(DomNode.querySelector('.radio-group')).find('.radio').each(function() {
|
||||
if ($(this).attr('data-value') === previewStepName) {
|
||||
$(this).find('img').remove();
|
||||
$(this).append(img);
|
||||
$(selector + ' .radio-group').find('div').each(function() {
|
||||
if ($(this).find('div').attr('data-value') === previewStepName) {
|
||||
$(this).find('div').append(img);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -30,8 +29,8 @@ function generatePreview(previewStepName, customValues, path, DomNode) {
|
||||
previewSequencer.loadImage(path, loadPreview);
|
||||
}
|
||||
|
||||
function updatePreviews(src, DomNode) {
|
||||
$(DomNode).find('img').remove();
|
||||
function updatePreviews(src, selector) {
|
||||
$(selector + ' img').remove();
|
||||
|
||||
var previewSequencerSteps = {
|
||||
'resize': '125%',
|
||||
@@ -42,20 +41,19 @@ function updatePreviews(src, DomNode) {
|
||||
'crop': {
|
||||
'x': 0,
|
||||
'y': 0,
|
||||
'w': '50%',
|
||||
'h': '50%',
|
||||
'w': '(50%)',
|
||||
'h': '(50%)',
|
||||
'noUI': true
|
||||
}
|
||||
};
|
||||
|
||||
var img = new Image();
|
||||
|
||||
img.onload = function(){
|
||||
var height = img.height;
|
||||
var width = img.width;
|
||||
|
||||
let percentage = (80 / height) * 100; // Take the min resize value that fits the preview area => (new-width/orig_ht) - '80 as the preview area has 80*80 dimension.
|
||||
percentage = Math.max((80 / width) * 100, percentage); // Make sure that one dimension doesn't resize greater, leading distorting preview-area fitting.
|
||||
let percentage = (80 / height) * 100; //take the min resize value that fits the preview area => (new-width/orig_ht) - '80 as the preview area has 80*80 dimension
|
||||
percentage = Math.max((80 / width) * 100, percentage); // make sure that one dimension doesn't resize greater, leading distorting preview-area fitting
|
||||
percentage = Math.ceil(percentage);
|
||||
|
||||
var sequencer = ImageSequencer();
|
||||
@@ -64,7 +62,7 @@ function updatePreviews(src, DomNode) {
|
||||
this.addSteps('resize', {['resize']: percentage + '%'});
|
||||
this.run((src)=>{
|
||||
Object.keys(previewSequencerSteps).forEach(function (step, index) {
|
||||
generatePreview(step, Object.values(previewSequencerSteps)[index], src, DomNode);
|
||||
generatePreview(step, Object.values(previewSequencerSteps)[index], src, selector);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -76,4 +74,4 @@ function updatePreviews(src, DomNode) {
|
||||
module.exports = {
|
||||
generatePreview : generatePreview,
|
||||
updatePreviews : updatePreviews
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
var urlHash = require('./urlHash.js'),
|
||||
insertPreview = require('./insertPreview.js');
|
||||
|
||||
/**
|
||||
* @method IntermediateHtmlStepUi
|
||||
* @description Inserts a module selector in between the current sequence
|
||||
* @param {Object} _sequencer Sequencer instance
|
||||
* @param {Object} step Current step variable
|
||||
* @param {Object} options Optional options Object
|
||||
* @returns {Object} Object containing the insertStep function
|
||||
*/
|
||||
function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
function stepUI() {
|
||||
// Basic markup for the selector
|
||||
return '<div class="row insertDiv collapse">\
|
||||
<section class="panel panel-primary .insert-step">\
|
||||
<button class="btn btn-default close-insert-box"><i class="fa fa-times" aria-hidden="true"></i> Close</button>\
|
||||
@@ -64,7 +55,7 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
</select>\
|
||||
<div>\
|
||||
<div class="col-md-4">\
|
||||
<button class="btn btn-primary btn-lg insert-save-btn add-step-btn" name="add">Add Step</button>\
|
||||
<button class="btn btn-success btn-lg insert-save-btn add-step-btn" name="add">Add Step</button>\
|
||||
<div>\
|
||||
</div>\
|
||||
</div>\
|
||||
@@ -73,13 +64,6 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @method toggleDiv
|
||||
* @description Toggles the module selector dropdown.
|
||||
* @param {Object} $step $step util function
|
||||
* @param {Fucntion} callback Optional callback function
|
||||
* @returns {Null}
|
||||
*/
|
||||
var toggleDiv = function($step, callback = function(){}){
|
||||
$step('.insertDiv').collapse('toggle');
|
||||
if ($step('.insert-text').css('display') != 'none'){
|
||||
@@ -90,19 +74,13 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method insertStep
|
||||
* @description Handler to insert selected module in the sequence
|
||||
* @returns {Null}
|
||||
*/
|
||||
insertStep = function (id) {
|
||||
const $step = step.$step,
|
||||
$stepAll = step.$stepAll;
|
||||
const $step = step.ui.$step,
|
||||
$stepAll = step.ui.$stepAll;
|
||||
var modulesInfo = _sequencer.modulesInfo();
|
||||
var parser = new DOMParser();
|
||||
var addStepUI = stepUI();
|
||||
addStepUI = parser.parseFromString(addStepUI, 'text/html').querySelector('div');
|
||||
|
||||
if ($step('.insertDiv').length > 0){
|
||||
toggleDiv($step);
|
||||
}
|
||||
@@ -113,20 +91,17 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
addStepUI
|
||||
);
|
||||
toggleDiv($step, function(){
|
||||
if (step.name === 'load-image') insertPreview.updatePreviews(step.output.src, $step('.insertDiv').getDomElem());
|
||||
else insertPreview.updatePreviews(step.output, $step('.insertDiv').getDomElem());
|
||||
insertPreview.updatePreviews(step.output, '.insertDiv');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$step('.insertDiv .close-insert-box').off('click').on('click', function(){
|
||||
toggleDiv($step);
|
||||
$step('.insertDiv').removeClass('insertDiv');
|
||||
});
|
||||
|
||||
|
||||
var insertStepSelect = $step('.insert-step-select');
|
||||
insertStepSelect.html('');
|
||||
|
||||
// Add modules to the insertStep dropdown
|
||||
for (var m in modulesInfo) {
|
||||
if (modulesInfo[m] && modulesInfo[m].name)
|
||||
@@ -134,30 +109,20 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
'<option value="' + m + '">' + modulesInfo[m].name + '</option>'
|
||||
);
|
||||
}
|
||||
|
||||
insertStepSelect.selectize({
|
||||
sortField: 'text'
|
||||
});
|
||||
|
||||
$('.insertDiv .radio-group .radio').on('click', function () {
|
||||
var newStepName = $(this).attr('data-value'); // Get the name of the module to be inserted
|
||||
var newStepName = $(this).attr('data-value');
|
||||
id = $($step('.insertDiv').parents()[3]).prevAll().length;
|
||||
insert(id, $step, newStepName); // Insert the selected module
|
||||
insert(id, $step, newStepName);
|
||||
});
|
||||
|
||||
$step('.insertDiv .add-step-btn').on('click', function () {
|
||||
var newStepName = insertStepSelect.val();
|
||||
id = $($step('.insertDiv').parents()[3]).prevAll().length;
|
||||
insert(id, $step, newStepName); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @method insert
|
||||
* @description Inserts the specified step at the specified index in the sequence
|
||||
* @param {Number} id Index of the step
|
||||
* @param {Function} $step $step util function
|
||||
* @param {String} newStepName Name of the new step
|
||||
*/
|
||||
function insert(id, $step, newStepName) {
|
||||
toggleDiv($step);
|
||||
$step('.insertDiv').removeClass('insertDiv');
|
||||
@@ -169,4 +134,4 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
|
||||
insertStep
|
||||
};
|
||||
}
|
||||
module.exports = IntermediateHtmlStepUi;
|
||||
module.exports = IntermediateHtmlStepUi;
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @description Maps module input types to their respective html <input> tag types.
|
||||
* @param {Object} inputInfo Object containing the type and optionally min/max for range type inputs.
|
||||
*/
|
||||
function mapHtmlTypes(inputInfo){
|
||||
var htmlType;
|
||||
switch(inputInfo.type.toLowerCase()){
|
||||
@@ -24,9 +20,9 @@ function mapHtmlTypes(inputInfo){
|
||||
htmlType = 'text';
|
||||
break;
|
||||
}
|
||||
var response = Object.assign({}, inputInfo);
|
||||
var response = inputInfo;
|
||||
response.type = htmlType;
|
||||
return response;
|
||||
}
|
||||
|
||||
module.exports = mapHtmlTypes;
|
||||
module.exports = mapHtmlTypes;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @method $scope
|
||||
* @param {"DOMNode"} scope A DOM Node as the scope
|
||||
* @returns {Function} Constructor for the scopeSelector Object.
|
||||
*/
|
||||
function $scope(scope) {
|
||||
return function(queryString){
|
||||
@@ -25,7 +24,6 @@ function $scope(scope) {
|
||||
/**
|
||||
* @method $scopeAll
|
||||
* @param {"DOMNode"} scope A DOM Node as the scope
|
||||
* @returns {Function} Constructor for the scopeSelectorAll Object.
|
||||
*/
|
||||
function $scopeAll(scope){
|
||||
return function(queryString){
|
||||
@@ -49,8 +47,7 @@ function $scopeAll(scope){
|
||||
/**
|
||||
* @method scopeSelector
|
||||
* @description A scoped jQuery selector
|
||||
* @param {"DOMNode"} scope A DOM Node as the scope
|
||||
* @returns {Function}
|
||||
* @param {"DOMNode"} scope DOM Node as the scope
|
||||
*/
|
||||
function scopeSelector(scope){
|
||||
return $scope(scope);
|
||||
@@ -59,8 +56,7 @@ function scopeSelector(scope){
|
||||
/**
|
||||
* @method scopeSelectorAll
|
||||
* @description A scoped jQuery multiple selector
|
||||
* @param {"DOMNode} scope A DOM Node as the scope
|
||||
* @returns {Function}
|
||||
* @param {"DOMNode} scope DOM Node as the scope
|
||||
*/
|
||||
function scopeSelectorAll(scope){
|
||||
return $scopeAll(scope);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Functions for getting version information.
|
||||
* Note: these functions are not used by the service worker to check for updates;
|
||||
* the service worker updates whenever sw.js has changed.
|
||||
* sw.js is changed when grunt replace:version is run. This task is run during
|
||||
* grunt build, serve, and productions tasks.
|
||||
*/
|
||||
|
||||
const package = require('../../package.json');
|
||||
|
||||
/**
|
||||
* Get the current version number from package.json on the homepage.
|
||||
* @param {function} callback The function that uses the version number.
|
||||
*/
|
||||
function getLatestVersionNumber(callback) {
|
||||
// Get the homepage reference from the local package.json.
|
||||
var homepage = package.homepage;
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4 && request.status == 200) {
|
||||
var response = JSON.parse(this.responseText);
|
||||
var latestVersionNumber = response.version;
|
||||
|
||||
// Do something with the version number using a callback function.
|
||||
if (callback)
|
||||
callback(latestVersionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the package.json file from online using a GET request.
|
||||
request.open("GET", homepage + "/package.json", true);
|
||||
request.send();
|
||||
}
|
||||
|
||||
// Get the version number from the local package.json file.
|
||||
function getLocalVersionNumber() {
|
||||
return package.version;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLatestVersionNumber,
|
||||
getLocalVersionNumber
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const staticCacheName = 'image-sequencer-static-v3.5.1';
|
||||
const staticCacheName = 'image-sequencer-static-v3';
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Attempting to install service worker');
|
||||
});
|
||||
@@ -32,10 +33,3 @@ self.addEventListener('fetch', function(event) {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// When the update modal sends a 'skipWaiting' message, call the skipWaiting method.
|
||||
self.addEventListener('message', function(event) {
|
||||
if(event.data.action === 'skipWaiting') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
module.exports = {
|
||||
launch: {
|
||||
headless: process.env.HEADLESS !== 'false',
|
||||
},
|
||||
server: {
|
||||
command: 'grunt serve',
|
||||
port:3000,
|
||||
launchTimeout: 5000000,
|
||||
},
|
||||
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'jest-puppeteer',
|
||||
testRegex: './*\\.test\\.js$',
|
||||
verbose: true,
|
||||
};
|
||||
|
||||
6299
package-lock.json
generated
6299
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@@ -5,14 +5,9 @@
|
||||
"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\"",
|
||||
"test-all": "npm run test && npm run benchmark && npm run gif-test && grunt tests && npm run core-tests",
|
||||
"test": "TEST=true istanbul cover tape test/core/*.js test/core/ui/user-interface.js test/core/modules/*.js | tap-spec; node test/core/sequencer/benchmark.js; browserify 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 | tape-run --render=\"tap-spec\"",
|
||||
"test-ui": "node node_modules/jasmine/bin/jasmine test/ui/spec/*.js",
|
||||
"test-ui-2": "node ./node_modules/.bin/jest",
|
||||
"setup": "npm i && npm i -g grunt grunt-cli && npm rebuild --build-from-source && grunt build",
|
||||
"setup": "npm i && npm i -g grunt grunt-cli && grunt build",
|
||||
"start": "grunt serve"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -37,12 +32,12 @@
|
||||
"dependencies": {
|
||||
"atob": "^2.1.2",
|
||||
"base64-img": "^1.0.4",
|
||||
"bootstrap": "^3.4.1",
|
||||
"bootstrap": "~3.4.0",
|
||||
"bootstrap-colorpicker": "^2.5.3",
|
||||
"buffer": "~5.6.0",
|
||||
"commander": "^4.0.1",
|
||||
"buffer": "~5.4.0",
|
||||
"commander": "^3.0.1",
|
||||
"compressorjs": "^1.0.5",
|
||||
"data-uri-to-buffer": "^3.0.0",
|
||||
"data-uri-to-buffer": "^2.0.0",
|
||||
"downloadjs": "^1.4.7",
|
||||
"eslint": "^6.1.0",
|
||||
"fisheyegl": "^0.1.2",
|
||||
@@ -51,26 +46,23 @@
|
||||
"get-pixels": "~3.3.0",
|
||||
"gifshot": "^0.4.5",
|
||||
"glfx": "0.0.4",
|
||||
"gpu.js": "^2.3.1",
|
||||
"gpu.js": "^2.0.0-rc.12",
|
||||
"image-sequencer-invert": "^1.0.0",
|
||||
"imagejs": "0.0.9",
|
||||
"imagemin": "^7.0.1",
|
||||
"imagemin": "^7.0.0",
|
||||
"imagemin-jpegtran": "^6.0.0",
|
||||
"imagemin-pngquant": "^8.0.0",
|
||||
"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",
|
||||
"jquery": "^3.3.1",
|
||||
"jsdom": "^15.0.0",
|
||||
"jspdf": "^1.5.3",
|
||||
"jsqr": "^1.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"ndarray": "^1.0.18",
|
||||
"opencv.js": "^1.2.1",
|
||||
"ora": "^4.0.3",
|
||||
"ora": "^3.0.0",
|
||||
"pace": "0.0.4",
|
||||
"pngquant-bin": "^5.0.2",
|
||||
"puppeteer": "^1.14.0",
|
||||
"qrcode": "^1.3.3",
|
||||
"readline-sync": "^1.4.7",
|
||||
@@ -85,7 +77,7 @@
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.4.3",
|
||||
"@babel/plugin-syntax-object-rest-spread": "^7.2.0",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "16.5.0",
|
||||
"browserify": "16.2.3",
|
||||
"eslint": "^6.1.0",
|
||||
"grunt": "^1.0.3",
|
||||
"grunt-browser-sync": "^2.2.0",
|
||||
@@ -93,21 +85,18 @@
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-uglify-es": "^3.3.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-text-replace": "^0.4.0",
|
||||
"husky": "^3.0.5",
|
||||
"image-filter-core": "~2.0.2",
|
||||
"image-filter-threshold": "~2.0.1",
|
||||
"jasmine-core": "^3.3.0",
|
||||
"jasmine-jquery": "^2.1.1",
|
||||
"jasmine-spec-reporter": "^4.2.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest-puppeteer": "^4.3.0",
|
||||
"lint-staged": "^10.0.3",
|
||||
"lint-staged": "^9.1.0",
|
||||
"looks-same": "^7.0.0",
|
||||
"matchdep": "^2.0.0",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^4.9.2",
|
||||
"tape-run": "^7.0.0",
|
||||
"tape-run": "^6.0.0",
|
||||
"uglify-es": "^3.3.7"
|
||||
},
|
||||
"husky": {
|
||||
@@ -119,4 +108,4 @@
|
||||
"bin": {
|
||||
"sequencer": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,6 @@ if (typeof window !== 'undefined') { isBrowser = true; }
|
||||
else { var isBrowser = false; }
|
||||
require('./util/getStep.js');
|
||||
|
||||
/**
|
||||
* @method ImageSequencer
|
||||
* @param {Object|Float32Array} options Optional options
|
||||
* @returns {Object}
|
||||
*/
|
||||
ImageSequencer = function ImageSequencer(options) {
|
||||
|
||||
var str = require('./Strings.js')(this.steps, modulesInfo, addSteps, copy);
|
||||
@@ -19,12 +14,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return Object.prototype.toString.call(object).split(' ')[1].slice(0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @method log
|
||||
* @description Logs colored messages to the console using ASCII color codes
|
||||
* @param {String} color ASCII color code
|
||||
* @param {String} msg Message to be logged to the console
|
||||
*/
|
||||
function log(color, msg) {
|
||||
if (options.ui != 'none') {
|
||||
if (arguments.length == 1) console.log(arguments[0]);
|
||||
@@ -32,12 +21,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method copy
|
||||
* @description Returns a clone of the input object.
|
||||
* @param {Object|Float32Array} a The Object/Array to be cloned
|
||||
* @returns {Object|Float32Array}
|
||||
*/
|
||||
function copy(a) {
|
||||
if (!typeof (a) == 'object') return a;
|
||||
if (objTypeOf(a) == 'Array') return a.slice();
|
||||
@@ -70,10 +53,10 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
for (o in sequencer) {
|
||||
modules[o] = sequencer[o];
|
||||
}
|
||||
sequences = JSON.parse(window.localStorage.getItem('sequences')); // Get saved sequences from localStorage
|
||||
sequences = JSON.parse(window.localStorage.getItem('sequences'));
|
||||
if (!sequences) {
|
||||
sequences = {};
|
||||
window.localStorage.setItem('sequences', JSON.stringify(sequences)); // Set the localStorage entry as an empty Object by default
|
||||
window.localStorage.setItem('sequences', JSON.stringify(sequences));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +64,11 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
// if (options.imageSelect || options.inBrowser) addStep('image-select');
|
||||
// else if (options.imageUrl) loadImage(imageUrl);
|
||||
|
||||
/**
|
||||
* @method addSteps
|
||||
* @description Adds one of more steps to the sequence.
|
||||
* @return {Object}
|
||||
*/
|
||||
function addSteps() {
|
||||
var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer;
|
||||
var args = [];
|
||||
var json_q = {};
|
||||
for (var arg in arguments) { args.push(copy(arguments[arg])); } // Get all the module names from the arguments
|
||||
for (var arg in arguments) { args.push(copy(arguments[arg])); }
|
||||
json_q = formatInput.call(this_, args, '+');
|
||||
|
||||
inputlog.push({ method: 'addSteps', json_q: copy(json_q) });
|
||||
@@ -99,28 +77,17 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method removeStep
|
||||
* @description Removes the step at the specified index from the sequence.
|
||||
* @param {Object} ref ImageSequencer instance
|
||||
* @param {Number} index Index of the step to be removed
|
||||
* @returns {Null}
|
||||
*/
|
||||
function removeStep(ref, index) {
|
||||
// Remove the step from images[image].steps and redraw remaining images
|
||||
//remove the step from images[image].steps and redraw remaining images
|
||||
if (index > 0) {
|
||||
// var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
|
||||
//var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
|
||||
thisStep = ref.steps[index];
|
||||
thisStep.UI.onRemove(thisStep.options.step);
|
||||
ref.steps.splice(index, 1);
|
||||
}
|
||||
//tell the UI a step has been removed
|
||||
}
|
||||
|
||||
/**
|
||||
* @method removeSteps
|
||||
* @description Removes one or more steps from the sequence
|
||||
* @returns {Object}
|
||||
*/
|
||||
function removeSteps() {
|
||||
var indices;
|
||||
var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer;
|
||||
@@ -136,11 +103,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method insertSteps
|
||||
* @description Inserts steps at the specified index
|
||||
* @returns {Object}
|
||||
*/
|
||||
function insertSteps() {
|
||||
var this_ = (this.name == 'ImageSequencer') ? this : this.sequencer;
|
||||
var args = [];
|
||||
@@ -156,11 +118,8 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method run
|
||||
* @param {Object} config Object which contains the runtime configuration like progress bar information and index from which the sequencer should run.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
// Config is an object which contains the runtime configuration like progress bar
|
||||
// information and index from which the sequencer should run
|
||||
function run(config) {
|
||||
var progressObj, index = 0;
|
||||
config = config || { mode: 'no-arg' };
|
||||
@@ -178,7 +137,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
var callback = function() { };
|
||||
for (var arg in args)
|
||||
if (objTypeOf(args[arg]) == 'Function')
|
||||
callback = args.splice(arg, 1)[0]; // Callback is formed
|
||||
callback = args.splice(arg, 1)[0]; //callback is formed
|
||||
|
||||
var json_q = formatInput.call(this_, args, 'r');
|
||||
|
||||
@@ -187,11 +146,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method loadImages
|
||||
* @description Loads an image via dataURL or normal URL. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info.
|
||||
* @returns {Null}
|
||||
*/
|
||||
function loadImages() {
|
||||
var args = [];
|
||||
var prevSteps = this.getSteps().slice(1).map(step=>step.options.name);
|
||||
@@ -228,35 +182,17 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @method replaceImage
|
||||
* @description Replaces the current image in the sequencer
|
||||
* @param {String} selector DOM selector string for the image input
|
||||
* @param {*} steps Current steps Object
|
||||
* @param {Object} options
|
||||
* @returns {*}
|
||||
*/
|
||||
function replaceImage(selector, steps, options) {
|
||||
options = options || {};
|
||||
options.callback = options.callback || function() { };
|
||||
return require('./ReplaceImage')(this, selector, steps, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @method getSteps
|
||||
* @description Returns the current sequence of steps
|
||||
* @returns {Object}
|
||||
*/
|
||||
//returns the steps added
|
||||
function getSteps(){
|
||||
return this.steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method setUI
|
||||
* @description To set up a UI for ImageSequencer via different callback methods. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info.
|
||||
* @param {Object} UI Object containing UI callback methods. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for more info.
|
||||
* @returns {Null}
|
||||
*/
|
||||
function setUI(UI) {
|
||||
this.events = require('./ui/UserInterface')(UI);
|
||||
}
|
||||
@@ -265,12 +201,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return require('./ExportBin')(dir, this, basic, filename);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method modulesInfo
|
||||
* @description Returns information about the given module or all the available modules
|
||||
* @param {String} name Module name
|
||||
* @returns {Object}
|
||||
*/
|
||||
function modulesInfo(name) {
|
||||
var modulesdata = {};
|
||||
if (name == 'load-image') return {};
|
||||
@@ -292,30 +222,23 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return modulesdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method loadNewModule
|
||||
* @description Adds a new local module to sequencer. Read the docs(https://github.com/publiclab/image-sequencer/blob/main/README.md) for mode info.
|
||||
* @param {String} name Name of the new module
|
||||
* @param {Object} options An Object containing path and info about the new module.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function loadNewModule(name, options) {
|
||||
|
||||
if (!options) {
|
||||
return this;
|
||||
|
||||
} else if (Array.isArray(options)) {
|
||||
// Contains the array of module and info
|
||||
// contains the array of module and info
|
||||
this.modules[name] = options;
|
||||
|
||||
} else if (options.func && options.info) {
|
||||
// Passed in options object
|
||||
// passed in options object
|
||||
this.modules[name] = [
|
||||
options.func, options.info
|
||||
];
|
||||
|
||||
} else if (options.path && !this.inBrowser) {
|
||||
// Load from path(only in node)
|
||||
// load from path(only in node)
|
||||
const module = [
|
||||
require(`${options.path}/Module.js`),
|
||||
require(`${options.path}/info.json`)
|
||||
@@ -325,13 +248,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @method saveNewModule
|
||||
* @description Saves a new local module to ImageSequencer
|
||||
* @param {String} name Name of the new module
|
||||
* @param {String} path Path to the new module
|
||||
* @returns {Null}
|
||||
*/
|
||||
function saveNewModule(name, path) {
|
||||
if (options.inBrowser) {
|
||||
// Not for browser context
|
||||
@@ -342,13 +258,6 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
fs.writeFileSync('./src/Modules.js', mods);
|
||||
}
|
||||
|
||||
/**
|
||||
* @method saveSequence
|
||||
* @description Saves a sequence on the browser localStorage.
|
||||
* @param {String} name Name for the sequence
|
||||
* @param {String} sequenceString Sequence data as a string
|
||||
* @returns {Null}
|
||||
*/
|
||||
function saveSequence(name, sequenceString) { // 4. save sequence
|
||||
const sequence = str.stringToJSON(sequenceString);
|
||||
// Save the given sequence string as a module
|
||||
@@ -367,7 +276,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
}
|
||||
|
||||
function loadModules() {
|
||||
// loadModules function loads the modules and saved sequences.
|
||||
// This function loads the modules and saved sequences
|
||||
this.modules = require('./Modules');
|
||||
if (options.inBrowser)
|
||||
this.sequences = JSON.parse(window.localStorage.getItem('sequences'));
|
||||
@@ -377,7 +286,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
|
||||
|
||||
return {
|
||||
// Literals and objects
|
||||
//literals and objects
|
||||
name: 'ImageSequencer',
|
||||
options: options,
|
||||
inputlog: inputlog,
|
||||
@@ -387,7 +296,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
steps: steps,
|
||||
image: image,
|
||||
|
||||
// User functions
|
||||
//user functions
|
||||
loadImages: loadImages,
|
||||
loadImage: loadImages,
|
||||
addSteps: addSteps,
|
||||
@@ -416,7 +325,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
loadModules: loadModules,
|
||||
getSteps:getSteps,
|
||||
|
||||
// Other functions
|
||||
//other functions
|
||||
log: log,
|
||||
objTypeOf: objTypeOf,
|
||||
copy: copy,
|
||||
|
||||
@@ -11,10 +11,8 @@ module.exports = {
|
||||
'canvas-resize': require('./modules/CanvasResize'),
|
||||
'channel': require('./modules/Channel'),
|
||||
'colorbar': require('./modules/Colorbar'),
|
||||
'color-halftone': require('./modules/ColorHalftone'),
|
||||
'color-temperature': require('./modules/ColorTemperature'),
|
||||
'colormap': require('./modules/Colormap'),
|
||||
'constrained-crop': require('./modules/ConstrainedCrop'),
|
||||
'contrast': require('./modules/Contrast'),
|
||||
'convolution': require('./modules/Convolution'),
|
||||
'crop': require('./modules/Crop'),
|
||||
@@ -32,8 +30,7 @@ module.exports = {
|
||||
'grid-overlay': require('./modules/GridOverlay'),
|
||||
'import-image': require('./modules/ImportImage'),
|
||||
'minify-image': require('./modules/MinifyImage'),
|
||||
// 'invert': require('image-sequencer-invert'),
|
||||
'invert': require('./modules/Invert'),
|
||||
'invert': require('image-sequencer-invert'),
|
||||
'ndvi': require('./modules/Ndvi'),
|
||||
'ndvi-colormap': require('./modules/NdviColormap'),
|
||||
'noise-reduction': require('./modules/NoiseReduction'),
|
||||
|
||||
@@ -45,8 +45,6 @@ function Run(ref, json_q, callback, ind, progressObj) {
|
||||
|
||||
// This output is accessible by UI
|
||||
ref.steps[i].options.step.output = ref.steps[i].output.src;
|
||||
ref.steps[i].options.step.wasmSuccess = ref.steps[i].output.wasmSuccess || false;
|
||||
ref.steps[i].options.step.useWasm = ref.steps[i].output.useWasm || false;
|
||||
|
||||
// Tell UI that step has been drawn.
|
||||
ref.steps[i].UI.onComplete(ref.steps[i].options.step);
|
||||
|
||||
@@ -1,58 +1,46 @@
|
||||
const _ = require('lodash'),
|
||||
parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
module.exports = function AddQR(options, UI) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.size = options.size || defaults.size;
|
||||
options.qrCodeString = options.qrCodeString || 'https://github.com/publiclab/image-sequencer';
|
||||
var output;
|
||||
getPixels = require('get-pixels');
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
options.size = Number(options.size || defaults.size);
|
||||
options.qrCodeString = options.qrCodeString || defaults.qrCodeString;
|
||||
options.startingX = options.startingX || defaults.startingX;
|
||||
options.startingY = options.startingY || defaults.startingY;
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function extraManipulation(pixels, setRenderState, generateOutput) {
|
||||
let iw = pixels.shape[0], // Width of Original Image
|
||||
ih = pixels.shape[1]; // Height of Original Image
|
||||
const oldPixels = _.cloneDeep(pixels);
|
||||
setRenderState(false); // Prevent rendering of final output image until extraManipulation completes.
|
||||
return getPixels(input.src, function(err, oldPixels) {
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
// Parse the inputs.
|
||||
parseCornerCoordinateInputs({iw, ih},
|
||||
{
|
||||
startingX: { valInp: options.startingX, type: 'horizontal'},
|
||||
startingY: { valInp: options.startingY, type: 'vertical' },
|
||||
}, function(opt, cord){
|
||||
options.startingX = Math.floor(cord.startingX.valInp);
|
||||
options.startingY = Math.floor(cord.startingY.valInp);
|
||||
});
|
||||
|
||||
require('./QR')(options, pixels, oldPixels, () => {
|
||||
setRenderState(true); // Allow rendering in the callback.
|
||||
generateOutput();
|
||||
function extraManipulation(pixels, generateOutput) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
require('./QR')(options, pixels, oldPixels, generateOutput);
|
||||
}
|
||||
function output(image, datauri, mimetype) {
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,37 +1,44 @@
|
||||
const pixelSetter = require('../../util/pixelSetter.js'),
|
||||
getPixels = require('get-pixels'),
|
||||
QRCode = require('qrcode');
|
||||
module.exports = exports = function (options, pixels, oldPixels, cb) {
|
||||
module.exports = exports = function (options, pixels, oldPixels, callback) {
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
QRCode.toDataURL(options.qrCodeString, {width: options.size, scale: 1}, function (error, url) {
|
||||
var QRCode = require('qrcode');
|
||||
QRCode.toDataURL(options.qrCodeString, function (err, url) {
|
||||
var getPixels = require('get-pixels');
|
||||
getPixels(url, function (err, qrPixels) {
|
||||
if (err) {
|
||||
console.log('get-pixels error: ', err);
|
||||
console.log('Bad image path', image);
|
||||
}
|
||||
|
||||
const width = oldPixels.shape[0],
|
||||
var imagejs = require('imagejs');
|
||||
var bitmap = new imagejs.Bitmap({ width: qrPixels.shape[0], height: qrPixels.shape[1] });
|
||||
bitmap._data.data = qrPixels.data;
|
||||
var resized = bitmap.resize({
|
||||
width: options.size, height: options.size,
|
||||
algorithm: 'bicubicInterpolation'
|
||||
});
|
||||
|
||||
qrPixels.data = resized._data.data;
|
||||
qrPixels.shape = [options.size, options.size, 4];
|
||||
qrPixels.stride[1] = 4 * options.size;
|
||||
|
||||
var width = oldPixels.shape[0],
|
||||
height = oldPixels.shape[1];
|
||||
var xe = width - options.size,
|
||||
ye = height - options.size;
|
||||
for (var m = 0; m < width; m++) {
|
||||
for (var n = 0; n < height; n++) {
|
||||
if (m >= xe && n >= ye) {
|
||||
pixelSetter(m, n, [qrPixels.get(m - xe, n - ye, 0), qrPixels.get(m - xe, n - ye, 1), qrPixels.get(m - xe, n - ye, 2), qrPixels.get(m - xe, n - ye, 3)], pixels);
|
||||
}
|
||||
|
||||
const xe = Math.min(options.startingX, width - options.size), // Starting pixel coordinates
|
||||
ye = Math.min(options.startingY, height - options.size);
|
||||
else {
|
||||
pixelSetter(m, n, [oldPixels.get(m, n, 0), oldPixels.get(m, n, 1), oldPixels.get(m, n, 2), oldPixels.get(m, n, 3)], pixels);
|
||||
}
|
||||
|
||||
for (let x = xe; x < Math.min(xe + options.size, width); x++) {
|
||||
for (let y = ye; y < Math.min(ye + options.size, height); y++) {
|
||||
pixelSetter(
|
||||
x,
|
||||
y,
|
||||
[
|
||||
qrPixels.get(x - xe, y - ye, 0),
|
||||
qrPixels.get(x - xe, y - ye, 1),
|
||||
qrPixels.get(x - xe, y - ye, 2),
|
||||
qrPixels.get(x - xe, y - ye, 3)
|
||||
],
|
||||
pixels
|
||||
);
|
||||
}
|
||||
}
|
||||
callback();
|
||||
|
||||
if(cb) cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -12,16 +12,6 @@
|
||||
"type": "string",
|
||||
"desc": "input string to generate QR code",
|
||||
"default": "https://github.com/publiclab/image-sequencer"
|
||||
},
|
||||
"startingX": {
|
||||
"type": "string",
|
||||
"desc": "X-position (measured from left) from where QR starts",
|
||||
"default": "0"
|
||||
},
|
||||
"startingY": {
|
||||
"type": "string",
|
||||
"desc": "Y-position (measured from top) from where QR starts",
|
||||
"default": "0"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#add-qr-module"
|
||||
|
||||
@@ -16,6 +16,7 @@ module.exports = function Average(options, UI) {
|
||||
|
||||
// do the averaging
|
||||
function extraManipulation(pixels) {
|
||||
const $ = window.$;
|
||||
var i = 0, sum = [0, 0, 0, 0];
|
||||
while (i < pixels.data.length) {
|
||||
sum[0] += pixels.data[i++];
|
||||
@@ -42,19 +43,23 @@ module.exports = function Average(options, UI) {
|
||||
// report back and store average in metadata:
|
||||
options.step.metadata.averages = sum;
|
||||
|
||||
if (options.step.average === undefined) options.step.average = '';
|
||||
options.step.average += 'rgba(' + sum.join(', ') + ')';
|
||||
// TODO: refactor into a new "display()" method as per https://github.com/publiclab/image-sequencer/issues/242
|
||||
if (options.step.inBrowser && options.step.ui) $(options.step.ui).find('.details').append('<p><b>Averages</b> (r, g, b, a): ' + sum.join(', ') + '</p>');
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = {
|
||||
src: datauri,
|
||||
format: mimetype
|
||||
};
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
inBrowser: options.inBrowser,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
|
||||
@@ -3,11 +3,5 @@
|
||||
"description": "Average all pixel color",
|
||||
"inputs": {
|
||||
},
|
||||
"outputs": {
|
||||
"average": {
|
||||
"type": "string",
|
||||
"desc": "The average value of all the pixels."
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#average-module"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
module.exports = function Blend(options, UI, util) {
|
||||
module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
options.func = options.blend || defaults.blend;
|
||||
options.func = options.func || defaults.blend;
|
||||
options.offset = options.offset || defaults.offset;
|
||||
options.blendMode = options.blendMode || defaults.blendMode;
|
||||
|
||||
var output;
|
||||
|
||||
@@ -16,6 +15,9 @@ module.exports = function Blend(options, UI, util) {
|
||||
|
||||
var step = this;
|
||||
|
||||
// convert to runnable code:
|
||||
if (typeof options.func === 'string') eval('options.func = ' + options.func);
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
|
||||
// convert offset as string to int
|
||||
@@ -30,83 +32,26 @@ module.exports = function Blend(options, UI, util) {
|
||||
callback();
|
||||
}
|
||||
|
||||
// see http://docs.gimp.org/en/gimp-concepts-layer-modes.html for other blend modes
|
||||
|
||||
const multiply_mode = function (i, m) {
|
||||
return ~~( (i * m) / 255 );
|
||||
};
|
||||
const divide_mode = function (i, m) {
|
||||
return ~~( (256 * i) / (m + 1) );
|
||||
};
|
||||
|
||||
const overlay_mode = function (i, m) {
|
||||
return ~~( (i / 255) * (i + ((2 * m) / 255) * (255 - i)) );
|
||||
};
|
||||
|
||||
const screen_mode = function (i, m) {
|
||||
return ~~( 255 - ((255 - m) * (255 - i)) / 255 );
|
||||
};
|
||||
|
||||
const sof_light_mode = function (i, m) {
|
||||
var Rs = screen_mode(i, m);
|
||||
return ~~( ((((255 - i) * m) + Rs) * i) / 255 );
|
||||
};
|
||||
|
||||
const color_dodge = function (i, m) {
|
||||
return ~~( (256 * i) / (255 - m + 1) );
|
||||
};
|
||||
|
||||
const burn_mode = function (i, m) {
|
||||
return ~~( 255 - (256 * (255 - i)) / (m + 1));
|
||||
};
|
||||
|
||||
const grain_extract_mode = function (i, m) {
|
||||
return ~~( i - m + 128 );
|
||||
};
|
||||
|
||||
const grain_merge_mode = function (i, m) {
|
||||
return ~~( i + m - 128 );
|
||||
};
|
||||
|
||||
|
||||
getPixels(priorStep.output.src, function(err, pixels) {
|
||||
options.firstImagePixels = pixels;
|
||||
|
||||
// Convert to runnable code.
|
||||
if (typeof options.func === 'string') eval('options.func = ' + options.func);
|
||||
|
||||
function changePixel(r2, g2, b2, a2, x, y) {
|
||||
// blend!
|
||||
let p = options.firstImagePixels;
|
||||
let r1 = p.get(x, y, 0),
|
||||
g1 = p.get(x, y, 1),
|
||||
b1 = p.get(x, y, 2),
|
||||
a1 = p.get(x, y, 3);
|
||||
|
||||
const blends = {
|
||||
'Color Dodge': () => [color_dodge(r2, r1), color_dodge(g2, g1), color_dodge(b2, b1), 255],
|
||||
'Multiply': () => [multiply_mode(r2, r1), multiply_mode(g2, g1), multiply_mode(b2, b1), multiply_mode(a2, a1)],
|
||||
'Divide': () => [divide_mode(r2, r1), divide_mode(g2, g1), divide_mode(b2, b1), 255],
|
||||
'Overlay': () => [overlay_mode(r2, r1), overlay_mode(g2, g1), overlay_mode(b2, b1), 255],
|
||||
'Screen': () => [screen_mode(r2, r1), screen_mode(g2, g1), screen_mode(b2, b1), 255],
|
||||
'Soft Light': () => [sof_light_mode(r2, r1), sof_light_mode(g2, g1), sof_light_mode(b2, b1), 255],
|
||||
'Color Burn': () => [burn_mode(r2, r1), burn_mode(g2, g1), burn_mode(b2, b1), 255],
|
||||
'Grain Extract': () => [grain_extract_mode(r2, r1), grain_extract_mode(g2, g1), grain_extract_mode(b2, b1), 255],
|
||||
'Grain Merge': () => [grain_merge_mode(r2, r1), grain_merge_mode(g2, g1), grain_merge_mode(b2, b1), 255]
|
||||
};
|
||||
|
||||
if(options.blendMode == 'custom')
|
||||
return options.func(
|
||||
r2, g2, b2, a2, r1, g1, b1, a1
|
||||
);
|
||||
else {
|
||||
return blends[options.blendMode]();
|
||||
}
|
||||
|
||||
return options.func(
|
||||
r2, g2, b2, a2,
|
||||
p.get(x, y, 0),
|
||||
p.get(x, y, 1),
|
||||
p.get(x, y, 2),
|
||||
p.get(x, y, 3)
|
||||
);
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
// run PixelManipulatin on second image's pixels
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
{
|
||||
"name": "blend",
|
||||
"description": "Blend two chosen image steps with the given function. Defaults to using the red channel from image 1 and the green and blue and alpha channels of image 2.",
|
||||
"description": "Blend two chosen image steps with the given function. Defaults to using the red channel from image 1 and the green and blue and alpha channels of image 2. Easier to use interfaces coming soon!",
|
||||
"inputs": {
|
||||
"offset": {
|
||||
"type": "integer",
|
||||
"desc": "Choose which image to blend the current image with. Two steps back is -2, three steps back is -3 etc.",
|
||||
"default": -2
|
||||
},
|
||||
"blendMode": {
|
||||
"type": "select",
|
||||
"desc": "Name of the Blend Mode to use",
|
||||
"default": "custom",
|
||||
"values": [
|
||||
"custom",
|
||||
"Multiply",
|
||||
"Divide",
|
||||
"Overlay",
|
||||
"Screen",
|
||||
"Soft Light",
|
||||
"Color Burn",
|
||||
"Color Dodge",
|
||||
"Grain Extract",
|
||||
"Grain Merge"
|
||||
]
|
||||
},
|
||||
"blend": {
|
||||
"type": "string",
|
||||
"desc": "Function to use to blend the two images.",
|
||||
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2, x, y) { return [ r2, g2, b2, a2 ] }"
|
||||
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }"
|
||||
}
|
||||
},
|
||||
"docs-link": "https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blend-module"
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blend-module"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
module.exports = function(pixels){
|
||||
module.exports = function(pixels, options, priorStep){
|
||||
|
||||
var $ = require('jquery'); // to make Blob-analysis work for node.js
|
||||
|
||||
var img = $(priorStep.imgElement);
|
||||
if(Object.keys(img).length === 0){
|
||||
img = $(priorStep.options.step.imgElement);
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = pixels.shape[0];
|
||||
canvas.height = pixels.shape[1];
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.putImageData(new ImageData(new Uint8ClampedArray(pixels.data), pixels.shape[0], pixels.shape[1]), 0, 0);
|
||||
|
||||
ctx.drawImage(img[0], 0, 0);
|
||||
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
|
||||
@@ -18,26 +25,26 @@ module.exports = function(pixels){
|
||||
let unknown = new cv.Mat();
|
||||
let markers = new cv.Mat();
|
||||
|
||||
// Gray and Threshold the image
|
||||
// gray and threshold image
|
||||
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);
|
||||
cv.threshold(gray, gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU);
|
||||
|
||||
// Get background
|
||||
// get background
|
||||
let M = cv.Mat.ones(3, 3, cv.CV_8U);
|
||||
cv.erode(gray, gray, M);
|
||||
cv.dilate(gray, opening, M);
|
||||
cv.dilate(opening, imageBg, M, new cv.Point(-1, -1), 3);
|
||||
|
||||
// Distance transform
|
||||
// distance transform
|
||||
cv.distanceTransform(opening, distTrans, cv.DIST_L2, 5);
|
||||
cv.normalize(distTrans, distTrans, 1, 0, cv.NORM_INF);
|
||||
|
||||
// Get foreground
|
||||
// get foreground
|
||||
cv.threshold(distTrans, imageFg, 0.7 * 1, 255, cv.THRESH_BINARY);
|
||||
imageFg.convertTo(imageFg, cv.CV_8U, 1, 0);
|
||||
cv.subtract(imageBg, imageFg, unknown);
|
||||
|
||||
// Get connected components markers
|
||||
// get connected components markers
|
||||
cv.connectedComponents(imageFg, markers);
|
||||
for (let i = 0; i < markers.rows; i++) {
|
||||
for (let j = 0; j < markers.cols; j++) {
|
||||
@@ -51,13 +58,13 @@ module.exports = function(pixels){
|
||||
cv.cvtColor(src, src, cv.COLOR_RGBA2RGB, 0);
|
||||
cv.watershed(src, markers);
|
||||
|
||||
// Grow barriers
|
||||
// draw barriers
|
||||
for (let i = 0; i < markers.rows; i++) {
|
||||
for (let j = 0; j < markers.cols; j++) {
|
||||
if (markers.intPtr(i, j)[0] == -1) {
|
||||
src.ucharPtr(i, j)[0] = 255; // Red
|
||||
src.ucharPtr(i, j)[1] = 0; // Green
|
||||
src.ucharPtr(i, j)[2] = 0; // Blue
|
||||
src.ucharPtr(i, j)[0] = 255; // R
|
||||
src.ucharPtr(i, j)[1] = 0; // G
|
||||
src.ucharPtr(i, j)[2] = 0; // B
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,4 +78,4 @@ module.exports = function(pixels){
|
||||
pixels.data = myImageData.data;
|
||||
|
||||
return pixels;
|
||||
};
|
||||
};
|
||||
@@ -10,14 +10,17 @@ module.exports = function BlobAnalysis(options, UI){
|
||||
|
||||
var step = this;
|
||||
|
||||
var priorStep = this.getStep(-1); // get the previous step to process it
|
||||
|
||||
function extraManipulation(pixels){
|
||||
|
||||
pixels = require('./BlobAnalysis')(pixels);
|
||||
pixels = require('./BlobAnalysis')(pixels, options, priorStep);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype){
|
||||
|
||||
step.output = { src: datauri, format: mimetype};
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -37,4 +40,4 @@ module.exports = function BlobAnalysis(options, UI){
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -2,6 +2,5 @@
|
||||
"name": "Blob Analysis",
|
||||
"description": "Blob/Region identification for microscopic images.",
|
||||
"inputs": {},
|
||||
"requires": ["webgl", "browser"],
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blob-analysis"
|
||||
}
|
||||
|
||||
@@ -1,36 +1,8 @@
|
||||
// Generates a 5x5 Gaussian kernel
|
||||
function kernelGenerator(sigma = 1) {
|
||||
|
||||
let kernel = [],
|
||||
sum = 0;
|
||||
|
||||
if (sigma == 0) sigma += 0.05;
|
||||
|
||||
const s = 2 * Math.pow(sigma, 2);
|
||||
|
||||
for (let y = -2; y <= 2; y++) {
|
||||
kernel.push([]);
|
||||
for (let x = -2; x <= 2; x++) {
|
||||
let r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||
kernel[y + 2].push(Math.exp(-(r / s)));
|
||||
sum += kernel[y + 2][x + 2];
|
||||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < 5; x++){
|
||||
for (let y = 0; y < 5; y++){
|
||||
kernel[y][x] = (kernel[y][x] / sum);
|
||||
}
|
||||
}
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
module.exports = exports = function(pixels, blur) {
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
let kernel = kernelGenerator(blur), // Generate the Gaussian kernel based on the sigma input.
|
||||
pixs = { // Separates the rgb channel pixels to convolve on the GPU.
|
||||
let kernel = kernelGenerator(blur),
|
||||
pixs = {
|
||||
r: [],
|
||||
g: [],
|
||||
b: [],
|
||||
@@ -48,18 +20,46 @@ module.exports = exports = function(pixels, blur) {
|
||||
}
|
||||
}
|
||||
|
||||
const convolve = require('../_nomodule/gpuUtils').convolve; // GPU convolution function.
|
||||
const convolve = require('../_nomodule/gpuUtils').convolve;
|
||||
|
||||
const conPix = convolve([pixs.r, pixs.g, pixs.b], kernel); // Convolves the pixels (all channels separately) on the GPU.
|
||||
const conPix = convolve([pixs.r, pixs.g, pixs.b], kernel);
|
||||
|
||||
for (let y = 0; y < pixels.shape[1]; y++){
|
||||
for (let x = 0; x < pixels.shape[0]; x++){
|
||||
var pixelvalue = [Math.max(0, Math.min(conPix[0][y][x], 255)),
|
||||
Math.max(0, Math.min(conPix[1][y][x], 255)),
|
||||
Math.max(0, Math.min(conPix[2][y][x], 255))];
|
||||
pixelSetter(x, y, pixelvalue, pixels); // Sets the image pixels according to the blurred values.
|
||||
pixelSetter(x, y, pixelvalue, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
return pixels;
|
||||
};
|
||||
|
||||
//Generates a 5x5 Gaussian kernel
|
||||
function kernelGenerator(sigma = 1) {
|
||||
|
||||
let kernel = [],
|
||||
sum = 0;
|
||||
|
||||
if (sigma == 0) sigma += 0.05;
|
||||
|
||||
const s = 2 * Math.pow(sigma, 2);
|
||||
|
||||
for (let y = -2; y <= 2; y++) {
|
||||
kernel.push([]);
|
||||
for (let x = -2; x <= 2; x++) {
|
||||
let r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||
kernel[y + 2].push(Math.exp(-(r / s)));
|
||||
sum += kernel[y + 2][x + 2];
|
||||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < 5; x++){
|
||||
for (let y = 0; y < 5; y++){
|
||||
kernel[y][x] = (kernel[y][x] / sum);
|
||||
}
|
||||
}
|
||||
|
||||
return kernel;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,14 +20,16 @@ module.exports = function Blur(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
inBrowser: options.inBrowser,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
|
||||
@@ -31,8 +31,11 @@ module.exports = function Brightness(options, UI) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -35,8 +35,11 @@ module.exports = function canvasResize(options, UI) {
|
||||
return newPixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -22,6 +22,5 @@
|
||||
"desc": "Y-cord of the top left corner of the image on the canvas",
|
||||
"default": 500
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#canvas-resize-module"
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,11 @@ module.exports = function Channel(options, UI) {
|
||||
if (options.channel === 'blue') return [0, 0, b, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Generate halftone versions of CMYK channels and blend them with varying rotations as in analog print color separation processes.
|
||||
* Simulates a CMYK halftone rendering of the image by multiplying pixel values with a four rotated 2D sine wave patterns, one each for cyan, magenta, yellow, and black.
|
||||
* http://evanw.github.io/glfx.js/docs/#colorHalftone
|
||||
*/
|
||||
module.exports = function ColorHalftone(options, UI) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
var output;
|
||||
var fx = require('glfx');
|
||||
|
||||
var dataURItoBlob = function dataURItoBlob(dataURI) {
|
||||
// convert base64/URLEncoded data component to raw binary data held in a string
|
||||
var byteString;
|
||||
if (dataURI.split(',')[0].indexOf('base64') >= 0)
|
||||
byteString = atob(dataURI.split(',')[1]);
|
||||
else
|
||||
byteString = unescape(dataURI.split(',')[1]);
|
||||
|
||||
// separate out the mime component
|
||||
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
||||
|
||||
// write the bytes of the string to a typed array
|
||||
var ia = new Uint8Array(byteString.length);
|
||||
for (var i = 0; i < byteString.length; i++) {
|
||||
ia[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new Blob([ia], { type: mimeString });
|
||||
};
|
||||
|
||||
var canvasToBlobUrl = function canvasToBlobUrl(canvas) {
|
||||
|
||||
var blob = dataURItoBlob(canvas.toDataURL('image/png'));
|
||||
return window.URL.createObjectURL(blob);
|
||||
|
||||
};
|
||||
|
||||
var colorHalftone = function colorHalftone(id, options, download) {
|
||||
|
||||
// try to create a WebGL canvas (will fail if WebGL isn't supported)
|
||||
try {
|
||||
var canvas = fx.canvas(1500, 1500);
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// convert the image to a texture
|
||||
var imageEl = document.getElementById(id);
|
||||
|
||||
var image = new Image();
|
||||
|
||||
image.onload = function() {
|
||||
|
||||
var texture = canvas.texture(image);
|
||||
|
||||
canvas.draw(texture,
|
||||
image.width, // * ratio,
|
||||
image.height// * ratio
|
||||
);
|
||||
|
||||
canvas.colorHalftone(
|
||||
image.width / 2,
|
||||
image.height / 2,
|
||||
parseFloat(options.angle),
|
||||
parseFloat(options.size)
|
||||
).update();
|
||||
|
||||
var burl = canvasToBlobUrl(canvas);
|
||||
|
||||
if (download) {
|
||||
|
||||
window.open(burl);
|
||||
|
||||
} else { // replace the image
|
||||
|
||||
// keep non-blob version in case we have to fall back:
|
||||
// image.src = canvas.toDataURL('image/png');
|
||||
// window.location = canvas.toDataURL('image/png');
|
||||
imageEl.src = burl;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$(image).hide();
|
||||
image.src = imageEl.src;
|
||||
|
||||
};
|
||||
|
||||
function draw(input, callback) {
|
||||
|
||||
var step = this;
|
||||
|
||||
options.angle = options.angle || defaults.angle;
|
||||
options.size = options.size || defaults.size;
|
||||
|
||||
if (!options.inBrowser) {
|
||||
// this.output = input;
|
||||
// callback();
|
||||
require('../_nomodule/gl-context')(input, callback, step, options);
|
||||
}
|
||||
else {
|
||||
var image = document.createElement('img');
|
||||
image.onload = () => {
|
||||
colorHalftone(
|
||||
'img',
|
||||
options
|
||||
);
|
||||
image.onload = () => {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = image.naturalWidth; // or 'width' if you want a special/scaled size
|
||||
canvas.height = image.naturalHeight; // or 'height' if you want a special/scaled size
|
||||
canvas.getContext('2d').drawImage(image, 0, 0);
|
||||
|
||||
step.output = { src: canvas.toDataURL('image/png'), format: 'png' };
|
||||
image.remove();
|
||||
callback();
|
||||
};
|
||||
};
|
||||
image.src = input.src;
|
||||
image.id = 'img';
|
||||
document.body.appendChild(image);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "color-halftone",
|
||||
"requires": ["webgl"],
|
||||
"description": "Generate halftone versions of CMYK channels and blend them with varying rotations as in analog print color separation processes.",
|
||||
"inputs": {
|
||||
"angle": {
|
||||
"type": "float",
|
||||
"desc": "angle of rotation of the halftone patterns in radians",
|
||||
"default": "0.25",
|
||||
"min": "0",
|
||||
"max": "1.57"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"desc": "the diameter of a dot in pixels",
|
||||
"default": "4",
|
||||
"min": "3",
|
||||
"max": "20"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#color-halftone-module"
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
module.exports = function ColorTemperature(options, UI) {
|
||||
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
options.temperature = options.temperature || defaults.temperature;
|
||||
options.temperature = (options.temperature > 40000) ? 40000 : options.temperature;
|
||||
options.temperature = (options.temperature < 0) ? 0 : options.temperature;
|
||||
options.temperature = (options.temperature > '40000') ? '40000' : options.temperature;
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
@@ -55,8 +52,10 @@ module.exports = function ColorTemperature(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module.exports = require('../../util/createMetaModule.js')(
|
||||
function mapFunction(options) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
// return steps with options:
|
||||
return [
|
||||
{ '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': 'colormap', 'options': { colormap: options.colormap } },
|
||||
{ 'name': 'crop', 'options': { 'y': 0, 'h': options.h } },
|
||||
{ 'name': 'overlay', 'options': { 'x': options.x, 'y': options.y, 'offset': -4 } }
|
||||
];
|
||||
}, {
|
||||
infoJson: require('./info.json')
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
*/
|
||||
|
||||
module.exports = function Colormap(value, options) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.colormap = options.colormap || defaults.colormap;
|
||||
options.colormap = options.colormap || colormaps.default;
|
||||
// if a lookup table is provided as an array:
|
||||
if(typeof(options.colormap) == 'object')
|
||||
colormapFunction = colormap(options.colormap);
|
||||
|
||||
@@ -16,10 +16,12 @@ module.exports = function Colormap(options, UI) {
|
||||
return [res[0], res[1], res[2], 255];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Crops an Image on the basis of the ratio provided
|
||||
*/
|
||||
module.exports = function ConstrainedCrop(options, UI) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
var output;
|
||||
|
||||
function draw(input, callback) {
|
||||
|
||||
var step = this,
|
||||
startingX = Number(options.startingX || defaults.startingX),
|
||||
startingY = Number(options.startingY || defaults.startingY),
|
||||
aspectRatio = (options.aspectRatio || defaults.aspectRatio).split(':'),
|
||||
widthRatio = Number(aspectRatio[0]),
|
||||
heightRatio = Number(aspectRatio[1]);
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
var width = pixels.shape[0],
|
||||
height = pixels.shape[1];
|
||||
var endX, endY;
|
||||
if(((width - startingX) / widthRatio) * heightRatio <= (height - startingY)) {
|
||||
endX = width;
|
||||
endY = (((width - startingX) / widthRatio) * heightRatio) + startingY;
|
||||
}
|
||||
else {
|
||||
endX = (((height - startingY) / heightRatio) * widthRatio) + startingX;
|
||||
endY = height;
|
||||
}
|
||||
const newPixels = require('../Crop/Crop')(pixels, {'x': startingX, 'y': startingY, 'w': endX - startingX, 'h': endY - startingY}, function() {
|
||||
});
|
||||
return newPixels;
|
||||
}
|
||||
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
return require('../_nomodule/PixelManipulation')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"name": "constrained-crop",
|
||||
"description": "Crops an image in a particular aspect-ratio",
|
||||
"inputs": {
|
||||
"startingX": {
|
||||
"type": "integer",
|
||||
"desc": "X-position (measured from left) from where cropping starts",
|
||||
"default": 0
|
||||
},
|
||||
"startingY": {
|
||||
"type": "integer",
|
||||
"desc": "Y-position (measured from top) from where cropping starts",
|
||||
"default": 0
|
||||
},
|
||||
"aspectRatio":{
|
||||
"type": "string",
|
||||
"desc": "Enter aspect ratio in following format width:height",
|
||||
"default": "1:1"
|
||||
}
|
||||
},
|
||||
"docs-link":""
|
||||
}
|
||||
|
||||
30
src/modules/Contrast/Contrast.js
Normal file
30
src/modules/Contrast/Contrast.js
Normal file
@@ -0,0 +1,30 @@
|
||||
var _ = require('lodash');
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
module.exports = exports = function(pixels, contrast) {
|
||||
let oldpix = _.cloneDeep(pixels);
|
||||
contrast = Number(contrast);
|
||||
if (contrast < -100) contrast = -100;
|
||||
if (contrast > 100) contrast = 100;
|
||||
contrast = (100.0 + contrast) / 100.0;
|
||||
contrast *= contrast;
|
||||
|
||||
for (let i = 0; i < pixels.shape[0]; i++) {
|
||||
for (let j = 0; j < pixels.shape[1]; j++) {
|
||||
|
||||
var rgbarray = [oldpix.get(i, j, 0) / 255.0, oldpix.get(i, j, 1) / 255.0, oldpix.get(i, j, 2) / 255.0];
|
||||
for(var idx = 0;idx < 3;idx++){
|
||||
rgbarray[idx] -= 0.5;
|
||||
rgbarray[idx] *= contrast;
|
||||
rgbarray[idx] += 0.5;
|
||||
rgbarray[idx] *= 255;
|
||||
if (rgbarray[idx] < 0) rgbarray[idx] = 0;
|
||||
if (rgbarray[idx] > 255) rgbarray[idx] = 255;
|
||||
}
|
||||
|
||||
pixelSetter(i, j, rgbarray, pixels);
|
||||
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
};
|
||||
@@ -15,41 +15,25 @@ module.exports = function Contrast(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
let contrast = options.contrast;
|
||||
|
||||
contrast = Number(contrast);
|
||||
if (contrast < -100) contrast = -100;
|
||||
if (contrast > 100) contrast = 100;
|
||||
contrast = (100.0 + contrast) / 100.0;
|
||||
contrast *= contrast;
|
||||
|
||||
function changeContrast(p){
|
||||
p -= 0.5;
|
||||
p *= contrast;
|
||||
p += 0.5;
|
||||
p *= 255;
|
||||
p = Math.max(0, p);
|
||||
p = Math.min(p, 255);
|
||||
return p;
|
||||
function extraManipulation(pixels) {
|
||||
pixels = require('./Contrast')(pixels, options.contrast);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
|
||||
return [changeContrast(r / 255), changeContrast(g / 255), changeContrast(b / 255), a];
|
||||
}
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
callback: callback,
|
||||
inBrowser: options.inBrowser,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@ module.exports = function Convolution(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -29,7 +31,6 @@ module.exports = function Convolution(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -1,71 +1,61 @@
|
||||
const ndarray = require('ndarray'),
|
||||
pixelSetter = require('../../util/pixelSetter'),
|
||||
parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
module.exports = function Crop(pixels, options, cb) {
|
||||
module.exports = function Crop(input, options, callback) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.x = options.x || defaults.x;
|
||||
options.y = options.y || defaults.y;
|
||||
var getPixels = require('get-pixels'),
|
||||
savePixels = require('save-pixels');
|
||||
|
||||
options.w = options.w || defaults.w;
|
||||
options.h = options.h || defaults.h;
|
||||
options.x = parseInt(options.x) || defaults.x;
|
||||
options.y = parseInt(options.y) || defaults.y;
|
||||
|
||||
options.backgroundColor = options.backgroundColor || defaults.backgroundColor;
|
||||
getPixels(input.src, function(err, pixels){
|
||||
options.w = parseInt(options.w) || Math.floor(pixels.shape[0]);
|
||||
options.h = parseInt(options.h) || Math.floor(pixels.shape[1]);
|
||||
options.backgroundColor = options.backgroundColor || defaults.backgroundColor;
|
||||
var ox = options.x;
|
||||
var oy = options.y;
|
||||
var w = options.w;
|
||||
var h = options.h;
|
||||
var iw = pixels.shape[0]; //Width of Original Image
|
||||
var ih = pixels.shape[1]; //Height of Original Image
|
||||
var backgroundArray = [];
|
||||
backgroundColor = options.backgroundColor.substring(options.backgroundColor.indexOf('(') + 1, options.backgroundColor.length - 1); // extract only the values from rgba(_,_,_,_)
|
||||
backgroundColor = backgroundColor.split(',');
|
||||
for(var i = 0; i < w ; i++){
|
||||
backgroundArray = backgroundArray.concat([backgroundColor[0], backgroundColor[1], backgroundColor[2], backgroundColor[3]]);
|
||||
}
|
||||
// var newarray = new Uint8Array(4*w*h);
|
||||
var array = [];
|
||||
for (var n = oy; n < oy + h; n++) {
|
||||
var offsetValue = 4 * w * n;
|
||||
if(n < ih){
|
||||
var start = n * 4 * iw + ox * 4;
|
||||
var end = n * 4 * iw + ox * 4 + 4 * w;
|
||||
var pushArray = Array.from(pixels.data.slice(start, end ));
|
||||
array.push.apply(array, pushArray);
|
||||
} else {
|
||||
array.push.apply(array, backgroundArray);
|
||||
}
|
||||
}
|
||||
|
||||
var newarray = Uint8Array.from(array);
|
||||
pixels.data = newarray;
|
||||
pixels.shape = [w, h, 4];
|
||||
pixels.stride[1] = 4 * w;
|
||||
|
||||
const bg = options.backgroundColor.replace('rgba', '').replace('(', '').replace(')', '').split(',');
|
||||
options.format = input.format;
|
||||
|
||||
let iw = pixels.shape[0], // Width of Original Image
|
||||
ih = pixels.shape[1], // Height of Original Image
|
||||
offsetX,
|
||||
offsetY,
|
||||
w,
|
||||
h;
|
||||
var chunks = [];
|
||||
var totalLength = 0;
|
||||
var r = savePixels(pixels, options.format);
|
||||
|
||||
// Parse the inputs
|
||||
parseCornerCoordinateInputs({iw, ih},
|
||||
{
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
w: { valInp: options.w, type: 'horizontal' },
|
||||
h: { valInp: options.h, type: 'vertical' },
|
||||
}, function (opt, coord) {
|
||||
offsetX = Math.floor(coord.x.valInp);
|
||||
offsetY = Math.floor(coord.y.valInp);
|
||||
w = Math.floor(coord.w.valInp);
|
||||
h = Math.floor(coord.h.valInp);
|
||||
r.on('data', function(chunk){
|
||||
totalLength += chunk.length;
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
const newPixels = new ndarray([], [w, h, 4]);
|
||||
|
||||
for (let x = 0; x < w; x++) {
|
||||
for (let y = 0; y < h; y++) {
|
||||
pixelSetter(x, y, bg, newPixels); // Set the background color
|
||||
}
|
||||
}
|
||||
|
||||
for (
|
||||
let x = 0;
|
||||
x < Math.min(w - 1, offsetX + iw - 1);
|
||||
x++
|
||||
) {
|
||||
for (
|
||||
let y = 0;
|
||||
y < Math.min(h - 1, offsetY + ih - 1);
|
||||
y++
|
||||
) {
|
||||
const inputImgX = x + offsetX,
|
||||
inputImgY = y + offsetY;
|
||||
|
||||
pixelSetter(x, y, [
|
||||
pixels.get(inputImgX, inputImgY, 0),
|
||||
pixels.get(inputImgX, inputImgY, 1),
|
||||
pixels.get(inputImgX, inputImgY, 2),
|
||||
pixels.get(inputImgX, inputImgY, 3)
|
||||
], newPixels); // Set the background color
|
||||
}
|
||||
}
|
||||
|
||||
if (cb) cb();
|
||||
|
||||
return newPixels;
|
||||
r.on('end', function(){
|
||||
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
||||
var datauri = 'data:image/' + options.format + ';base64,' + data;
|
||||
callback(datauri, options.format);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||
/*
|
||||
* Image Cropping module
|
||||
* Usage:
|
||||
@@ -27,37 +26,54 @@ module.exports = function CropModule(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
// save the input image;
|
||||
// TODO: this should be moved to module API to persist the input image
|
||||
options.step.input = input.src;
|
||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
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) {
|
||||
setupComplete = true;
|
||||
ui.setup();
|
||||
}
|
||||
});
|
||||
return newPixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return pixelManipulation(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
//parse the inputs
|
||||
parseCornerCoordinateInputs(options, {
|
||||
src: input.src,
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
w: { valInp: options.w, type: 'horizontal' },
|
||||
h: { valInp: options.h, type: 'vertical' },
|
||||
}, function (options, coord) {
|
||||
options.x = parseInt(coord.x.valInp);
|
||||
options.y = parseInt(coord.y.valInp);
|
||||
options.w = coord.w.valInp;
|
||||
options.h = coord.h.valInp;
|
||||
});
|
||||
|
||||
require('./Crop')(input, options, function (out, format) {
|
||||
|
||||
// This output is accessible to Image Sequencer
|
||||
step.output = {
|
||||
src: out,
|
||||
format: format
|
||||
};
|
||||
|
||||
// This output is accessible to the UI
|
||||
options.step.output = out;
|
||||
|
||||
// Tell the UI that the step has been drawn
|
||||
UI.onComplete(options.step);
|
||||
|
||||
// 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) {
|
||||
setupComplete = true;
|
||||
ui.setup();
|
||||
}
|
||||
|
||||
// Tell Image Sequencer that step has been drawn
|
||||
callback();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -59,6 +59,12 @@ module.exports = function CropModuleUi(step, ui) {
|
||||
];
|
||||
}
|
||||
|
||||
function remove() {
|
||||
$(imgEl()).imgAreaSelect({
|
||||
remove: true
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
// then hide the draggable UI
|
||||
$(imgEl()).imgAreaSelect({
|
||||
@@ -86,6 +92,7 @@ module.exports = function CropModuleUi(step, ui) {
|
||||
|
||||
return {
|
||||
setup: setup,
|
||||
remove: remove,
|
||||
hide: hide
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
"w": {
|
||||
"type": "string",
|
||||
"desc": "Width of crop",
|
||||
"default": "100%"
|
||||
"default": "(50%)"
|
||||
},
|
||||
"h": {
|
||||
"type": "string",
|
||||
"desc": "Height of crop",
|
||||
"default": "100%"
|
||||
"default": "(50%)"
|
||||
},
|
||||
"backgroundColor": {
|
||||
"type": "text",
|
||||
|
||||
@@ -28,16 +28,18 @@ module.exports = function DoNothing(options, UI) {
|
||||
options.step.qrval = (decoded) ? decoded.data : 'undefined';
|
||||
});
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = {
|
||||
src: datauri,
|
||||
format: mimetype
|
||||
};
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -15,8 +15,10 @@ module.exports = function Dither(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -25,7 +27,6 @@ module.exports = function Dither(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -4,24 +4,22 @@ module.exports = exports = function(pixels, options){
|
||||
|
||||
options.startingX = options.startingX || defaults.startingX;
|
||||
options.startingY = options.startingY || defaults.startingY;
|
||||
|
||||
var ox = Number(options.startingX),
|
||||
oy = Number(options.startingY),
|
||||
iw = pixels.shape[0],
|
||||
ih = pixels.shape[1],
|
||||
thickness = Number(options.thickness) || defaults.thickness,
|
||||
ex = Number(options.endX || defaults.endX) - thickness || iw - 1,
|
||||
ey = Number(options.endY || defaults.endY) - thickness || ih - 1,
|
||||
ex = options.endX = Number(options.endX) - thickness || iw - 1,
|
||||
ey = options.endY = Number(options.endY) - thickness || ih - 1,
|
||||
color = options.color || defaults.color;
|
||||
|
||||
color = color.substring(color.indexOf('(') + 1, color.length - 1); // Extract only the values from rgba(_,_,_,_)
|
||||
color = color.substring(color.indexOf('(') + 1, color.length - 1); // extract only the values from rgba(_,_,_,_)
|
||||
color = color.split(',');
|
||||
|
||||
var drawSide = function(startX, startY, endX, endY){
|
||||
for (var n = startX; n <= endX + thickness; n++){
|
||||
for (var k = startY; k <= endY + thickness; k++){
|
||||
|
||||
pixelSetter(n, k, [color[0], color[1], color[2]], pixels); // To remove 4th channel - pixels.set(n, k, 3, color[3]);
|
||||
pixelSetter(n, k, [color[0], color[1], color[2]], pixels); //to remove 4th channel - pixels.set(n, k, 3, color[3]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,8 +19,10 @@ module.exports = function DrawRectangle(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -30,7 +32,6 @@ module.exports = function DrawRectangle(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
"endX":{
|
||||
"type": "integer",
|
||||
"desc": "last x position of the rectangle",
|
||||
"default": 10
|
||||
"default": "width"
|
||||
},
|
||||
|
||||
"endY":{
|
||||
"type": "integer",
|
||||
"desc": "last y position of the rectangle",
|
||||
"default": 10
|
||||
"default": "height"
|
||||
},
|
||||
|
||||
"thickness":{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = function Dynamic(options, UI) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
var output;
|
||||
|
||||
// This function is called on every draw.
|
||||
@@ -10,12 +10,13 @@ module.exports = function Dynamic(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
options.red = options.red || defaults.red;
|
||||
options.blue = options.blue || defaults.blue;
|
||||
options.green = options.green || defaults.green;
|
||||
// start with monochrome, but if options.red, options.green, and options.blue are set, accept them too
|
||||
options.monochrome = options.monochrome || '(R+G+B)/3';
|
||||
|
||||
function generator(expression) {
|
||||
var func = 'f = function (r, g, b, a) { var R = r, G = g, B = b, A = a; return ' + expression + ';}';
|
||||
var func = 'f = function (r, g, b, a) { var R = r, G = g, B = b, A = a;';
|
||||
func = func + 'return ';
|
||||
func = func + expression + '}';
|
||||
var f;
|
||||
eval(func);
|
||||
return f;
|
||||
@@ -24,12 +25,9 @@ module.exports = function Dynamic(options, UI) {
|
||||
var channels = ['red', 'green', 'blue', 'alpha'];
|
||||
|
||||
channels.forEach(function(channel) {
|
||||
if (channel === 'alpha'){
|
||||
options['alpha_function'] = function() { return 255; };
|
||||
}
|
||||
else{
|
||||
options[channel + '_function'] = generator(options[channel]);
|
||||
}
|
||||
if (options.hasOwnProperty(channel)) options[channel + '_function'] = generator(options[channel]);
|
||||
else if (channel === 'alpha') options['alpha_function'] = function() { return 255; };
|
||||
else options[channel + '_function'] = generator(options.monochrome);
|
||||
});
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
@@ -73,10 +71,12 @@ module.exports = function Dynamic(options, UI) {
|
||||
}
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
"type": "string",
|
||||
"desc": "Expression to return for blue channel with R, G, B, and A inputs",
|
||||
"default": "b"
|
||||
},
|
||||
"monochrome (fallback)": {
|
||||
"type": "string",
|
||||
"desc": "Expression to return with R, G, B, and A inputs; fallback for other channels if none provided",
|
||||
"default": "r + g + b"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#dynamic-module"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Read More: https://en.wikipedia.org/wiki/Canny_edge_detector
|
||||
// Define kernels for the sobel filter
|
||||
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
// Define kernels for the sobel filter.
|
||||
const kernelx = [
|
||||
[-1, 0, 1],
|
||||
[-2, 0, 2],
|
||||
@@ -23,7 +21,7 @@ module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, useHyst
|
||||
grads.push([]);
|
||||
angles.push([]);
|
||||
for (var y = 0; y < pixels.shape[1]; y++) {
|
||||
var result = sobelFilter( // Convolves the sobel filter on every pixel
|
||||
var result = sobelFilter(
|
||||
pixels,
|
||||
x,
|
||||
y
|
||||
@@ -34,47 +32,28 @@ 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.
|
||||
if(useHysteresis.toLowerCase() == 'true') hysteresis(strongEdgePixels, weakEdgePixels); // Optional Hysteresis (very slow) to minimize edges generated due to noise.
|
||||
nonMaxSupress(pixels, grads, angles);
|
||||
doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels);
|
||||
if(useHysteresis.toLowerCase() == 'true') hysteresis(strongEdgePixels, weakEdgePixels);
|
||||
|
||||
strongEdgePixels.forEach(pixel => preserve(pixels, pixel)); // Makes the strong edges White.
|
||||
weakEdgePixels.forEach(pixel => supress(pixels, pixel)); // Makes the weak edges black(bg color) after filtering.
|
||||
pixelsToBeSupressed.forEach(pixel => supress(pixels, pixel)); // Makes the rest of the image black.
|
||||
strongEdgePixels.forEach(pixel => preserve(pixels, pixel));
|
||||
weakEdgePixels.forEach(pixel => supress(pixels, pixel));
|
||||
pixelsToBeSupressed.forEach(pixel => supress(pixels, pixel));
|
||||
|
||||
return pixels;
|
||||
};
|
||||
|
||||
/**
|
||||
* @method supress
|
||||
* @description Supresses (fills with background color) the specified (non-edge)pixel.
|
||||
* @param {Object} pixels ndarry of pixels
|
||||
* @param {Float32Array} pixel Pixel coordinates
|
||||
* @returns {Null}
|
||||
*/
|
||||
function supress(pixels, pixel) {
|
||||
pixelSetter(pixel[0], pixel[1], [0, 0, 0, 255], pixels);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @method preserve
|
||||
* @description Preserve the specified pixel(of an edge).
|
||||
* @param {Object} pixels ndarray of pixels
|
||||
* @param {*} pixel Pixel coordinates
|
||||
* @returns {Null}
|
||||
*/
|
||||
function preserve(pixels, pixel) {
|
||||
pixelSetter(pixel[0], pixel[1], [255, 255, 255, 255], pixels);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @method sobelFiler
|
||||
* @description Runs the sobel filter on the specified and neighbouring pixels.
|
||||
* @param {Object} pixels ndarray of pixels
|
||||
* @param {Number} x x-coordinate of the pixel
|
||||
* @param {Number} y y-coordinate of the pixel
|
||||
* @returns {Object} Object containing the gradient and angle.
|
||||
*/
|
||||
// sobelFilter function that convolves sobel kernel over every pixel
|
||||
function sobelFilter(pixels, x, y) {
|
||||
let val = pixels.get(x, y, 0),
|
||||
gradX = 0.0,
|
||||
@@ -86,8 +65,8 @@ function sobelFilter(pixels, x, y) {
|
||||
let xn = x + a - 1,
|
||||
yn = y + b - 1;
|
||||
|
||||
if (isOutOfBounds(pixels, xn, yn)) { // Fallback for coordinates which lie outside the image.
|
||||
gradX += pixels.get(xn + 1, yn + 1, 0) * kernelx[a][b]; // Fallback to nearest pixel
|
||||
if (isOutOfBounds(pixels, xn, yn)) {
|
||||
gradX += pixels.get(xn + 1, yn + 1, 0) * kernelx[a][b];
|
||||
gradY += pixels.get(xn + 1, yn + 1, 0) * kernely[a][b];
|
||||
}
|
||||
else {
|
||||
@@ -105,12 +84,6 @@ function sobelFilter(pixels, x, y) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @method categorizeAngle
|
||||
* @description Categorizes the given angle into 4 catagories according to the Category Map given below.
|
||||
* @param {Number} angle Angle in degrees
|
||||
* @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;
|
||||
@@ -125,25 +98,17 @@ function categorizeAngle(angle){
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @method isOutOfBounds
|
||||
* @description Checks whether the given coordinates lie outside the bounds of the image. Used for error handling in convolution.
|
||||
* @param {Object} pixels ndarray of pixels
|
||||
* @param {*} x x-coordinate of the pixel
|
||||
* @param {*} y y-coordinate of the pixel
|
||||
* @returns {Boolean} True if the given coordinates are out of bounds.
|
||||
*/
|
||||
function isOutOfBounds(pixels, x, y){
|
||||
return ((x < 0) || (y < 0) || (x >= pixels.shape[0]) || (y >= pixels.shape[1]));
|
||||
}
|
||||
|
||||
const removeElem = (arr = [], elem) => { // Removes the specified element from the given array.
|
||||
const removeElem = (arr = [], elem) => {
|
||||
return arr = arr.filter((arrelem) => {
|
||||
return arrelem !== elem;
|
||||
});
|
||||
};
|
||||
|
||||
// Non Maximum Supression without interpolation.
|
||||
// Non Maximum Supression without interpolation
|
||||
function nonMaxSupress(pixels, grads, angles) {
|
||||
angles = angles.map((arr) => arr.map(convertToDegrees));
|
||||
|
||||
@@ -153,7 +118,7 @@ function nonMaxSupress(pixels, grads, angles) {
|
||||
let angleCategory = categorizeAngle(angles[x][y]);
|
||||
|
||||
if (!isOutOfBounds(pixels, x - 1, y - 1) && !isOutOfBounds(pixels, x + 1, y + 1)){
|
||||
switch (angleCategory){ // Non maximum suppression according to angle category
|
||||
switch (angleCategory){
|
||||
case 1:
|
||||
if (!((grads[x][y] >= grads[x][y + 1]) && (grads[x][y] >= grads[x][y - 1]))) {
|
||||
pixelsToBeSupressed.push([x, y]);
|
||||
@@ -182,24 +147,17 @@ 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
|
||||
*/
|
||||
// Converts radians to degrees
|
||||
var convertToDegrees = radians => (radians * 180) / Math.PI;
|
||||
|
||||
// Finds the max value in a 2d array like grads.
|
||||
// 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.
|
||||
// Applies the double threshold to the image
|
||||
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels) {
|
||||
|
||||
const highThreshold = findMaxInMatrix(grads) * highThresholdRatio, // High Threshold relative to the strongest edge
|
||||
lowThreshold = highThreshold * lowThresholdRatio; // Low threshold relative to high threshold
|
||||
const highThreshold = findMaxInMatrix(grads) * highThresholdRatio,
|
||||
lowThreshold = highThreshold * lowThresholdRatio;
|
||||
|
||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||
@@ -220,12 +178,6 @@ function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, s
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method hysteresis
|
||||
* @description Filters weak edge pixels that are not connected to a strong edge pixel.
|
||||
* @param {Float32array} strongEdgePixels 2D array of strong edge pixel coordinates
|
||||
* @param {*} weakEdgePixels 2D array of weak edge pixel coordinated
|
||||
*/
|
||||
function hysteresis(strongEdgePixels, weakEdgePixels){
|
||||
strongEdgePixels.forEach(pixel => {
|
||||
let x = pixel[0],
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
const Blur = require('../Blur/Blur');
|
||||
/*
|
||||
* Detect Edges in an Image
|
||||
* Uses Canny method for the same
|
||||
* Read more: https://en.wikipedia.org/wiki/Canny_edge_detector
|
||||
*/
|
||||
module.exports = function edgeDetect(options, UI) {
|
||||
|
||||
@@ -22,31 +19,42 @@ module.exports = function edgeDetect(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
// Makes the image greyscale
|
||||
function changePixel(r, g, b, a) {
|
||||
return [(r + g + b) / 3, (r + g + b) / 3, (r + g + b) / 3, a];
|
||||
}
|
||||
// Blur the image
|
||||
const internalSequencer = ImageSequencer({ inBrowser: false, ui: false });
|
||||
return internalSequencer.loadImage(input.src, function() {
|
||||
internalSequencer.importJSON([{ 'name': 'blur', 'options': { blur: options.blur } }]);
|
||||
return internalSequencer.run(function onCallback(internalOutput) {
|
||||
require('get-pixels')(internalOutput, function(err, blurPixels) {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
|
||||
function extraManipulation(pixels) {
|
||||
const blurPixels = Blur(pixels, options.blur);
|
||||
return require('./EdgeUtils')(blurPixels, options.highThresholdRatio, options.lowThresholdRatio, options.hysteresis);
|
||||
}
|
||||
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
|
||||
function changePixel(r, g, b, a) {
|
||||
return [(r + g + b) / 3, (r + g + b) / 3, (r + g + b) / 3, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
function extraManipulation() {
|
||||
return require('./EdgeUtils')(blurPixels, options.highThresholdRatio, options.lowThresholdRatio, options.hysteresis);
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm: options.useWasm
|
||||
function output(image, datauri, mimetype) {
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm: options.useWasm
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,4 +64,4 @@ module.exports = function edgeDetect(options, UI) {
|
||||
output: output,
|
||||
UI: UI
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "edge-detect",
|
||||
"description": "Edge Detect module detects edges using the Canny method, which first blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge. [Read more](https://en.wikipedia.org/wiki/Canny_edge_detector)",
|
||||
"description": "This module detects edges using the Canny method, which first Gaussian blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge.<a href='https://en.wikipedia.org/wiki/Canny_edge_detector'> Read more. </a>",
|
||||
"inputs": {
|
||||
"blur": {
|
||||
"type": "float",
|
||||
|
||||
@@ -24,8 +24,11 @@ module.exports = function Exposure(options, UI) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
"step": 0.05
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#exposure-module"
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
const _ = require('lodash');
|
||||
/*
|
||||
* Flip the image on vertical/horizontal axis.
|
||||
*/
|
||||
module.exports = function FlipImage(options, UI) {
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.Axis = options.Axis || defaults.Axis;
|
||||
options.Axis = options.Axis || require('./info.json').inputs.Axis.default;
|
||||
|
||||
let output;
|
||||
var output,
|
||||
getPixels = require('get-pixels');
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
@@ -15,30 +14,34 @@ module.exports = function FlipImage(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
function extraManipulation(pixels) {
|
||||
const oldPixels = _.cloneDeep(pixels);
|
||||
return getPixels(input.src, function(err, oldPixels) {
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
function extraManipulation(pixels) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
return require('./flipImage')(oldPixels, pixels, options.Axis);
|
||||
}
|
||||
function output(image, datauri, mimetype) {
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('./flipImage')(oldPixels, pixels, options.Axis);
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -21,8 +21,10 @@ module.exports = function Gamma(options, UI) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -1,57 +1,55 @@
|
||||
const pixelSetter = require('../../util/pixelSetter.js'),
|
||||
pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||
|
||||
module.exports = function Gradient(options, UI) {
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.gradientType = options.gradientType || defaults.gradientType;
|
||||
module.exports = function Invert(options, UI) {
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
var output;
|
||||
|
||||
// The function which is called on every draw.
|
||||
function draw(input, callback) {
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
var savePixels = require('save-pixels');
|
||||
|
||||
var step = this;
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
getPixels(input.src, function(err, pixels) {
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
const [w, h] = pixels.shape;
|
||||
if (options.gradientType === 'linear') {
|
||||
for (var i = 0; i < w; i++) {
|
||||
for (var j = 0; j < h; j++) {
|
||||
let val = (i / w) * 255;
|
||||
|
||||
pixelSetter(i, j, [val, val, val, 255], pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < w; i++) {
|
||||
for (var j = 0; j < h; j++) {
|
||||
var distX = Math.abs(w / 2 - i);
|
||||
var distY = Math.abs(h / 2 - j);
|
||||
var distance = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2));
|
||||
val = 255 * (distance / pixels.shape[0]);
|
||||
pixelSetter(i, j, [val, val, val, 255], pixels);
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
console.log('Bad Image path');
|
||||
return;
|
||||
}
|
||||
|
||||
return pixels;
|
||||
}
|
||||
var width = pixels.shape[0];
|
||||
|
||||
return pixelManipulation(input, {
|
||||
output,
|
||||
extraManipulation,
|
||||
callback,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
useWasm:options.useWasm
|
||||
for (var i = 0; i < pixels.shape[0]; i++) {
|
||||
for (var j = 0; j < pixels.shape[1]; j++) {
|
||||
let val = (i / width) * 255;
|
||||
pixelSetter(i, j, [val, val, val, 255], pixels);
|
||||
|
||||
}
|
||||
}
|
||||
var chunks = [];
|
||||
var totalLength = 0;
|
||||
var r = savePixels(pixels, input.format, { quality: 100 });
|
||||
|
||||
r.on('data', function(chunk) {
|
||||
totalLength += chunk.length;
|
||||
chunks.push(chunk);
|
||||
});
|
||||
|
||||
r.on('end', function() {
|
||||
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
||||
var datauri = 'data:image/' + input.format + ';base64,' + data;
|
||||
output(input.image, datauri, input.format);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
{
|
||||
"name": "gradient",
|
||||
"description": "Gives a gradient of the image",
|
||||
"inputs": {
|
||||
"gradientType": {
|
||||
"type": "select",
|
||||
"desc": "Choose between linear or circular gradient",
|
||||
"default": "linear",
|
||||
"values": ["linear", "circular"]
|
||||
}
|
||||
},
|
||||
"inputs": {},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#gradient-module"
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ module.exports = function GridOverlay(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = function Channel(options, UI) {
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
|
||||
options.gradient = options.gradient || defaults.gradient;
|
||||
options.gradient = String(JSON.parse(options.gradient));
|
||||
options.gradient = JSON.parse(options.gradient);
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
@@ -63,8 +63,11 @@ module.exports = function Channel(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = function ImportImageModuleUi(step, ui) {
|
||||
|
||||
// add a file input listener
|
||||
var dropZone = '\
|
||||
<div class="dropzone import-image-zone" id="' + dropzoneId + '">\
|
||||
<div class="dropzone" style="padding: 30px;margin: 10px 20% 30px;border: 4px dashed #ccc;border-radius: 8px;text-align: center;color: #444;" id="' + dropzoneId + '">\
|
||||
<p>\
|
||||
<i>Select or drag in an image to overlay.</i>\
|
||||
</p>\
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const pixelManipulation = require('../_nomodule/PixelManipulation');
|
||||
/*
|
||||
* Invert the image
|
||||
*/
|
||||
@@ -18,11 +17,14 @@ function Invert(options, UI) {
|
||||
return [255 - r, 255 - g, 255 - b, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return pixelManipulation(input, {
|
||||
return input.pixelManipulation({
|
||||
output: output,
|
||||
changePixel: changePixel,
|
||||
format: input.format,
|
||||
@@ -41,4 +43,10 @@ function Invert(options, UI) {
|
||||
UI: UI
|
||||
};
|
||||
}
|
||||
module.exports = Invert;
|
||||
var info = {
|
||||
'name': 'invert',
|
||||
'description': 'Inverts the image.',
|
||||
'inputs': {
|
||||
}
|
||||
};
|
||||
module.exports = [Invert, info];
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
];
|
||||
@@ -49,7 +49,7 @@ module.exports = function MinifyImage(options, UI) {
|
||||
reader.readAsDataURL(result);
|
||||
reader.onloadend = function () {
|
||||
base64data = reader.result;
|
||||
output(null, base64data, input.format, false);
|
||||
output(base64data, input.format);
|
||||
if (callback) callback();
|
||||
return;
|
||||
};
|
||||
@@ -76,14 +76,19 @@ module.exports = function MinifyImage(options, UI) {
|
||||
});
|
||||
var destPath = __dirname + '/results/test.' + input.format;
|
||||
var data = base64Img.base64Sync(destPath);
|
||||
output(null, data, input.format, false);
|
||||
output(data, input.format);
|
||||
if (callback) callback();
|
||||
})().catch(e => console.log(e));
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = {
|
||||
src: datauri,
|
||||
format: mimetype
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 600 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 222 KiB |
@@ -25,8 +25,11 @@ module.exports = function Ndvi(options, UI) {
|
||||
return [x, x, x, a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
function modifiedCallback() {
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
{
|
||||
"name": "ndvi-colormap",
|
||||
"description": "Sequentially Applies NDVI and Colormap steps",
|
||||
"inputs": {
|
||||
"filter": {
|
||||
"type": "select",
|
||||
"desc": "Filter color",
|
||||
"default": "red",
|
||||
"values": ["red", "blue"]
|
||||
},
|
||||
"colormap": {
|
||||
"type": "select",
|
||||
"desc": "Name of the Colormap",
|
||||
"default": "default",
|
||||
"values": [
|
||||
"default",
|
||||
"greyscale",
|
||||
"stretched",
|
||||
"fastie"
|
||||
]
|
||||
}
|
||||
},
|
||||
"inputs": {},
|
||||
"docs-link": "https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#ndvi-colormap-module"
|
||||
}
|
||||
|
||||
@@ -15,8 +15,10 @@ module.exports = function NoiseReduction(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -25,7 +27,6 @@ module.exports = function NoiseReduction(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -4,17 +4,12 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
options.x = options.x || defaults.x;
|
||||
options.y = options.y || defaults.y;
|
||||
|
||||
if(options.step.inBrowser && !options.noUI && sequencer.getSteps().length < 2)
|
||||
options.offset = -1;
|
||||
|
||||
if (options.step.inBrowser && !options.noUI) var ui = require('./Ui.js')(options.step, UI);
|
||||
|
||||
var output;
|
||||
|
||||
// This function is called on every draw.
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
options.offset = parseInt(options.offset || defaults.offset);
|
||||
options.offset = parseInt(options.offset) || -2;
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
@@ -23,6 +18,16 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
//parse the inputs
|
||||
parseCornerCoordinateInputs(options, {
|
||||
src: input.src,
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
}, function(options, input) {
|
||||
options.x = parseInt(input.x.valInp);
|
||||
options.y = parseInt(input.y.valInp);
|
||||
});
|
||||
|
||||
// save the pixels of the base image
|
||||
var baseStepImage = this.getStep(options.offset).image;
|
||||
var baseStepOutput = this.getOutput(options.offset);
|
||||
@@ -30,19 +35,6 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
var getPixels = require('get-pixels');
|
||||
|
||||
getPixels(input.src, function(err, pixels) {
|
||||
// parse the inputs
|
||||
parseCornerCoordinateInputs({
|
||||
iw: pixels.shape[0],
|
||||
ih: pixels.shape[1]
|
||||
},
|
||||
{
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
}, function(opt, input) {
|
||||
options.x = parseInt(input.x.valInp);
|
||||
options.y = parseInt(input.y.valInp);
|
||||
});
|
||||
|
||||
options.secondImagePixels = pixels;
|
||||
|
||||
function changePixel(r1, g1, b1, a1, x, y) {
|
||||
@@ -63,15 +55,11 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
return [r1, g1, b1, a1];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
}
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
function modifiedCallback() {
|
||||
if (options.step.inBrowser && !options.noUI) {
|
||||
ui.setup();
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
// run PixelManipulation on first Image pixels
|
||||
@@ -82,7 +70,7 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
format: baseStepOutput.format,
|
||||
image: baseStepImage,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: modifiedCallback,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
module.exports = function OverlayModuleUi(step, ui) {
|
||||
|
||||
function setup() {
|
||||
var steps = sequencer.getSteps();
|
||||
steps.forEach(function (_step, index) {
|
||||
if(_step.options && step.options.number === _step.options.number) {
|
||||
if(index === 1){
|
||||
step.ui.querySelector('input[type=range]').value = -1;
|
||||
step.ui.querySelector('input[type=range]').min = -1;
|
||||
}else
|
||||
step.ui.querySelector('input[type=range]').min = -index;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setup: setup
|
||||
};
|
||||
};
|
||||
@@ -15,10 +15,7 @@
|
||||
"offset": {
|
||||
"type": "integer",
|
||||
"desc": "offset to the output of the step on which the output of the last step is overlayed",
|
||||
"default": -2,
|
||||
"min": -2,
|
||||
"max": -1,
|
||||
"step": 1
|
||||
"default": -2
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#overlay-module"
|
||||
|
||||
@@ -16,8 +16,9 @@ module.exports = function PaintBucket(options, UI) {
|
||||
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -24,7 +24,7 @@ module.exports = exports = function(pixels, options) {
|
||||
minFactor = (1 - tolerance / 100);
|
||||
fillColor = fillColor.substring(fillColor.indexOf('(') + 1, fillColor.length - 1); // extract only the values from rgba(_,_,_,_)
|
||||
fillColor = fillColor.split(',');
|
||||
fillColor[3] = fillColor[3] * 255;
|
||||
|
||||
function isSimilar(currx, curry) {
|
||||
return (pixels.get(currx, curry, 0) >= r * minFactor && pixels.get(currx, curry, 0) <= r * maxFactor &&
|
||||
pixels.get(currx, curry, 1) >= g * minFactor && pixels.get(currx, curry, 1) <= g * maxFactor &&
|
||||
|
||||
@@ -18,8 +18,11 @@ module.exports = function ReplaceColor(options, UI) {
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
@@ -29,7 +32,6 @@ module.exports = function ReplaceColor(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
module.exports = exports = function(pixels, options){
|
||||
const pixelSetter = require('../../util/pixelSetter.js');
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
var color = options.color || defaults.color;
|
||||
var color = options.color || 'rgb(228,86,81)';
|
||||
color = color.substring(color.indexOf('(') + 1, color.length - 1); // extract only the values from rgba(_,_,_,_)
|
||||
|
||||
var replaceColor = options.replaceColor || defaults.replaceColor;
|
||||
var replaceColor = options.replaceColor || 'rgb(0,0,255)';
|
||||
replaceColor = replaceColor.substring(replaceColor.indexOf('(') + 1, replaceColor.length - 1); // extract only the values from rgba(_,_,_,_)
|
||||
|
||||
var replaceMethod = options.replaceMethod || defaults.replaceMethod;
|
||||
var replaceMethod = options.replaceMethod || 'greyscale';
|
||||
color = color.split(',');
|
||||
replaceColor = replaceColor.split(',');
|
||||
|
||||
@@ -17,7 +16,7 @@ module.exports = exports = function(pixels, options){
|
||||
cg = color[1],
|
||||
cb = color[2];
|
||||
|
||||
var tolerance = options.tolerance || defaults.tolerance;
|
||||
var tolerance = options.tolerance || 50;
|
||||
var maxFactor = (1 + tolerance / 100);
|
||||
var minFactor = (1 - tolerance / 100);
|
||||
|
||||
|
||||
@@ -1,71 +1,61 @@
|
||||
const imagejs = require('imagejs'),
|
||||
pixelSetter = require('../../util/pixelSetter'),
|
||||
ndarray = require('ndarray');
|
||||
/*
|
||||
* Resize the image by given percentage value
|
||||
*/
|
||||
module.exports = function Resize(options, UI) {
|
||||
|
||||
let output;
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.resize = options.resize || defaults.resize;
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
const step = this;
|
||||
var step = this;
|
||||
|
||||
var imagejs = require('imagejs');
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
// Value above 100% scales up, and below 100% scales down
|
||||
const resize_value = parseInt(options.resize.slice(0, -1));
|
||||
// value above 100% scales up, and below 100% scales down
|
||||
var resize_value = parseInt(options.resize.slice(0, -1));
|
||||
|
||||
if (resize_value == 100) return pixels;
|
||||
var new_width,
|
||||
new_height;
|
||||
|
||||
new_width = Math.round(pixels.shape[0] * (resize_value / 100));
|
||||
new_height = Math.round(pixels.shape[1] * (resize_value / 100));
|
||||
|
||||
var bitmap = new imagejs.Bitmap({ width: pixels.shape[0], height: pixels.shape[1] });
|
||||
bitmap._data.data = pixels.data;
|
||||
|
||||
|
||||
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,
|
||||
var resized = bitmap.resize({
|
||||
width: new_width, height: new_height,
|
||||
algorithm: 'bicubicInterpolation'
|
||||
});
|
||||
|
||||
const newPix = new ndarray([], [new_width, new_height, 4]);
|
||||
pixels.data = resized._data.data;
|
||||
pixels.shape = [new_width, new_height, 4];
|
||||
pixels.stride[1] = 4 * new_width;
|
||||
|
||||
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 pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
|
||||
@@ -3,63 +3,41 @@
|
||||
*/
|
||||
module.exports = function Rotate(options, UI) {
|
||||
|
||||
let output;
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
options.rotate = options.rotate || defaults.rotate;
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
const step = this;
|
||||
var step = this;
|
||||
|
||||
var imagejs = require('imagejs');
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a];
|
||||
}
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
const rotate_value = (options.rotate) % 360;
|
||||
radians = (Math.PI) * rotate_value / 180,
|
||||
width = pixels.shape[0],
|
||||
height = pixels.shape[1],
|
||||
cos = Math.cos(radians),
|
||||
sin = Math.sin(radians);
|
||||
// Final dimensions after rotation
|
||||
|
||||
const finalPixels = require('ndarray')(
|
||||
new Uint8Array(
|
||||
4 *
|
||||
(
|
||||
Math.floor(
|
||||
Math.abs(width * cos) +
|
||||
Math.abs(height * sin) +
|
||||
5
|
||||
) *
|
||||
(
|
||||
Math.floor(
|
||||
Math.abs(width * sin) +
|
||||
Math.abs(height * cos)
|
||||
) +
|
||||
5
|
||||
)
|
||||
)
|
||||
).fill(255),
|
||||
[
|
||||
Math.floor(Math.abs(width * cos) + Math.abs(height * sin)) + 5,
|
||||
Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 4,
|
||||
4
|
||||
]
|
||||
);
|
||||
|
||||
pixels = require('./Rotate')(pixels, finalPixels, rotate_value, width, height, cos, sin);
|
||||
var rotate_value = (options.rotate) % 360;
|
||||
var radians = (Math.PI) * rotate_value / 180;
|
||||
var width = pixels.shape[0];
|
||||
var height = pixels.shape[1];
|
||||
var cos = Math.cos(radians);
|
||||
var sin = Math.sin(radians);
|
||||
//final dimensions after rotation
|
||||
var pixels2 = require('ndarray')(new Uint8Array(4 * (Math.floor(Math.abs(width * cos) + Math.abs(height * sin) + 5) * (Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 5))).fill(0), [Math.floor(Math.abs(width * cos) + Math.abs(height * sin)) + 5, Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 4, 4]);
|
||||
pixels = require('./Rotate')(pixels, pixels2, options, rotate_value, width, height, cos, sin);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -1,81 +1,38 @@
|
||||
const imagejs = require('imagejs'),
|
||||
ndarray = require('ndarray'),
|
||||
pixelSetter = require('../../util/pixelSetter');
|
||||
module.exports = function Rotate(pixels, pixels2, options, rotate_value, width, height, cos, sin){
|
||||
var imagejs = require('imagejs');
|
||||
var height_half = Math.floor(height / 2);
|
||||
var width_half = Math.floor(width / 2);
|
||||
var dimension = width + height;
|
||||
|
||||
module.exports = function Rotate(pixels, finalPixels, rotate_value, width, height, cos, sin){
|
||||
const height_half = Math.floor(height / 2),
|
||||
width_half = Math.floor(width / 2);
|
||||
dimension = width + height;
|
||||
|
||||
if (rotate_value % 360 == 0) return pixels;
|
||||
|
||||
function copyPixel(x1, y1, x2, y2, finalPix, initPix) {
|
||||
finalPix.set(x1, y1, 0, initPix.get(x2, y2, 0));
|
||||
finalPix.set(x1, y1, 1, initPix.get(x2, y2, 1));
|
||||
finalPix.set(x1, y1, 2, initPix.get(x2, y2, 2));
|
||||
finalPix.set(x1, y1, 3, initPix.get(x2, y2, 3));
|
||||
if (rotate_value % 360 == 0)
|
||||
return pixels;
|
||||
function copyPixel(x1, y1, x2, y2,pixel_set,pixel_get){
|
||||
pixel_set.set(x1, y1, 0, pixel_get.get(x2, y2, 0));
|
||||
pixel_set.set(x1, y1, 1, pixel_get.get(x2, y2, 1));
|
||||
pixel_set.set(x1, y1, 2, pixel_get.get(x2, y2, 2));
|
||||
pixel_set.set(x1, y1, 3, pixel_get.get(x2, y2, 3));
|
||||
}
|
||||
|
||||
const intermediatePixels = new ndarray(
|
||||
new Uint8Array(4 * dimension * dimension).fill(255),
|
||||
[dimension, dimension, 4]
|
||||
); // Intermediate ndarray of pixels with a greater size to prevent clipping.
|
||||
|
||||
// Copying all the pixels from image to intermediatePixels
|
||||
for (let x = 0; x < pixels.shape[0]; x++){
|
||||
for (let y = 0; y < pixels.shape[1]; y++){
|
||||
copyPixel(x + height_half, y + width_half, x, y, intermediatePixels, pixels);
|
||||
pixels1 = require('ndarray')(new Uint8Array(4 * dimension * dimension).fill(0), [dimension, dimension, 4]);
|
||||
//copying all the pixels from image to pixels1
|
||||
for (var n = 0; n < pixels.shape[0]; n++){
|
||||
for (var m = 0; m < pixels.shape[1]; m++){
|
||||
copyPixel(n + height_half, m + width_half, n, m,pixels1,pixels);
|
||||
}
|
||||
}
|
||||
//rotating pixels1
|
||||
var bitmap = new imagejs.Bitmap({ width: pixels1.shape[0], height: pixels1.shape[1] });
|
||||
bitmap._data.data = pixels1.data;
|
||||
|
||||
// Rotating intermediatePixels
|
||||
const bitmap = new imagejs.Bitmap({ width: intermediatePixels.shape[0], height: intermediatePixels.shape[1] });
|
||||
|
||||
for (let x = 0; x < intermediatePixels.shape[0]; x++) {
|
||||
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
|
||||
let r = intermediatePixels.get(x, y, 0),
|
||||
g = intermediatePixels.get(x, y, 1),
|
||||
b = intermediatePixels.get(x, y, 2),
|
||||
a = intermediatePixels.get(x, y, 3);
|
||||
|
||||
bitmap.setPixel(x, y, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
const rotated = bitmap.rotate({
|
||||
var rotated = bitmap.rotate({
|
||||
degrees: rotate_value,
|
||||
});
|
||||
|
||||
for (let x = 0; x < intermediatePixels.shape[0]; x++) {
|
||||
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
|
||||
const {r, g, b, a} = rotated.getPixel(x, y);
|
||||
pixelSetter(x, y, [r, g, b, a], intermediatePixels);
|
||||
pixels1.data = rotated._data.data;
|
||||
//cropping extra whitespace
|
||||
for (var n = 0; n < pixels2.shape[0]; n++){
|
||||
for (var m = 0; m < pixels2.shape[1]; m++){
|
||||
copyPixel(n, m, n + Math.floor(dimension / 2 - Math.abs(width * cos / 2) - Math.abs(height * sin / 2)) - 1, m + Math.floor(dimension / 2 - Math.abs(height * cos / 2) - Math.abs(width * sin / 2)) - 1,pixels2,pixels1);
|
||||
}
|
||||
}
|
||||
|
||||
// Cropping extra whitespace
|
||||
for (let x = 0; x < finalPixels.shape[0]; x++){
|
||||
for (let y = 0; y < finalPixels.shape[1]; y++){
|
||||
copyPixel(
|
||||
x,
|
||||
y,
|
||||
x +
|
||||
Math.floor(
|
||||
dimension / 2 -
|
||||
Math.abs(width * cos / 2) -
|
||||
Math.abs(height * sin / 2)
|
||||
) - 1,
|
||||
y +
|
||||
Math.floor(
|
||||
dimension / 2 -
|
||||
Math.abs(height * cos / 2) -
|
||||
Math.abs(width * sin / 2)
|
||||
) - 1,
|
||||
finalPixels,
|
||||
intermediatePixels
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return finalPixels;
|
||||
return pixels2;
|
||||
};
|
||||
|
||||
@@ -31,8 +31,11 @@ module.exports = function Saturation(options, UI) {
|
||||
return [Math.round(r), Math.round(g), Math.round(b), a];
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -7,21 +7,19 @@ module.exports = function TextOverlay(options, UI) {
|
||||
|
||||
var step = this;
|
||||
|
||||
function extraManipulation(pixels, setRenderState, generateOutput) {
|
||||
var priorStep = this.getStep(-1); // get the previous step to add text onto it.
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
//if (options.step.inBrowser)
|
||||
const getDataUri = require('../../util/getDataUri');
|
||||
getDataUri(pixels, input.format).then(dataUri => {
|
||||
setRenderState(false);
|
||||
pixels = require('./TextOverlay')(pixels, options, dataUri, () => {
|
||||
setRenderState(true);
|
||||
generateOutput();
|
||||
});
|
||||
|
||||
});
|
||||
pixels = require('./TextOverlay')(pixels, options, priorStep);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
// This output is accesible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const getPixels = require('get-pixels'),
|
||||
pixelSetter = require('../../util/pixelSetter.js');
|
||||
module.exports = exports = function(pixels, options, url1, cb){
|
||||
module.exports = exports = function(pixels, options, priorstep){
|
||||
|
||||
var $ = require('jquery'); // to make text-overlay work for node.js
|
||||
|
||||
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
|
||||
|
||||
options.text = options.text || defaults.text;
|
||||
@@ -10,46 +11,20 @@ module.exports = exports = function(pixels, options, url1, cb){
|
||||
options.color = options.color || defaults.color;
|
||||
options.size = options.size || defaults.size;
|
||||
|
||||
var img = $(priorstep.imgElement);
|
||||
if(Object.keys(img).length === 0){
|
||||
img = $(priorstep.options.step.imgElement);
|
||||
}
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = pixels.shape[0]; //img.width();
|
||||
canvas.height = pixels.shape[1]; //img.height();
|
||||
var ctx = canvas.getContext('2d');
|
||||
var image = new Image;
|
||||
image.src = url1;
|
||||
image.onload = function(){
|
||||
|
||||
ctx.drawImage(image, 0, 0);
|
||||
ctx.fillStyle = options.color;
|
||||
ctx.font = options.size + 'px ' + options.font;
|
||||
ctx.fillText(options.text, options.x, options.y);
|
||||
ctx.drawImage(img[0], 0, 0);
|
||||
ctx.fillStyle = options.color;
|
||||
ctx.font = options.size + 'px ' + options.font;
|
||||
ctx.fillText(options.text, options.x, options.y);
|
||||
|
||||
getPixels(canvas.toDataURL(), function (err, qrPixels) {
|
||||
if (err) {
|
||||
console.log('get-pixels error: ', err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (let x = 0; x < pixels.shape[0]; x++) {
|
||||
for (let y = 0; y < pixels.shape[1]; y++) {
|
||||
pixelSetter(
|
||||
x,
|
||||
y,
|
||||
[
|
||||
qrPixels.get(x, y, 0),
|
||||
qrPixels.get(x, y, 1),
|
||||
qrPixels.get(x, y, 2),
|
||||
qrPixels.get(x, y, 3)
|
||||
],
|
||||
pixels
|
||||
);
|
||||
}
|
||||
}
|
||||
if (cb) cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
var myImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
pixels.data = myImageData.data;
|
||||
return pixels;
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "text-overlay",
|
||||
"description": "Overlay text on image.",
|
||||
"requires": ["webgl", "browser"],
|
||||
"inputs": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
|
||||
@@ -22,11 +22,10 @@ module.exports = function ImageThreshold(options, UI) {
|
||||
pixels = require('./Threshold')(pixels, options, hist);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype, wasmSuccess) {
|
||||
step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm };
|
||||
function output(image, datauri, mimetype) {
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
ui: options.step.ui,
|
||||
@@ -34,7 +33,6 @@ module.exports = function ImageThreshold(options, UI) {
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback,
|
||||
useWasm:options.useWasm
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user