Compare commits

...

18 Commits

Author SHA1 Message Date
Varun Gupta
0638a1c68c refactor and remove hysteresis (#327)
* refactor and remove hysteresis

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fixes #323

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* bump version #
2018-09-05 16:39:13 -04:00
Mridul97
6106e5ee0c Disable-save-button (#322) 2018-08-30 15:04:09 -04:00
Jeffrey Warren
d4eff45664 version bup (#321) 2018-08-06 14:42:19 -04:00
Varun Gupta
5a3b94d4ee fix (#319)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-06 14:33:37 -04:00
Varun Gupta
a238fc65e1 2.2.0 (#317) 2018-08-06 12:10:45 -04:00
Varun Gupta
e15e981d0b Colorbar added (#316)
* setup

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* overlay done

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add colorbar fixes #285

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add inputs to colorbar

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-06 11:53:08 -04:00
Varun Gupta
43e6253095 change url string fixes #306 (#313)
* change url string fixes #306

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fix typo

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-04 17:44:09 -04:00
Varun Gupta
6be996692a pixel value on hover in ndvi fixes #307 (#314)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-04 11:09:36 -04:00
Varun Gupta
5b4e904636 add toCliString function fixes #140 (#312)
* add toCliString function fixes #140

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add test

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-04 10:38:41 -04:00
Varun Gupta
5ab018fb84 Meta modules (#308)
* meta-modules in node

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* meta-modules in browser

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* more doc

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fix typo

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs and sample meta-module

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add dynamic modules CLI from npm

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* meta-modules and sequence saving completed

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fixed test

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-08-04 10:07:20 -04:00
Varun Gupta
10e47656a3 add steps api functions (#293)
* add steps api functions

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* optimize getHeight and getWidth

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* add tests

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update tests

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-07-25 14:00:54 -04:00
kevinkmmak
56feb4c546 Add-button-dim (#310)
Dim the button onLoad, then enable it when selected.
2018-07-24 12:55:52 -04:00
Varun Gupta
73a9cd2961 named export for single image basic mode fixes #297 (#301)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-07-18 10:39:21 -04:00
Varun Gupta
42d49112ef implement importNewModule (#294)
* implement importNewModule

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* make apis available on input object

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* minor updates and more docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* major refactor simplify module loading

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* final updates and invert installed from npm

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update contributing.md

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fixes

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update documentation

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-07-18 10:14:16 -04:00
Varun Gupta
0c0147354e fallback to jpg when no format detected (#296)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-07-01 08:09:10 -04:00
Varun Gupta
510cf3421a reduce redundance of add and insert fixes #261 (#295)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-06-21 15:15:09 -04:00
Varun Gupta
8d6b82d988 Minimize mod req (#289)
* remove trailing spaces from Run.js

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* minimize module requirements demonstrated with invert fixes #122

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* refactored modules

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* remove all trailing spaces from all files

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fixing crop module

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* fix

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>

* update contributing.md

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-06-15 14:44:18 -05:00
Varun Gupta
5132612dc2 fixed the demo (#291)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-06-11 11:25:33 -04:00
81 changed files with 10984 additions and 1903 deletions

View File

@@ -35,23 +35,18 @@ Any module must follow this basic format:
```js
module.exports = function ModuleName(options,UI) {
options = options || {};
UI.onSetup(options.step);
var output;
// Module requirements have been simplified in version 3, see https://github.com/publiclab/image-sequencer/blob/master/CONTRIBUTING.md#contributing-modules
function draw(input,callback) {
UI.onDraw(options.step); // tell the UI to "draw"
var output = function(input){
/* do something with the input */
return input;
}
this.output = output(input); // run the output and assign it to this.output
options.step.output = output.src;
callback();
UI.onComplete(options.step); // tell UI we are done
}
return {
@@ -63,6 +58,57 @@ module.exports = function ModuleName(options,UI) {
}
```
Image Sequencer modules are designed to be run either in the browser or in a Node.js environment. For dynamically loaded modules, that means that any uses of `require()` to include an external library must be compiled using a system like `browserify` or `webpack` to ensure browser compatibility. An example of this can be found here:
https://github.com/tech4gt/image-sequencer
If you wish to offer a module without browser-compatibility, please indicate this in the returned `info` object as:
module.exports = [
ModuleName,
{ only: ['node'] }
];
If you believe that full cross-compatibility is possible, but need help, please open an issue on your module's issue tracker requesting assistance (and potentially link to it from an inline comment or the module description).
Any Independent Module Must follow this basic format
```js
function ModuleName(options,UI) {
var output;
function draw(input,callback) {
var output = function(input){
/* do something with the input */
return input;
}
this.output = output(input); // run the output and assign it to this.output
callback();
}
return {
options: options,
draw: draw,
output: output,
UI: UI
};
}
module.exports = [ModuleName,{
"name": "ModuleName",
"description": "",
"inputs": {
// inputs here
}
/* Info can be defined here or imported from a json file */
// require('./info.json') This only works in node
// In a module compiled with browserify or webpack, a require() can be used to
// load a standard info.json file.
];
```
### options
@@ -84,9 +130,18 @@ The `draw` method should accept an `input` parameter, which will be an object of
```js
input = {
src: "datauri of an image here",
format: "jpeg/png/etc"
format: "jpeg/png/etc",
// utility functions
getPixels: "function to get Image pixels. Wrapper around https://npmjs.com/get-pixels",
savePixels: "function to save Image pixels. Wrapper around https://npmjs.com/save-pixels",
lodash: "wrapper around lodash library, https://github.com/lodash",
dataUriToBuffer: "wrapper around https://www.npmjs.com/package/data-uri-to-buffer",
pixelManipulation: "general purpose pixel manipulation API, see https://github.com/publiclab/image-sequencer/blob/master/src/modules/_nomodule/PixelManipulation.js"
}
```
For example usage for pixelManipulation see https://github.com/publiclab/image-sequencer/blob/master/src/modules/Invert/Module.js
**The module is included in the browser inside a script tag and since the code runs directly in the browser if any other module is required apart from the apis available on the input object, it should be either bundled with the module code and imported in es6 format or the module code must be browserified before distribution for browser**
The draw method is run every time the step is `run` using `sequencer.run()`.
So any calculations must go **into** the `draw()` method's definition.
@@ -171,32 +226,25 @@ The default "loading spinner" can be optionally overriden with a custom progress
```js
module.exports = function ModuleName(options,UI) {
options = options || {};
UI.onSetup(options.step);
var output;
function draw(input,callback,progressObj) {
/* If you wish to supply your own progress bar you need to override progressObj */
progressObj.stop() // Stop the current progress spinner
progressObj.overrideFlag = true; // Tell image sequencer that you will supply your own progressBar
progressObj.overrideFlag = true; // Tell image sequencer that you will supply your own progressBar
/* Override the object and give your own progress Bar */
progressObj = /* Your own progress Object */
UI.onDraw(options.step);
var output = function(input){
/* do something with the input */
/* do something with the input */
return input;
};
this.output = output();
options.step.output = output.src;
callback();
UI.onComplete(options.step);
}
return {
@@ -217,3 +265,70 @@ See existing module `green-channel` for an example: https://github.com/publiclab
The `green-channel` module is included into the core modules here: https://github.com/publiclab/image-sequencer/blob/master/src/Modules.js#L5-L7
For help integrating, please open an issue.
## Meta Module
IMAGE SEQUENCER supports "meta modules" -- modules made of other modules. The syntax and structure of these meta modules is very similar to standard modules. Sequencer can also genarate meta modules dynamically with the function `createMetaModule` which can be called in the following ways
```js
// stepsString is a stringified sequence
sequencer.createMetaModule(stepsString,info)
/* stepsArray is the array of objects in this format
* [
* {name: "moduleName",options: {}},
* {name: "moduleName",options: {}}
* ]
*/
sequencer.createMetaModule(stepsArray,info)
```
A Meta module can also be contributed like a normal module with an info and a Module.js. A basic Meta module shall follow the following format
```js
// Module.js
module.exports = function metaModuleFun(){
this.expandSteps([
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', options: {} }
]);
}
```
```json
// Info
{
"name": "meta-moduleName",
"description": "",
"length": //Integer representing number of steps in the metaModule
}
```
```js
//index.js
module.exports = [
require('./Module.js'),
require('./info.json')
]
```
All of the above can also be combined together to form a single file module.
```js
// MetaModule.js
module.export = [
function (){
this.expandSteps([
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', options: {} }
]);
},
{
"name": "meta-moduleName",
"description": "",
"length": //Integer representing number of steps in the metaModule
}
]
```
The length is absolutely required for a meta-module, since sequencer is optimized to re-run minimum number of steps when a step is added in the UI which is 1 in the case of normal modules, if the added step is a meta-module the length of the sequence governs the number of steps to re-run.

View File

@@ -1,4 +1,4 @@
module.exports = function (grunt) {
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-browserify");
grunt.loadNpmTasks("grunt-contrib-uglify-es");
grunt.loadNpmTasks("grunt-browser-sync");
@@ -15,7 +15,7 @@ module.exports = function (grunt) {
livereload: true
},
source: {
files: ["src/*.js", "src/*/*.js", "src/*/*/*.js", "Gruntfile.js"],
files: ["src/**/*", "Gruntfile.js"],
tasks: ["build:js"]
}
},

View File

@@ -104,8 +104,10 @@ Image Sequencer also provides a CLI for applying operations to local files. The
-b | --basic | Basic mode only outputs the final image
-o | --output [PATH] | Directory where output will be stored. (optional)
-c | --config {object} | Options for the step. (optional)
--save-sequence [string] | Name space separated with Stringified sequence to save
--install-module [string] | Module name space seaprated npm package name
The basic format for using the CLI is as follows:
The basic format for using the CLI is as follows:
```
$ ./index.js -i [PATH] -s step-name
@@ -121,15 +123,24 @@ The CLI also can take multiple steps at once, like so:
But for this, double quotes must wrap the space-separated steps.
Options for the steps can be passed in one line as json in the details option like
Options for the steps can be passed in one line as json in the details option like
```
$ ./index.js -i [PATH] -s "brightness" -d '{"brightness":50}'
$ ./index.js -i [PATH] -s "brightness" -c '{"brightness":50}'
```
Or the values can be given through terminal prompt like
<img width="1436" alt="screen shot 2018-02-14 at 5 18 50 pm" src="https://user-images.githubusercontent.com/25617855/36202790-3c6e8204-11ab-11e8-9e17-7f3387ab0158.png">
`save-sequence` option can be used to save a sequence and the associated options for later usage. You should provide a string which contains a name of the sequence space separated from the sequence of steps which constitute it.
```shell
sequencer --save-sequence "invert-colormap invert(),colormap()"
```
`install-module` option can be used to install new modules from npm. You can register this module in your sequencer with a custom name space sepated with the npm package name. Below is an example for the `image-sequencer-invert` module.
```shell
sequencer --install-module "invert image-sequencer-invert"
```
The CLI is also chainable with other commands using `&&`
@@ -277,8 +288,18 @@ sequencer.insertSteps(index,module_name,optional_options);
return value: **`sequencer`** (To allow method chaining)
### Importing an independent module
The `loadNewModule` method can be used to import a new module inside sequencer. Modules can be downloaded via npm, yarn or cdn and are imported with a custom name. If you wish to load a new module at runtime, it will need to avoid using `require()` -- unless it is compiled with a system like browserify or webpack.
```js
const module = require('sequencer-moduleName')
sequencer.loadNewModule('moduleName',module);
```
## Method Chaining
Methods can be chained on the Image Sequencer:
* loadImage()/loadImages() can only terminate a chain.
* run() can not be in the middle of the chain.
@@ -451,6 +472,47 @@ sequencer.insertSteps({
```
return value: **`sequencer`** (To allow method chaining)
## Saving Sequences
IMAGE SEQUENCER supports saving a sequence of modules and their associated settings in a simple string syntax. These sequences can be saved in the local storage inside the browser and inside a json file in node.js. sequences can be saved in node context using the CLI option
```shell
--save-sequence "name stringified-sequence"
```
In Node and the browser the following function can be used
```js
sequencer.saveSequence(name,sequenceString)
```
The function `sequencer.loadModules()` reloads the modules and the saved sequences into `sequencer.modules` and `sequencer.sequences`
## String syntax
Image sequencer supports stringifying a sequence which is appended to the url and hence can then be shared. An example below shows the string syntax for `channel` and `invert` module
```
channel{channel:green},invert{}
```
Sequencer also supports use of `()` in place of `{}` for backwards compatibility with older links.(This syntax is deprecated and should be avoided as far as possible)
```
channel(channel:green),invert()
```
Following are the core API functions that can be used to stringify and jsonify steps.
```js
sequencer.toString() //returns the stringified sequence of current steps
sequencer.toJSON(str) // returns the JSON for the current sequence
sequencer.stringToJSON(str) // returns the JSON for given stringified sequence
sequencer.importString(str) //Imports the sequence of steps into sequencer
sequencer.importJSON(obj) //Imports the given sequence of JSON steps into sequencer
```
Image Sequencer can also generate a string for usage in the CLI for the current sequence of steps:
```js
sequencer.toCliString()
```
## Creating a User Interface

2151
dist/image-sequencer.js vendored Executable file → Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -107,3 +107,7 @@ h1 {
text-align: center;
margin: 10px;
}
#save-seq {
display: block;
margin: 0 auto;
}

View File

@@ -1,15 +1,21 @@
window.onload = function() {
sequencer = ImageSequencer();
// Load information of all modules (Name, Inputs, Outputs)
var modulesInfo = sequencer.modulesInfo();
function refreshOptions() {
// Load information of all modules (Name, Inputs, Outputs)
var modulesInfo = sequencer.modulesInfo();
// Add modules to the addStep dropdown
for (var m in modulesInfo) {
$("#addStep select").append(
'<option value="' + m + '">' + modulesInfo[m].name + "</option>"
);
var addStepSelect = $("#addStep select");
addStepSelect.html("");
// Add modules to the addStep dropdown
for (var m in modulesInfo) {
addStepSelect.append(
'<option value="' + m + '">' + modulesInfo[m].name + "</option>"
);
}
}
refreshOptions();
// UI for each step:
sequencer.setUI(DefaultHtmlStepUi(sequencer));
@@ -22,6 +28,11 @@ window.onload = function() {
$("#addStep select").on("change", ui.selectNewStepUi);
$("#addStep button").on("click", ui.addStepUi);
$('body').on('click', 'button.remove', ui.removeStepUi);
$('#save-seq').click(() => {
sequencer.saveSequence(window.prompt("Please give a name to your sequence..."), sequencer.toString());
sequencer.loadModules();
refreshOptions();
});
// image selection and drag/drop handling from examples/lib/imageSelection.js
sequencer.setInputStep({
@@ -31,7 +42,7 @@ window.onload = function() {
var reader = progress.target;
var step = sequencer.images.image1.steps[0];
step.output.src = reader.result;
sequencer.run(0);
sequencer.run({ index: 0 });
step.options.step.imgElement.src = reader.result;
}
});

View File

@@ -14,6 +14,7 @@
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../src/ui/prepareDynamic.js"></script>
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
<script src="lib/urlHash.js" charset="utf-8"></script>
<script src="lib/defaultHtmlStepUi.js" charset="utf-8"></script>
@@ -80,15 +81,16 @@
</div>
</div>
</section>
<button class="btn btn-primary btn-lg" name="save-sequence" id="save-seq">Save Sequence</button>
</div>
<script type="text/javascript">
$(function () {
$(function() {
var sequencer;
})
</script>
</body>
</html>
</html>

View File

@@ -7,6 +7,8 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
function onLoad() {
importStepsFromUrlHash();
if (!$('#selectStep').val())
$(addStepSel + " button").prop("disabled", true);
}
// look up needed steps from Url Hash:
@@ -15,7 +17,7 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
if (hash) {
_sequencer.importString(hash);
_sequencer.run({index:0});
_sequencer.run({ index: 0 });
}
setUrlHashParameter("steps", sequencer.toString());
}
@@ -23,11 +25,12 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
function selectNewStepUi() {
var m = $(addStepSel + " select").val();
$(addStepSel + " .info").html(_sequencer.modulesInfo(m).description);
$(addStepSel + " button").prop("disabled", false);
}
function removeStepUi() {
var index = $(removeStepSel).index(this) + 1;
sequencer.removeSteps(index).run({index : index-1});
sequencer.removeSteps(index).run({ index: index - 1 });
// remove from URL hash too
setUrlHashParameter("steps", sequencer.toString());
}
@@ -42,9 +45,15 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
* and since loadImage is not a part of the drawarray the step lies at current
* length - 2 of the drawarray
*/
var sequenceLength = 1;
if (sequencer.sequences[newStepName]) {
sequenceLength = sequencer.sequences[newStepName].length;
} else if (sequencer.modules[newStepName][1]["length"]) {
sequenceLength = sequencer.modules[newStepName][1]["length"];
}
_sequencer
.addSteps(newStepName, options)
.run({index: _sequencer.images.image1.steps.length - 2});
.addSteps(newStepName, options)
.run({ index: _sequencer.images.image1.steps.length - sequenceLength - 1 });
// add to URL hash too
setUrlHashParameter("steps", _sequencer.toString());

View File

@@ -60,14 +60,14 @@ function DefaultHtmlStepUi(_sequencer, options) {
if (!isInput) {
html += '<span class="output"></span>';
} else if (inputDesc.type.toLowerCase() == "select") {
html += '<select class="form-control" name="' + paramName + '">';
html += '<select class="form-control target" name="' + paramName + '">';
for (var option in inputDesc.values) {
html += "<option>" + inputDesc.values[option] + "</option>";
}
html += "</select>";
} else {
html =
'<input class="form-control" type="' +
'<input class="form-control target" type="' +
inputDesc.type +
'" name="' +
paramName +
@@ -91,8 +91,15 @@ function DefaultHtmlStepUi(_sequencer, options) {
</div>";
step.ui.querySelector("div.details").appendChild(div);
}
function toggleSaveButton(){
$(step.ui.querySelector("div.details .btn-save")).prop("disabled",false);
}
$(step.ui.querySelector(".target")).change(toggleSaveButton);
$(step.ui.querySelector("div.details")).append(
"<p><button class='btn btn-default btn-save'>Save</button></p>"
"<p><button class='btn btn-default btn-save' disabled = 'true' >Save</button></p>"
);
function saveOptions() {
@@ -101,10 +108,12 @@ function DefaultHtmlStepUi(_sequencer, options) {
.each(function(i, input) {
step.options[$(input).attr("name")] = input.value;
});
_sequencer.run({index: _sequencer.images.image1.steps.length - 2});
_sequencer.run({ index: step.index - 1 });
// modify the url hash
setUrlHashParameter("steps", _sequencer.toString());
// disable the save button
$(step.ui.querySelector("div.details .btn-save")).prop("disabled",true);
}
// on clicking Save in the details pane of the step

293
index.js
View File

@@ -19,97 +19,118 @@ program
.option("-o, --output [PATH]", "Directory where output will be stored.")
.option("-b, --basic", "Basic mode outputs only final image")
.option("-c, --config [Object]", "Options for the step")
.option("--save-sequence [string]", "Name space separated with Stringified sequence")
.option('--install-module [string]', "Module name space seaprated npm package name")
.parse(process.argv);
// Parse step into an array to allow for multiple steps.
if (!program.step) exit("No steps passed");
program.step = program.step.split(" ");
if (program.saveSequence) {
var params = program.saveSequence.split(' ');
sequencer.saveSequence(params[0], params[1]);
} else if (program.installModule) {
var params = program.installModule.split(' ');
var spinner = Spinner("Now Installing...").start();
require('child_process').execSync(`npm i ${params[1]}`)
sequencer.saveNewModule(params[0], params[1]);
sequencer.loadNewModule(params[0], require(params[1]));
spinner.stop();
} else {
// Parse step into an array to allow for multiple steps.
if (!program.step) exit("No steps passed");
program.step = program.step.split(" ");
// User must input an image.
if (!program.image) exit("Can't read file.");
// User must input an image.
if (!program.image) exit("Can't read file.");
// User must input an image.
require("fs").access(program.image, function(err) {
if (err) exit("Can't read file.");
});
// User must input an image.
require("fs").access(program.image, function(err) {
if (err) exit("Can't read file.");
});
// User must input a step. If steps exist, check that every step is a valid step.
if (!program.step || !validateSteps(program.step))
exit("Please ensure all steps are valid.");
// User must input a step. If steps exist, check that every step is a valid step.
if (!program.step || !validateSteps(program.step))
exit("Please ensure all steps are valid.");
// If there's no user defined output directory, select a default directory.
program.output = program.output || "./output/";
// If there's no user defined output directory, select a default directory.
program.output = program.output || "./output/";
// Set sequencer to log module outputs, if any.
sequencer.setUI({
onComplete: function(step) {
// Get information of outputs.
step.info = sequencer.modulesInfo(step.name);
// Set sequencer to log module outputs, if any.
sequencer.setUI({
onComplete: function(step) {
// Get information of outputs.
step.info = sequencer.modulesInfo(step.name);
for (var output in step.info.outputs) {
console.log("[" + program.step + "]: " + output + " = " + step[output]);
}
}
});
// Finally, if everything is alright, load the image, add the steps and run the sequencer.
sequencer.loadImages(program.image, function() {
console.warn(
"\x1b[33m%s\x1b[0m",
"Please wait \n output directory generated will be empty until the execution is complete"
);
//Generate the Output Directory
require("./src/CliUtils").makedir(program.output, () => {
console.log('Files will be exported to "' + program.output + '"');
if (program.basic)
console.log("Basic mode is enabled, outputting only final image");
// Iterate through the steps and retrieve their inputs.
program.step.forEach(function(step) {
var options = Object.assign({}, sequencer.modulesInfo(step).inputs);
// If inputs exists, print to console.
if (Object.keys(options).length) {
console.log("[" + step + "]: Inputs");
for (var output in step.info.outputs) {
console.log("[" + program.step + "]: " + output + " = " + step[output]);
}
}
});
// If inputs exists, print them out with descriptions.
Object.keys(options).forEach(function(input) {
// The array below creates a variable number of spaces. This is done with (length + 1).
// The extra 4 that makes it (length + 5) is to account for the []: characters
console.log(
new Array(step.length + 5).join(" ") +
// Finally, if everything is alright, load the image, add the steps and run the sequencer.
sequencer.loadImages(program.image, function() {
console.warn(
"\x1b[33m%s\x1b[0m",
"Please wait \n output directory generated will be empty until the execution is complete"
);
//Generate the Output Directory
var outputFilename = program.output.split('/').slice(-1)[0];
if (outputFilename.includes('.')) {
// user did give an output filename we have to remove it from dir
program.output = program.output.split('/').slice(0, -1).join('/');
}
else {
outputFilename = null;
}
require("./src/CliUtils").makedir(program.output, () => {
console.log('Files will be exported to "' + program.output + '"');
if (program.basic)
console.log("Basic mode is enabled, outputting only final image");
// Iterate through the steps and retrieve their inputs.
program.step.forEach(function(step) {
var options = Object.assign({}, sequencer.modulesInfo(step).inputs);
// If inputs exists, print to console.
if (Object.keys(options).length) {
console.log("[" + step + "]: Inputs");
}
// If inputs exists, print them out with descriptions.
Object.keys(options).forEach(function(input) {
// The array below creates a variable number of spaces. This is done with (length + 1).
// The extra 4 that makes it (length + 5) is to account for the []: characters
console.log(
new Array(step.length + 5).join(" ") +
input +
": " +
options[input].desc
);
});
if (program.config) {
try {
program.config = JSON.parse(program.config);
console.log(`The parsed options object: `, program.config);
} catch (e) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options(Config) is not a not valid JSON Fallback activate`
);
program.config = false;
console.log(e);
}
}
if (program.config && validateConfig(program.config, options)) {
console.log("Now using Options object");
Object.keys(options).forEach(function(input) {
options[input] = program.config[input];
});
} else {
// If inputs exist, iterate through them and prompt for values.
Object.keys(options).forEach(function(input) {
var value = readlineSync.question(
"[" +
if (program.config) {
try {
var config = JSON.parse(program.config);
console.log(`The parsed options object: `, config);
} catch (e) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options(Config) is not a not valid JSON Fallback activate`
);
program.config = false;
console.log(e);
}
}
if (program.config && validateConfig(config, options)) {
console.log("Now using Options object");
Object.keys(options).forEach(function(input) {
options[input] = config[input];
});
} else {
// If inputs exist, iterate through them and prompt for values.
Object.keys(options).forEach(function(input) {
var value = readlineSync.question(
"[" +
step +
"]: Enter a value for " +
input +
@@ -118,65 +139,67 @@ sequencer.loadImages(program.image, function() {
", default: " +
options[input].default +
"): "
);
options[input] = value;
});
}
// Add the step and its inputs to the sequencer.
sequencer.addSteps(step, options);
});
var spinnerObj = { succeed: () => {}, stop: () => {} };
if (!process.env.TEST)
spinnerObj = Spinner("Your Image is being processed..").start();
// Run the sequencer.
sequencer.run({progressObj: spinnerObj}, function() {
// Export all images or final image as binary files.
sequencer.exportBin(program.output, program.basic);
//check if spinner was not overriden stop it
if (!spinnerObj.overrideFlag) {
spinnerObj.succeed();
console.log(`\nDone!!`);
}
});
});
});
// Takes an array of steps and checks if they are valid steps for the sequencer.
function validateSteps(steps) {
// Assume all are valid in the beginning.
var valid = true;
steps.forEach(function(step) {
// If any step in the array is not valid (not a property of modulesInfo), set valid to false.
if (!sequencer.modulesInfo().hasOwnProperty(step)) {
valid = false;
}
});
// Return valid. (If all of the steps are valid properties, valid will have remained true).
return valid;
}
//Takes config and options object and checks if all the keys exist in config
function validateConfig(config_, options_) {
options_ = Object.keys(options_);
if (
(function() {
for (var input in options_) {
if (!config_[options_[input]]) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options Object does not have the required details "${
options_[input]
}" not specified. Fallback case activated`
);
return false;
);
options[input] = value;
});
}
// Add the step and its inputs to the sequencer.
sequencer.addSteps(step, options);
});
var spinnerObj = { succeed: () => { }, stop: () => { } };
if (!process.env.TEST)
spinnerObj = Spinner("Your Image is being processed..").start();
// Run the sequencer.
sequencer.run({ progressObj: spinnerObj }, function() {
// Export all images or final image as binary files.
sequencer.exportBin(program.output, program.basic, outputFilename);
//check if spinner was not overriden stop it
if (!spinnerObj.overrideFlag) {
spinnerObj.succeed();
console.log(`\nDone!!`);
}
});
});
});
// Takes an array of steps and checks if they are valid steps for the sequencer.
function validateSteps(steps) {
// Assume all are valid in the beginning.
var valid = true;
steps.forEach(function(step) {
// If any step in the array is not valid (not a property of modulesInfo), set valid to false.
if (!sequencer.modulesInfo().hasOwnProperty(step)) {
valid = false;
}
})() == false
)
return false;
else return true;
});
// Return valid. (If all of the steps are valid properties, valid will have remained true).
return valid;
}
//Takes config and options object and checks if all the keys exist in config
function validateConfig(config_, options_) {
options_ = Object.keys(options_);
if (
(function() {
for (var input in options_) {
if (!config_[options_[input]]) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options Object does not have the required details "${
options_[input]
}" not specified. Fallback case activated`
);
return false;
}
}
})() == false
)
return false;
else return true;
}
}

8059
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
{
"name": "image-sequencer",
"version": "2.1.0",
"version": "2.2.2",
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
"main": "src/ImageSequencer.js",
"scripts": {
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
"test": "TEST=true tape test/**/*.js test/*.js | tap-spec; browserify test/modules/image-sequencer.js test/modules/chain.js test/modules/replace.js test/modules/import-export.js test/modules/run.js | tape-run --render=\"tap-spec\"",
"test": "TEST=true tape test/**/*.js test/*.js | tap-spec; browserify test/modules/image-sequencer.js test/modules/chain.js test/modules/meta-modules.js test/modules/replace.js test/modules/import-export.js test/modules/run.js test/modules/dynamic-imports.js | tape-run --render=\"tap-spec\"",
"start": "grunt serve"
},
"repository": {
@@ -29,6 +29,7 @@
"fisheyegl": "^0.1.2",
"font-awesome": "~4.5.0",
"get-pixels": "~3.3.0",
"image-sequencer-invert": "^1.0.0",
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
"jquery": "~2",
"jsqr": "^0.2.2",
@@ -42,7 +43,6 @@
},
"devDependencies": {
"browserify": "13.0.0",
"data-uri-to-buffer": "^2.0.0",
"grunt": "^0.4.5",
"grunt-browser-sync": "^2.2.0",
"grunt-browserify": "^5.0.0",

View File

@@ -1,37 +1,5 @@
// add steps to the sequencer
// TODO: reduce redundancy with InsertStep; this should be a specific usage of InsertStep at the final position
// add steps to the sequencer
function AddStep(_sequencer, image, name, o) {
function addStep(image, name, o_) {
if (_sequencer.modules[name]) var moduleInfo = _sequencer.modules[name][1];
else console.log('Module ' + name + ' not found.');
var o = _sequencer.copy(o_);
o.number = _sequencer.options.sequencerCounter++; // gives a unique ID to each step
o.name = o_.name || name || moduleInfo.name;
o.description = o_.description || moduleInfo.description;
o.selector = o_.selector || 'ismod-' + name;
o.container = o_.container || _sequencer.options.selector;
o.image = image;
o.inBrowser = _sequencer.options.inBrowser;
o.step = {
name: o.name,
description: o.description,
ID: o.number,
imageName: o.image,
inBrowser: _sequencer.options.inBrowser,
ui: _sequencer.options.ui,
options: o
};
var UI = _sequencer.events;
var module = _sequencer.modules[name][0](o, UI);
_sequencer.images[image].steps.push(module);
return true;
}
addStep(image, name, o);
_sequencer.steps = _sequencer.images[image].steps;
return require('./InsertStep')(_sequencer,image,-1,name,o);
}
module.exports = AddStep;

View File

@@ -2,7 +2,7 @@ const fs = require('fs')
/*
/*
* This function checks if the directory exists, if not it creates one on the given path
* Callback is called with argument error if an error is encountered
*/

View File

@@ -1,60 +1,73 @@
var fs = require('fs');
var getDirectories = function(rootDir, cb) {
fs.readdir(rootDir, function(err, files) {
var dirs = [];
if(typeof(files)=="undefined" || files.length == 0) {
cb(dirs);
return [];
}
for (var index = 0; index < files.length; ++index) {
var file = files[index];
if (file[0] !== '.') {
var filePath = rootDir + '/' + file;
fs.stat(filePath, function(err, stat) {
if (stat.isDirectory()) {
dirs.push(this.file);
}
if (files.length === (this.index + 1)) {
return cb(dirs);
}
}.bind({index: index, file: file}));
var dirs = [];
if (typeof (files) == "undefined" || files.length == 0) {
cb(dirs);
return [];
}
for (var index = 0; index < files.length; ++index) {
var file = files[index];
if (file[0] !== '.') {
var filePath = rootDir + '/' + file;
fs.stat(filePath, function(err, stat) {
if (stat.isDirectory()) {
dirs.push(this.file);
}
if (files.length === (this.index + 1)) {
return cb(dirs);
}
}.bind({ index: index, file: file }));
}
}
});
}
module.exports = function ExportBin(dir = "./output/",ref,basic) {
dir = (dir[dir.length-1]=="/") ? dir : dir + "/";
if(ref.options.inBrowser) return false;
fs.access(dir, function(err){
if(err) console.error(err)
module.exports = function ExportBin(dir = "./output/", ref, basic, filename) {
// If user did not give an output filename so we can continue without doing anything
dir = (dir[dir.length - 1] == "/") ? dir : dir + "/";
if (ref.options.inBrowser) return false;
fs.access(dir, function(err) {
if (err) console.error(err)
});
getDirectories(dir,function(dirs){
var num = 1;
for(var d in dirs){
if(dirs[d].match(/^sequencer(.*)$/)==null) continue;
var n = parseInt(dirs[d].match(/^sequencer(.*)$/)[1]);
num = (n>=num)?(n+1):num;
if (filename && basic) {
for (var image in ref.images) {
var steps = ref.images[image].steps;
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(dir + filename, buffer, function() { });
}
fs.mkdir(dir+'sequencer'+num,function(){
var root = dir+'sequencer'+num+'/';
for(var image in ref.images) {
var steps = ref.images[image].steps;
if(basic){
var datauri = steps.slice(-1)[0].output.src;
}
else {
getDirectories(dir, function(dirs) {
var num = 1;
for (var d in dirs) {
if (dirs[d].match(/^sequencer(.*)$/) == null) continue;
var n = parseInt(dirs[d].match(/^sequencer(.*)$/)[1]);
num = (n >= num) ? (n + 1) : num;
}
fs.mkdir(dir + 'sequencer' + num, function() {
var root = dir + 'sequencer' + num + '/';
for (var image in ref.images) {
var steps = ref.images[image].steps;
if (basic) {
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root+image+"_"+(steps.length-1)+"."+ext,buffer,function(){});
}
else{
for(var i in steps) {
var datauri = steps[i].output.src;
var ext = steps[i].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root+image+"_"+i+"."+ext,buffer,function(){});
fs.writeFile(root + image + "_" + (steps.length - 1) + "." + ext, buffer, function() { });
}
else {
for (var i in steps) {
var datauri = steps[i].output.src;
var ext = steps[i].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root + image + "_" + i + "." + ext, buffer, function() { });
}
}
}
}
})
});
});
});
}
}

View File

@@ -1,27 +1,27 @@
if (typeof window !== 'undefined') {isBrowser = true}
else {var isBrowser = false}
require('./util/getStep.js')
if (typeof window !== 'undefined') { isBrowser = true }
else { var isBrowser = false }
require('./util/getStep.js');
ImageSequencer = function ImageSequencer(options) {
var sequencer = (this.name == "ImageSequencer")?this:this.sequencer;
var sequencer = (this.name == "ImageSequencer") ? this : this.sequencer;
options = options || {};
options.inBrowser = options.inBrowser || isBrowser;
options.sequencerCounter = 0;
function objTypeOf(object){
return Object.prototype.toString.call(object).split(" ")[1].slice(0,-1)
function objTypeOf(object) {
return Object.prototype.toString.call(object).split(" ")[1].slice(0, -1)
}
function log(color,msg) {
if(options.ui!="none") {
if(arguments.length==1) console.log(arguments[0]);
else if(arguments.length==2) console.log(color,msg);
function log(color, msg) {
if (options.ui != "none") {
if (arguments.length == 1) console.log(arguments[0]);
else if (arguments.length == 2) console.log(color, msg);
}
}
function copy(a) {
if (!typeof(a) == "object") return a;
if (!typeof (a) == "object") return a;
if (objTypeOf(a) == "Array") return a.slice();
if (objTypeOf(a) == "Object") {
var b = {};
@@ -34,62 +34,76 @@ ImageSequencer = function ImageSequencer(options) {
}
function makeArray(input) {
return (objTypeOf(input)=="Array")?input:[input];
return (objTypeOf(input) == "Array") ? input : [input];
}
var image,
steps = [],
modules = require('./Modules'),
formatInput = require('./FormatInput'),
images = {},
inputlog = [],
events = require('./ui/UserInterface')(),
fs = require('fs');
steps = [],
modules = require('./Modules'),
sequences = require('./SavedSequences.json'),
formatInput = require('./FormatInput'),
images = {},
inputlog = [],
events = require('./ui/UserInterface')(),
fs = require('fs');
if (options.inBrowser) {
for (o in sequencer) {
modules[o] = sequencer[o];
}
sequences = JSON.parse(window.localStorage.getItem('sequences'));
if (!sequences) {
sequences = {};
window.localStorage.setItem('sequences', JSON.stringify(sequences));
}
}
// if in browser, prompt for an image
// if (options.imageSelect || options.inBrowser) addStep('image-select');
// else if (options.imageUrl) loadImage(imageUrl);
function addSteps(){
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
var args = (this.name == "ImageSequencer")?[]:[this.images];
function addSteps() {
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
var json_q = {};
for(var arg in arguments){args.push(copy(arguments[arg]));}
json_q = formatInput.call(this_,args,"+");
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)});
inputlog.push({ method: "addSteps", json_q: copy(json_q) });
for (var i in json_q)
for (var j in json_q[i])
require("./AddStep")(this_,i,json_q[i][j].name,json_q[i][j].o);
for (var j in json_q[i])
require("./AddStep")(this_, i, json_q[i][j].name, json_q[i][j].o);
return this;
}
function removeStep(image,index) {
function removeStep(image, index) {
//remove the step from images[image].steps and redraw remaining images
if(index>0) {
if (index > 0) {
thisStep = images[image].steps[index];
thisStep.UI.onRemove(thisStep.options.step);
images[image].steps.splice(index,1);
images[image].steps.splice(index, 1);
}
//tell the UI a step has been removed
}
function removeSteps(image,index) {
function removeSteps(image, index) {
var run = {}, indices;
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
var args = (this.name == "ImageSequencer")?[]:[this.images];
for(var arg in arguments) args.push(copy(arguments[arg]));
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
for (var arg in arguments) args.push(copy(arguments[arg]));
var json_q = formatInput.call(this_,args,"-");
inputlog.push({method:"removeSteps", json_q:copy(json_q)});
var json_q = formatInput.call(this_, args, "-");
inputlog.push({ method: "removeSteps", json_q: copy(json_q) });
for (var img in json_q) {
indices = json_q[img].sort(function(a,b){return b-a});
run[img] = indices[indices.length-1];
indices = json_q[img].sort(function(a, b) { return b - a });
run[img] = indices[indices.length - 1];
for (var i in indices)
removeStep(img,indices[i]);
removeStep(img, indices[i]);
}
// this.run(run); // This is creating problems
return this;
@@ -97,19 +111,19 @@ ImageSequencer = function ImageSequencer(options) {
function insertSteps(image, index, name, o) {
var run = {};
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
var args = (this.name == "ImageSequencer")?[]:[this.images];
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
for (var arg in arguments) args.push(arguments[arg]);
var json_q = formatInput.call(this_,args,"^");
inputlog.push({method:"insertSteps", json_q:copy(json_q)});
var json_q = formatInput.call(this_, args, "^");
inputlog.push({ method: "insertSteps", json_q: copy(json_q) });
for (var img in json_q) {
var details = json_q[img];
details = details.sort(function(a,b){return b.index-a.index});
details = details.sort(function(a, b) { return b.index - a.index });
for (var i in details)
require("./InsertStep")(this_,img,details[i].index,details[i].name,details[i].o);
run[img] = details[details.length-1].index;
require("./InsertStep")(this_, img, details[i].index, details[i].name, details[i].o);
run[img] = details[details.length - 1].index;
}
// this.run(run); // This is Creating issues
return this;
@@ -117,33 +131,33 @@ ImageSequencer = function ImageSequencer(options) {
// Config is an object which contains the runtime configuration like progress bar
// information and index from which the sequencer should run
function run(config,t_image,t_from) {
let progressObj,index=0;
config = config || {mode: 'no-arg'};
if(config.index) index = config.index;
function run(config, t_image, t_from) {
let progressObj, index = 0;
config = config || { mode: 'no-arg' };
if (config.index) index = config.index;
if(config.mode != 'test'){
if(config.mode != "no-arg" && typeof config != 'function'){
if(config.progressObj) progressObj = config.progressObj;
if (config.mode != 'test') {
if (config.mode != "no-arg" && typeof config != 'function') {
if (config.progressObj) progressObj = config.progressObj;
delete arguments['0'];
}
}
else{
else {
arguments['0'] = config.mode;
}
var this_ = (this.name == "ImageSequencer")?this:this.sequencer;
var args = (this.name == "ImageSequencer")?[]:[this.images];
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
for (var arg in arguments) args.push(copy(arguments[arg]));
var callback = function() {};
var callback = function() { };
for (var arg in args)
if(objTypeOf(args[arg]) == "Function")
callback = args.splice(arg,1)[0];
if (objTypeOf(args[arg]) == "Function")
callback = args.splice(arg, 1)[0];
var json_q = formatInput.call(this_,args,"r");
var json_q = formatInput.call(this_, args, "r");
require('./Run')(this_, json_q, callback,index,progressObj);
require('./Run')(this_, json_q, callback, index, progressObj);
return true;
}
@@ -152,9 +166,9 @@ ImageSequencer = function ImageSequencer(options) {
var args = [];
var sequencer = this;
for (var arg in arguments) args.push(copy(arguments[arg]));
var json_q = formatInput.call(this,args,"l");
var json_q = formatInput.call(this, args, "l");
inputlog.push({method:"loadImages", json_q:copy(json_q)});
inputlog.push({ method: "loadImages", json_q: copy(json_q) });
var loadedimages = this.copy(json_q.loadedimages);
var ret = {
@@ -170,12 +184,12 @@ ImageSequencer = function ImageSequencer(options) {
};
function load(i) {
if(i==loadedimages.length) {
if (i == loadedimages.length) {
json_q.callback.call(ret);
return;
}
var img = loadedimages[i];
require('./ui/LoadImage')(sequencer,img,json_q.images[img],function(){
require('./ui/LoadImage')(sequencer, img, json_q.images[img], function() {
load(++i);
});
}
@@ -183,34 +197,57 @@ ImageSequencer = function ImageSequencer(options) {
load(0);
}
function replaceImage(selector,steps,options) {
function replaceImage(selector, steps, options) {
options = options || {};
options.callback = options.callback || function() {};
return require('./ReplaceImage')(this,selector,steps,options);
options.callback = options.callback || function() { };
return require('./ReplaceImage')(this, selector, steps, options);
}
function setUI(UI) {
this.events = require('./ui/UserInterface')(UI);
}
var exportBin = function(dir,basic) {
return require('./ExportBin')(dir,this,basic);
var exportBin = function(dir, basic, filename) {
return require('./ExportBin')(dir, this, basic, filename);
}
function modulesInfo(name) {
var modulesdata = {}
if(name == "load-image") return {};
if(arguments.length==0)
for (var modulename in modules) {
modulesdata[modulename] = modules[modulename][1];
if (name == "load-image") return {};
if (arguments.length == 0) {
for (var modulename in this.modules) {
modulesdata[modulename] = modules[modulename][1];
}
for (var sequencename in this.sequences) {
modulesdata[sequencename] = { name: sequencename, steps: sequences[sequencename] };
}
}
else {
if (modules[name])
modulesdata = modules[name][1];
else
modulesdata = { 'inputs': sequences[name]['options'] };
}
else modulesdata = modules[name][1];
return modulesdata;
}
// Genates a CLI string for the current sequence
function toCliString() {
var cliStringSteps = `"`, cliOptions = {};
for (var step in this.steps) {
if (this.steps[step].options.name !== "load-image")
cliStringSteps += `${this.steps[step].options.name} `;
for (var inp in modulesInfo(this.steps[step].options.name).inputs) {
cliOptions[inp] = this.steps[step].options[inp];
}
}
cliStringSteps = cliStringSteps.substr(0, cliStringSteps.length - 1) + `"`;
return `sequencer -i [PATH] -s ${cliStringSteps} -d '${JSON.stringify(cliOptions)}'`
}
// Strigifies the current sequence
function toString(step) {
if(step) {
if (step) {
return stepToString(step);
} else {
return copy(this.images.image1.steps).map(stepToString).slice(1).join(',');
@@ -222,40 +259,54 @@ ImageSequencer = function ImageSequencer(options) {
let inputs = copy(modulesInfo(step.options.name).inputs);
inputs = inputs || {};
for(let input in inputs) {
for (let input in inputs) {
inputs[input] = step.options[input] || inputs[input].default;
inputs[input] = encodeURIComponent(inputs[input]);
}
var configurations = Object.keys(inputs).map(key => key + ':' + inputs[key]).join('|');
return `${step.options.name}(${configurations})`;
return `${step.options.name}{${configurations}}`;
}
// exports the current sequence as an array of JSON steps
function toJSON(str){
function toJSON() {
return this.stringToJSON(this.toString());
}
// Coverts stringified sequence into an array of JSON steps
function stringToJSON(str){
let steps = str.split(',');
function stringToJSON(str) {
let steps;
if (str.includes(','))
steps = str.split(',');
else
steps = [str];
return steps.map(stringToJSONstep);
}
// Converts one stringified step into JSON
function stringToJSONstep(str){
if(str.indexOf('(') === -1) { // if there are no settings specified
function stringToJSONstep(str) {
var bracesStrings;
if (str.includes('{'))
if (str.includes('(') && str.indexOf('(') < str.indexOf('{'))
bracesStrings = ['(', ')'];
else
bracesStrings = ['{', '}'];
else
bracesStrings = ['(', ')'];
if (str.indexOf(bracesStrings[0]) === -1) { // if there are no settings specified
var moduleName = str.substr(0);
stepSettings = "";
} else {
var moduleName = str.substr(0, str.indexOf('('));
stepSettings = str.slice(str.indexOf('(') + 1, -1);
var moduleName = str.substr(0, str.indexOf(bracesStrings[0]));
stepSettings = str.slice(str.indexOf(bracesStrings[0]) + 1, -1);
}
stepSettings = stepSettings.split('|').reduce(function formatSettings(accumulator, current, i){
stepSettings = stepSettings.split('|').reduce(function formatSettings(accumulator, current, i) {
var settingName = current.substr(0, current.indexOf(':')),
settingValue = current.substr(current.indexOf(':') + 1);
settingValue = current.substr(current.indexOf(':') + 1);
settingValue = settingValue.replace(/^\(/, '').replace(/\)$/, ''); // strip () at start/end
settingValue = settingValue.replace(/^\{/, '').replace(/\}$/, ''); // strip {} at start/end
settingValue = decodeURIComponent(settingValue);
current = [
settingName,
@@ -266,38 +317,114 @@ ImageSequencer = function ImageSequencer(options) {
}, {});
return {
name : moduleName,
name: moduleName,
options: stepSettings
}
}
// imports a string into the sequencer steps
function importString(str){
function importString(str) {
let sequencer = this;
if(this.name != "ImageSequencer")
sequencer = this.sequencer;
if (this.name != "ImageSequencer")
sequencer = this.sequencer;
var stepsFromString = stringToJSON(str);
stepsFromString.forEach(function eachStep(stepObj) {
sequencer.addSteps(stepObj.name,stepObj.options);
sequencer.addSteps(stepObj.name, stepObj.options);
});
}
// imports a array of JSON steps into the sequencer steps
function importJSON(obj){
function importJSON(obj) {
let sequencer = this;
if(this.name != "ImageSequencer")
sequencer = this.sequencer;
if (this.name != "ImageSequencer")
sequencer = this.sequencer;
obj.forEach(function eachStep(stepObj) {
sequencer.addSteps(stepObj.name,stepObj.options);
sequencer.addSteps(stepObj.name, stepObj.options);
});
}
function loadNewModule(name, options) {
if (!options) {
return this;
} else if (Array.isArray(options)) {
// contains the array of module and info
this.modules[name] = options;
} else if (options.func && options.info) {
// passed in options object
this.modules[name] = [
options.func, options.info
];
} else if (options.path && !this.inBrowser) {
// load from path(only in node)
const module = [
require(`${options.path}/Module.js`),
require(`${options.path}/info.json`)
];
this.modules[name] = module;
}
return this;
}
function saveNewModule(name, path) {
if (options.inBrowser) {
// Not for browser context
return;
}
var mods = fs.readFileSync('./src/Modules.js').toString();
mods = mods.substr(0, mods.length - 1) + " '" + name + "': require('" + path + "'),\n}";
fs.writeFileSync('./src/Modules.js', mods);
}
function createMetaModule(stepsCollection, info) {
var stepsArr = stepsCollection;
if (typeof stepsCollection === 'string')
stepsArr = stringToJSON(stepsCollection);
var metaMod = function() {
this.expandSteps(stepsArr);
return {
isMeta: true
}
}
return [metaMod, info];
}
function saveSequence(name, sequenceString) {
const sequence = stringToJSON(sequenceString);
// Save the given sequence string as a module
if (options.inBrowser) {
// Inside the browser we save the meta-modules using the Web Storage API
var sequences = JSON.parse(window.localStorage.getItem('sequences'));
sequences[name] = sequence;
window.localStorage.setItem('sequences', JSON.stringify(sequences));
}
else {
// In node we save the sequences in the json file SavedSequences.json
var sequences = require('./SavedSequences.json');
sequences[name] = sequence;
fs.writeFileSync('./src/SavedSequences.json', JSON.stringify(sequences));
}
}
function loadModules() {
// This function loads the modules and saved sequences
this.modules = require('./Modules');
if (options.inBrowser)
this.sequences = JSON.parse(window.localStorage.getItem('sequences'));
else
this.sequences = require('./SavedSequences.json');
}
return {
//literals and objects
name: "ImageSequencer",
options: options,
inputlog: inputlog,
modules: modules,
sequences: sequences,
images: images,
events: events,
@@ -312,6 +439,7 @@ ImageSequencer = function ImageSequencer(options) {
setUI: setUI,
exportBin: exportBin,
modulesInfo: modulesInfo,
toCliString: toCliString,
toString: toString,
stepToString: stepToString,
toJSON: toJSON,
@@ -319,6 +447,11 @@ ImageSequencer = function ImageSequencer(options) {
stringToJSONstep: stringToJSONstep,
importString: importString,
importJSON: importJSON,
loadNewModule: loadNewModule,
saveNewModule: saveNewModule,
createMetaModule: createMetaModule,
saveSequence: saveSequence,
loadModules: loadModules,
//other functions
log: log,

View File

@@ -2,21 +2,30 @@ const getStepUtils = require('./util/getStep.js');
// insert one or more steps at a given index in the sequencer
function InsertStep(ref, image, index, name, o) {
if (ref.sequences[name]) {
return ref.importJSON(ref.sequences[name]);
}
function insertStep(image, index, name, o_) {
if (ref.modules[name]) var moduleInfo = ref.modules[name][1];
else {
console.log('Module ' + name + ' not found.');
}
var o = ref.copy(o_);
o.number = ref.options.sequencerCounter++; //Gives a Unique ID to each step
o.name = o_.name || name;
o.name = o_.name || name || moduleInfo.name;
o.description = o_.description || moduleInfo.description;
o.selector = o_.selector || 'ismod-' + name;
o.container = o_.container || ref.options.selector;
o.image = image;
o.inBrowser = ref.options.inBrowser;
if (index == -1) index = ref.images[image].steps.length;
o.step = {
name: o.name,
description: o.description,
url: o.url,
ID: o.number,
imageName: o.image,
inBrowser: ref.options.inBrowser,
@@ -24,12 +33,25 @@ function InsertStep(ref, image, index, name, o) {
options: o
};
var UI = ref.events;
var module = ref.modules[name][0](o, UI);
ref.images[image].steps.splice(index, 0, module);
// Tell UI that a step has been set up.
o = o || {};
ref.modules[name].expandSteps = function expandSteps(stepsArray) {
for (var step of stepsArray) {
ref.addSteps(step['name'], step['options']);
}
}
var mod = ref.modules[name][0](o, UI);
if (!mod.isMeta) {
UI.onSetup(o.step);
ref.images[image].steps.splice(index, 0, mod);
}
return true;
}
insertStep(image, index, name, o);
ref.steps = ref.images[image].steps;
}
module.exports = InsertStep;

View File

@@ -2,49 +2,23 @@
* Core modules and their info files
*/
module.exports = {
'channel': [
require('./modules/Channel/Module'), require('./modules/Channel/info')
],
'brightness': [
require('./modules/Brightness/Module'), require('./modules/Brightness/info')
],
'edge-detect': [
require('./modules/EdgeDetect/Module'), require('./modules/EdgeDetect/info')
],
'ndvi': [
require('./modules/Ndvi/Module'), require('./modules/Ndvi/info')
],
'invert': [
require('./modules/Invert/Module'), require('./modules/Invert/info')
],
'crop': [
require('./modules/Crop/Module'), require('./modules/Crop/info')
],
'colormap': [
require('./modules/Colormap/Module'), require('./modules/Colormap/info')
],
'decode-qr': [
require('./modules/DecodeQr/Module'), require('./modules/DecodeQr/info')
],
'fisheye-gl': [
require('./modules/FisheyeGl/Module'), require('./modules/FisheyeGl/info')
],
'dynamic': [
require('./modules/Dynamic/Module'), require('./modules/Dynamic/info')
],
'blur': [
require('./modules/Blur/Module'), require('./modules/Blur/info')
],
'saturation': [
require('./modules/Saturation/Module'), require('./modules/Saturation/info')
],
'average': [
require('./modules/Average/Module'), require('./modules/Average/info')
],
'blend': [
require('./modules/Blend/Module'), require('./modules/Blend/info')
],
'import-image': [
require('./modules/ImportImage/Module'), require('./modules/ImportImage/info')
]
}
'channel': require('./modules/Channel'),
'brightness': require('./modules/Brightness'),
'edge-detect': require('./modules/EdgeDetect'),
'ndvi': require('./modules/Ndvi'),
'crop': require('./modules/Crop'),
'colormap': require('./modules/Colormap'),
'decode-qr': require('./modules/DecodeQr'),
'fisheye-gl': require('./modules/FisheyeGl'),
'dynamic': require('./modules/Dynamic'),
'blur': require('./modules/Blur'),
'saturation': require('./modules/Saturation'),
'average': require('./modules/Average'),
'blend': require('./modules/Blend'),
'import-image': require('./modules/ImportImage'),
'overlay': require('./modules/Overlay'),
'gradient': require('./modules/Gradient'),
'invert': require('image-sequencer-invert'),
'ndvi-colormap': require('./modules/NdviColormap'),
'colorbar': require('./modules/Colorbar'),
}

View File

@@ -1,5 +1,5 @@
// Uses a given image as input and replaces it with the output.
// Works only in the browser.
// Works only in the browser.
function ReplaceImage(ref,selector,steps,options) {
if(!ref.options.inBrowser) return false; // This isn't for Node.js
var tempSequencer = ImageSequencer({ui: false});

View File

@@ -1,8 +1,8 @@
const getStepUtils = require('./util/getStep.js');
function Run(ref, json_q, callback,ind, progressObj) {
if (!progressObj) progressObj = { stop: function () { } };
function Run(ref, json_q, callback, ind, progressObj) {
if (!progressObj) progressObj = { stop: function() { } };
function drawStep(drawarray, pos) {
if (pos == drawarray.length && drawarray[pos - 1] !== undefined) {
var image = drawarray[pos - 1].image;
@@ -13,37 +13,50 @@ function Run(ref, json_q, callback,ind, progressObj) {
return true;
}
}
// so we don't run on the loadImage module:
if (drawarray[pos] !== undefined) {
var image = drawarray[pos].image;
var i = drawarray[pos].i;
var input = ref.images[image].steps[i - 1].output;
ref.images[image].steps[i].getStep = function getStep(offset) {
if(i + offset >= ref.images[image].steps.length) return {options:{name:undefined}};
if (i + offset >= ref.images[image].steps.length) return { options: { name: undefined } };
else return ref.images[image].steps.slice(i + offset)[0];
};
ref.images[image].steps[i].getIndex = function getIndex(){
ref.images[image].steps[i].getIndex = function getIndex() {
return i;
}
for (var util in getStepUtils) {
if (getStepUtils.hasOwnProperty(util)) {
ref.images[image].steps[i][util] = getStepUtils[util];
}
}
// Tell UI that a step is being drawn.
ref.images[image].steps[i].UI.onDraw(ref.images[image].steps[i].options.step);
// provides a set of standard tools for each step
var inputForNextStep = require('./RunToolkit')(ref.copy(input));
ref.images[image].steps[i].draw(
ref.copy(input),
inputForNextStep,
function onEachStep() {
// This output is accessible by UI
ref.images[image].steps[i].options.step.output = ref.images[image].steps[i].output.src;
// Tell UI that step has been drawn.
ref.images[image].steps[i].UI.onComplete(ref.images[image].steps[i].options.step);
drawStep(drawarray, ++pos);
},
progressObj
);
}
}
function drawSteps(json_q) {
var drawarray = [];
for (var image in json_q) {
@@ -55,11 +68,11 @@ function Run(ref, json_q, callback,ind, progressObj) {
}
drawStep(drawarray, ind);
}
function filter(json_q) {
for (var image in json_q) {
if (json_q[image] == 0 && ref.images[image].steps.length == 1)
delete json_q[image];
delete json_q[image];
else if (json_q[image] == 0) json_q[image]++;
}
for (var image in json_q) {
@@ -73,7 +86,7 @@ function Run(ref, json_q, callback,ind, progressObj) {
}
return json_q;
}
var json_q = filter(json_q);
return drawSteps(json_q);
}

14
src/RunToolkit.js Normal file
View File

@@ -0,0 +1,14 @@
const getPixels = require('get-pixels');
const pixelManipulation = require('./modules/_nomodule/PixelManipulation');
const lodash = require('lodash');
const dataUriToBuffer = require('data-uri-to-buffer');
const savePixels = require('save-pixels');
module.exports = function(input) {
input.getPixels = getPixels;
input.pixelManipulation = pixelManipulation;
input.lodash = lodash;
input.dataUriToBuffer = dataUriToBuffer;
input.savePixels = savePixels;
return input;
}

1
src/SavedSequences.json Normal file
View File

@@ -0,0 +1 @@
{"sample":[{"name":"invert","options":{}},{"name":"channel","options":{"channel":"red"}},{"name":"blur","options":{"blur":"5"}}]}

View File

@@ -2,30 +2,24 @@
* Average all pixel colors
*/
module.exports = function Average(options, UI){
options = options || {};
options.blur = options.blur || 2
//Tell the UI that a step has been set up
UI.onSetup(options.step);
var output;
options.step.metadata = options.step.metadata || {};
function draw(input,callback,progressObj){
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that a step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a){
return [r,g,b,a]
}
// do the averaging
// do the averaging
function extraManipulation(pixels){
var sum = [0,0,0,0];
for (var i = 0; i < pixels.data.length; i += 4) {
@@ -55,20 +49,14 @@ module.exports = function Average(options, UI){
}
function output(image, datauri, mimetype){
// This output is accessible by Image Sequencer
step.output = {
src: datauri,
format: mimetype
};
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step has been drawn.
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
@@ -77,7 +65,7 @@ module.exports = function Average(options, UI){
image: options.image,
callback: callback
});
}
return {
options: options,

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,10 +1,7 @@
module.exports = function Dynamic(options, UI, util) {
options = options || {};
options.func = options.func || "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }";
// Tell the UI that a step has been set up.
UI.onSetup(options.step);
var output;
// This function is called on every draw.
@@ -13,8 +10,6 @@ module.exports = function Dynamic(options, UI, util) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that the step is being drawn
UI.onDraw(options.step);
var step = this;
// convert to runnable code:
@@ -25,7 +20,7 @@ module.exports = function Dynamic(options, UI, util) {
// save first image's pixels
var priorStep = this.getStep(-2);
getPixels(priorStep.output.src, function (err, pixels) {
getPixels(priorStep.output.src, function(err, pixels) {
options.firstImagePixels = pixels;
function changePixel(r2, g2, b2, a2, x, y) {
@@ -45,15 +40,9 @@ module.exports = function Dynamic(options, UI, util) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
// This output is accessible by the UI
options.step.output = datauri;
// Tell the UI that the draw is complete
UI.onComplete(options.step);
}
// run PixelManipulatin on second image's pixels
// run PixelManipulatin on second image's pixels
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,33 +1,32 @@
module.exports = exports = function(pixels,blur){
let kernel = kernelGenerator(blur,1)
kernel = flipKernel(kernel)
var oldpix = pixels
for(let i=0;i<pixels.shape[0];i++){
for(let j=0;j<pixels.shape[1];j++){
let neighboutPos = getNeighbouringPixelPositions([i,j])
let acc = [0.0,0.0,0.0,0.0]
for(let a = 0; a < kernel.length; a++){
for(let b = 0; b < kernel.length; b++){
acc[0] += (oldpix.get(neighboutPos[a][b][0],neighboutPos[a][b][1],0) * kernel[a][b]);
acc[1] += (oldpix.get(neighboutPos[a][b][0],neighboutPos[a][b][1],1) * kernel[a][b]);
acc[2] += (oldpix.get(neighboutPos[a][b][0],neighboutPos[a][b][1],2) * kernel[a][b]);
acc[3] += (oldpix.get(neighboutPos[a][b][0],neighboutPos[a][b][1],3) * kernel[a][b]);
module.exports = exports = function(pixels, blur) {
let kernel = kernelGenerator(blur, 1), oldpix = pixels;
kernel = flipKernel(kernel);
for (let i = 0; i < pixels.shape[0]; i++) {
for (let j = 0; j < pixels.shape[1]; j++) {
let neighboutPos = getNeighbouringPixelPositions([i, j]);
let acc = [0.0, 0.0, 0.0, 0.0];
for (let a = 0; a < kernel.length; a++) {
for (let b = 0; b < kernel.length; b++) {
acc[0] += (oldpix.get(neighboutPos[a][b][0], neighboutPos[a][b][1], 0) * kernel[a][b]);
acc[1] += (oldpix.get(neighboutPos[a][b][0], neighboutPos[a][b][1], 1) * kernel[a][b]);
acc[2] += (oldpix.get(neighboutPos[a][b][0], neighboutPos[a][b][1], 2) * kernel[a][b]);
acc[3] += (oldpix.get(neighboutPos[a][b][0], neighboutPos[a][b][1], 3) * kernel[a][b]);
}
}
pixels.set(i,j,0,acc[0])
pixels.set(i,j,1,acc[1])
pixels.set(i,j,2,acc[2])
pixels.set(i, j, 0, acc[0]);
pixels.set(i, j, 1, acc[1]);
pixels.set(i, j, 2, acc[2]);
}
}
return pixels
//Generates a 3x3 Gaussian kernel
function kernelGenerator(sigma,size){
return pixels;
/*
//Generates a 3x3 Gaussian kernel
function kernelGenerator(sigma, size) {
/*
Trying out a variable radius kernel not working as of now
*/
// const coeff = (1.0/(2.0*Math.PI*sigma*sigma))
@@ -47,39 +46,39 @@ module.exports = exports = function(pixels,blur){
// })
// })
// result = result.map(arr=>arr.map(val=>(val + 0.0)/(sum + 0.0)))
// return result
return [
[2.0/159.0,4.0/159.0,5.0/159.0,4.0/159.0,2.0/159.0],
[4.0/159.0,9.0/159.0,12.0/159.0,9.0/159.0,4.0/159.0],
[5.0/159.0,12.0/159.0,15.0/159.0,12.0/159.0,5.0/159.0],
[4.0/159.0,9.0/159.0,12.0/159.0,9.0/159.0,4.0/159.0],
[2.0/159.0,4.0/159.0,5.0/159.0,4.0/159.0,2.0/159.0]
]
}
function getNeighbouringPixelPositions(pixelPosition){
let x = pixelPosition[0],y=pixelPosition[1]
let result = []
for(let i=-2;i<=2;i++){
let arr = []
for(let j=-2;j<=2;j++){
arr.push([x + i,y + j])
}
result.push(arr)
}
return result
}
function flipKernel(kernel){
let result = []
for(let i =kernel.length-1;i>=0;i--){
let arr = []
for(let j = kernel[i].length-1;j>=0;j--){
arr.push(kernel[i][j])
}
result.push(arr)
// return result
return [
[2.0 / 159.0, 4.0 / 159.0, 5.0 / 159.0, 4.0 / 159.0, 2.0 / 159.0],
[4.0 / 159.0, 9.0 / 159.0, 12.0 / 159.0, 9.0 / 159.0, 4.0 / 159.0],
[5.0 / 159.0, 12.0 / 159.0, 15.0 / 159.0, 12.0 / 159.0, 5.0 / 159.0],
[4.0 / 159.0, 9.0 / 159.0, 12.0 / 159.0, 9.0 / 159.0, 4.0 / 159.0],
[2.0 / 159.0, 4.0 / 159.0, 5.0 / 159.0, 4.0 / 159.0, 2.0 / 159.0]
];
}
function getNeighbouringPixelPositions(pixelPosition) {
let x = pixelPosition[0], y = pixelPosition[1], result = [];
for (let i = -2; i <= 2; i++) {
let arr = [];
for (let j = -2; j <= 2; j++)
arr.push([x + i, y + j]);
result.push(arr);
}
return result;
}
function flipKernel(kernel) {
let result = [];
for (let i = kernel.length - 1; i >= 0; i--) {
let arr = [];
for (let j = kernel[i].length - 1; j >= 0; j--) {
arr.push(kernel[i][j]);
}
result.push(arr);
}
return result;
}
return result
}
}

View File

@@ -1,45 +1,34 @@
/*
* Blur an Image
*/
module.exports = function Blur(options,UI){
options = options || {};
module.exports = function Blur(options, UI) {
options.blur = options.blur || 2
//Tell the UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback,progressObj){
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that a step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a){
return [r,g,b,a]
function changePixel(r, g, b, a) {
return [r, g, b, a]
}
function extraManipulation(pixels){
pixels = require('./Blur')(pixels,options.blur)
function extraManipulation(pixels) {
pixels = require('./Blur')(pixels, options.blur)
return pixels
}
function output(image,datauri,mimetype){
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step has been drawn.
UI.onComplete(options.step);
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
@@ -48,11 +37,11 @@ module.exports = function Blur(options,UI){
image: options.image,
callback: callback
});
}
return {
options: options,
draw: draw,
draw: draw,
output: output,
UI: UI
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,30 +1,24 @@
/*
/*
* Changes the Image Brightness
*/
module.exports = function Brightness(options,UI){
options = options || {};
//Tell the UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback,progressObj){
progressObj.stop(true);
progressObj.overrideFlag = true;
/*
In this case progress is handled by changepixel internally otherwise progressObj
needs to be overriden and used
needs to be overriden and used
For eg. progressObj = new SomeProgressModule()
*/
// Tell the UI that a step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a){
var val = (options.brightness)/100.0
@@ -33,19 +27,14 @@ module.exports = function Brightness(options,UI){
b = val*b<255?val*b:255
return [r , g, b, a]
}
function output(image,datauri,mimetype){
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step has been drawn.
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
@@ -54,7 +43,7 @@ module.exports = function Brightness(options,UI){
inBrowser: options.inBrowser,
callback: callback
});
}
return {
options: options,

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,40 +1,30 @@
/*
* Display only one color channel
*/
module.exports = function Channel(options,UI) {
module.exports = function Channel(options, UI) {
options = options || {};
options.channel = options.channel || "green";
// Tell UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback,progressObj) {
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a) {
if (options.channel == "red") return [r, 0, 0, a];
if (options.channel == "red") return [r, 0, 0, a];
if (options.channel == "green") return [0, g, 0, a];
if (options.channel == "blue") return [0, 0, b, a];
if (options.channel == "blue") return [0, 0, b, a];
}
function output(image,datauri,mimetype){
function output(image, datauri, mimetype) {
// This output is accesible by Image Sequencer
step.output = {src:datauri,format:mimetype};
step.output = { src: datauri, format: mimetype };
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step ahs been drawn
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
@@ -51,7 +41,7 @@ module.exports = function Channel(options,UI) {
return {
options: options,
//setup: setup, // optional
draw: draw,
draw: draw,
output: output,
UI: UI
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,16 @@
module.exports = function NdviColormapfunction(options, UI) {
options.x = options.x || 0;
options.y = options.y || 0;
options.colormap = options.colormap || "default";
options.h = options.h || 10;
this.expandSteps([
{ 'name': 'gradient', 'options': {} },
{ '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 } }
]);
return {
isMeta: true
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,33 @@
{
"name": "Colorbar",
"description": "Generates a colorbar to lay over the image",
"inputs": {
"colormap": {
"type": "select",
"desc": "Name of the Colormap",
"default": "default",
"values": [
"default",
"greyscale",
"stretched",
"fastie"
]
},
"x": {
"type": "integer",
"desc": "X-position of the image on which the new image is overlayed",
"default": 0
},
"y": {
"type": "integer",
"desc": "Y-position of the image on which the new image is overlayed",
"default": 0
},
"h": {
"type": "iinteger",
"desc": "height of the colorbar",
"default": 10
}
},
"length": 4
}

View File

@@ -1,9 +1,5 @@
module.exports = function Colormap(options,UI) {
options = options || {};
// Tell the UI that a step has been set up.
UI.onSetup(options.step);
var output;
// This function is called on every draw.
@@ -12,8 +8,6 @@ module.exports = function Colormap(options,UI) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that the step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a) {
@@ -27,12 +21,6 @@ module.exports = function Colormap(options,UI) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
// This output is accessible by the UI
options.step.output = datauri;
// Tell the UI that the draw is complete
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -7,8 +7,8 @@ module.exports = function Crop(input,options,callback) {
options.y = parseInt(options.y) || 0;
getPixels(input.src,function(err,pixels){
options.w = parseInt(options.w) || Math.floor(0.5*pixels.shape[0]);
options.h = parseInt(options.h) || Math.floor(0.5*pixels.shape[1]);
options.w = parseInt(options.w) || Math.floor(pixels.shape[0]);
options.h = parseInt(options.h) || Math.floor(pixels.shape[1]);
var ox = options.x;
var oy = options.y;
var w = options.w;

View File

@@ -15,12 +15,7 @@
*/
module.exports = function CropModule(options, UI) {
// TODO: we could also set this to {} if nil in AddModule.js to avoid this line:
options = options || {};
// Tell the UI that a step has been added
UI.onSetup(options.step); // we should get UI to return the image thumbnail so we can attach our own UI extensions
// we should get UI to return the image thumbnail so we can attach our own UI extensions
// add our custom in-module html ui:
if (options.step.inBrowser) var ui = require('./Ui.js')(options.step, UI);
var output,
@@ -29,11 +24,9 @@ module.exports = function CropModule(options, UI) {
// This function is caled everytime the step has to be redrawn
function draw(input,callback) {
// Tell the UI that the step has been triggered
UI.onDraw(options.step);
var step = this;
// save the input image;
// save the input image;
// TODO: this should be moved to module API to persist the input image
options.step.input = input.src;

View File

@@ -4,7 +4,7 @@ module.exports = function CropModuleUi(step, ui) {
let inputWidth = 0,
inputHeight = 0;
// We don't have input image dimensions at the
// We don't have input image dimensions at the
// time of setting up the UI; that comes when draw() is triggered.
// So we trigger setup only on first run of draw()
// TODO: link this to an event rather than an explicit call in Module.js
@@ -13,7 +13,7 @@ module.exports = function CropModuleUi(step, ui) {
y = 0;
// display original uncropped input image on initial setup
showOriginal()
showOriginal();
inputWidth = Math.floor(imgEl().naturalWidth);
inputHeight = Math.floor(imgEl().naturalHeight);

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -16,12 +16,12 @@
"w": {
"type": "integer",
"desc": "Width of crop",
"default": "(50%)"
"default": "(100%)"
},
"h": {
"type": "integer",
"desc": "Height of crop",
"default": "(50%)"
"default": "(100%)"
}
}
}
}

View File

@@ -3,11 +3,6 @@
*/
module.exports = function DoNothing(options,UI) {
options = options || {};
// Tell the UI that a step has been added
UI.onSetup(options.step);
var output;
var jsQR = require('jsqr');
var getPixels = require('get-pixels');
@@ -15,8 +10,6 @@ module.exports = function DoNothing(options,UI) {
// This function is called everytime a step has to be redrawn
function draw(input,callback) {
UI.onDraw(options.step);
var step = this;
getPixels(input.src,function(err,pixels){
@@ -33,13 +26,8 @@ module.exports = function DoNothing(options,UI) {
// Tell Image Sequencer that this step is complete
callback();
// These values are accessible to the UI
options.step.output = input.src;
options.step.qrval = decoded;
// Tell the UI that the step is complete and output is set
UI.onComplete(options.step);
});
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,24 +1,18 @@
module.exports = function Dynamic(options,UI) {
options = options || {};
// Tell the UI that a step has been set up.
UI.onSetup(options.step);
var output;
// This function is called on every draw.
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that the step is being drawn
UI.onDraw(options.step);
var step = this;
// 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;'
func = func + 'return ';
@@ -27,15 +21,15 @@ module.exports = function Dynamic(options,UI) {
eval(func);
return f;
}
var channels = ['red', 'green', 'blue', 'alpha'];
channels.forEach(function(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) {
/* neighbourpixels can be calculated by
@@ -49,7 +43,7 @@ module.exports = function Dynamic(options,UI) {
options.alpha_function(r, g, b, a),
];
}
/* Functions to get the neighbouring pixel by position (x,y) */
function getNeighbourPixel(pixels,curX,curY,distX,distY){
return [
@@ -59,19 +53,13 @@ module.exports = function Dynamic(options,UI) {
,pixels.get(curX+distX,curY+distY,3)
]
}
function output(image,datauri,mimetype){
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
// This output is accessible by the UI
options.step.output = datauri;
// Tell the UI that the draw is complete
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
@@ -83,9 +71,9 @@ module.exports = function Dynamic(options,UI) {
inBrowser: options.inBrowser,
callback: callback
});
}
return {
options: options,
draw: draw,

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,179 +1,177 @@
const _ = require('lodash')
//define kernels for the sobel filter
const kernelx = [[-1,0,1],[-2,0,2],[-1,0,1]],
kernely = [[-1,-2,-1],[0,0,0],[1,2,1]]
const kernelx = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]],
kernely = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
let angles = []
let mags = []
let strongEdgePixels = []
let weakEdgePixels = []
let notInUI
module.exports = exports = function(pixels,highThresholdRatio,lowThresholdRatio,inBrowser){
notInUI = !inBrowser
for(var x = 0; x < pixels.shape[0]; x++) {
angles.push([])
mags.push([])
for(var y = 0; y < pixels.shape[1]; y++) {
let angles = [], mags = [], strongEdgePixels = [], weakEdgePixels = [], notInUI;
module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, inBrowser) {
notInUI = !inBrowser;
for (var x = 0; x < pixels.shape[0]; x++) {
angles.push([]);
mags.push([]);
for (var y = 0; y < pixels.shape[1]; y++) {
var result = changePixel(
pixels,
pixels.get(x,y,0),
pixels.get(x, y, 0),
pixels.get(x, y, 3),
x,
y
)
let pixel = result.pixel
);
let pixel = result.pixel;
pixels.set(x, y, 0, pixel[0]);
pixels.set(x, y, 1, pixel[1]);
pixels.set(x, y, 2, pixel[2]);
pixels.set(x, y, 3, pixel[3]);
mags.slice(-1)[0].push(pixel[3])
angles.slice(-1)[0].push(result.angle)
mags.slice(-1)[0].push(pixel[3]);
angles.slice(-1)[0].push(result.angle);
}
}
return hysteresis(doubleThreshold(nonMaxSupress(pixels),highThresholdRatio,lowThresholdRatio))
return doubleThreshold(nonMaxSupress(pixels), highThresholdRatio, lowThresholdRatio);
}
//changepixel function that convolutes every pixel (sobel filter)
function changePixel(pixels,val,a,x,y){
let magX = 0.0
for(let a = 0; a < 3; a++){
for(let b = 0; b < 3; b++){
function changePixel(pixels, val, a, x, y) {
let magX = 0.0;
for (let a = 0; a < 3; a++) {
for (let b = 0; b < 3; b++) {
let xn = x + a - 1;
let yn = y + b - 1;
magX += pixels.get(xn,yn,0) * kernelx[a][b];
magX += pixels.get(xn, yn, 0) * kernelx[a][b];
}
}
let magY = 0.0
for(let a = 0; a < 3; a++){
for(let b = 0; b < 3; b++){
let magY = 0.0;
for (let a = 0; a < 3; a++) {
for (let b = 0; b < 3; b++) {
let xn = x + a - 1;
let yn = y + b - 1;
magY += pixels.get(xn,yn,0) * kernely[a][b];
magY += pixels.get(xn, yn, 0) * kernely[a][b];
}
}
let mag = Math.sqrt(Math.pow(magX,2) + Math.pow(magY,2))
let angle = Math.atan2(magY,magX)
let mag = Math.sqrt(Math.pow(magX, 2) + Math.pow(magY, 2));
let angle = Math.atan2(magY, magX);
return {
pixel:
[val,val,val,mag],
[val, val, val, mag],
angle: angle
}
};
}
//Non Maximum Supression without interpolation
function nonMaxSupress(pixels) {
angles = angles.map((arr)=>arr.map(convertToDegrees))
for(let i = 1;i<pixels.shape[0]-1;i++){
for(let j=1;j<pixels.shape[1]-1;j++){
let angle = angles[i][j]
let pixel = pixels.get(i,j)
if ((angle>=-22.5 && angle<=22.5) ||
(angle<-157.5 && angle>=-180))
if ((mags[i][j]>= mags[i][j+1]) &&
(mags[i][j] >= mags[i][j-1]))
pixels.set(i,j,3,mags[i][j])
else
pixels.set(i,j,3,0)
else if ((angle>=22.5 && angle<=67.5) ||
(angle<-112.5 && angle>=-157.5))
if ((mags[i][j] >= mags[i+1][j+1]) &&
(mags[i][j] >= mags[i-1][j-1]))
pixels.set(i,j,3,mags[i][j])
else
pixels.set(i,j,3,0)
else if ((angle>=67.5 && angle<=112.5) ||
(angle<-67.5 && angle>=-112.5))
if ((mags[i][i] >= mags[i+1][j]) &&
(mags[i][j] >= mags[i][j]))
pixels.set(i,j,3,mags[i][j])
else
pixels.set(i,j,3,0)
else if ((angle>=112.5 && angle<=157.5) ||
(angle<-22.5 && angle>=-67.5))
if ((mags[i][j] >= mags[i+1][j-1]) &&
(mags[i][j] >= mags[i-1][j+1]))
pixels.set(i,j,3,mags[i][j])
else
pixels.set(i,j,3,0)
function nonMaxSupress(pixels) {
angles = angles.map((arr) => arr.map(convertToDegrees));
for (let i = 1; i < pixels.shape[0] - 1; i++) {
for (let j = 1; j < pixels.shape[1] - 1; j++) {
let angle = angles[i][j];
let pixel = pixels.get(i, j);
if ((angle >= -22.5 && angle <= 22.5) ||
(angle < -157.5 && angle >= -180))
if ((mags[i][j] >= mags[i][j + 1]) &&
(mags[i][j] >= mags[i][j - 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 22.5 && angle <= 67.5) ||
(angle < -112.5 && angle >= -157.5))
if ((mags[i][j] >= mags[i + 1][j + 1]) &&
(mags[i][j] >= mags[i - 1][j - 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 67.5 && angle <= 112.5) ||
(angle < -67.5 && angle >= -112.5))
if ((mags[i][i] >= mags[i + 1][j]) &&
(mags[i][j] >= mags[i][j]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 112.5 && angle <= 157.5) ||
(angle < -22.5 && angle >= -67.5))
if ((mags[i][j] >= mags[i + 1][j - 1]) &&
(mags[i][j] >= mags[i - 1][j + 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
}
}
return pixels
return pixels;
}
//Converts radians to degrees
var convertToDegrees = radians => (radians * 180)/Math.PI
var convertToDegrees = radians => (radians * 180) / Math.PI;
//Finds the max value in a 2d array like mags
var findMaxInMatrix = arr => Math.max(...arr.map(el=>el.map(val=>!!val?val:0)).map(el=>Math.max(...el)))
var findMaxInMatrix = arr => Math.max(...arr.map(el => el.map(val => !!val ? val : 0)).map(el => Math.max(...el)));
//Applies the double threshold to the image
function doubleThreshold(pixels,highThresholdRatio,lowThresholdRatio){
const highThreshold = findMaxInMatrix(mags) * 0.2
const lowThreshold = highThreshold * lowThresholdRatio
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio) {
for(let i =0;i<pixels.shape[0];i++){
for(let j=0;j<pixels.shape[1];j++){
let pixelPos = [i,j]
mags[i][j]>lowThreshold
?mags[i][j]>highThreshold
?strongEdgePixels.push(pixelPos)
:weakEdgePixels.push(pixelPos)
:pixels.set(i,j,3,0)
const highThreshold = findMaxInMatrix(mags) * 0.2;
const lowThreshold = highThreshold * lowThresholdRatio;
for (let i = 0; i < pixels.shape[0]; i++) {
for (let j = 0; j < pixels.shape[1]; j++) {
let pixelPos = [i, j];
mags[i][j] > lowThreshold
? mags[i][j] > highThreshold
? strongEdgePixels.push(pixelPos)
: weakEdgePixels.push(pixelPos)
: pixels.set(i, j, 3, 0);
}
}
strongEdgePixels.forEach(pix=>pixels.set(pix[0],pix[1],3,255))
return pixels
strongEdgePixels.forEach(pix => pixels.set(pix[0], pix[1], 3, 255));
return pixels;
}
// hysteresis edge tracking algorithm
function hysteresis(pixels){
function getNeighbouringPixelPositions(pixelPosition){
let x = pixelPosition[0],y=pixelPosition[1]
return [[x+1,y+1],
[x+1,y],
[x+1,y-1],
[x,y+1],
[x,y-1],
[x-1,y+1],
[x-1,y],
[x-1,y-1]]
// hysteresis edge tracking algorithm -- not working as of now
/* function hysteresis(pixels) {
function getNeighbouringPixelPositions(pixelPosition) {
let x = pixelPosition[0], y = pixelPosition[1]
return [[x + 1, y + 1],
[x + 1, y],
[x + 1, y - 1],
[x, y + 1],
[x, y - 1],
[x - 1, y + 1],
[x - 1, y],
[x - 1, y - 1]]
}
//This can potentially be improved see https://en.wikipedia.org/wiki/Connected-component_labeling
for(weakPixel in weakEdgePixels){
let neighbourPixels = getNeighbouringPixelPositions(weakEdgePixels[weakPixel])
for(pixel in neighbourPixels){
if(strongEdgePixels.find(el=> _.isEqual(el,neighbourPixels[pixel]))) {
pixels.set(weakPixel[0],weakPixel[1],3,255)
weakEdgePixels.splice(weakPixel,weakPixel)
break
}
}
}
weakEdgePixels.forEach(pix=>pixels.set(pix[0],pix[1],3,0))
return pixels
}
//This can potentially be improved see https://en.wikipedia.org/wiki/Connected-component_labeling
for (weakPixel in weakEdgePixels) {
let neighbourPixels = getNeighbouringPixelPositions(weakEdgePixels[weakPixel])
for (pixel in neighbourPixels) {
if (strongEdgePixels.find(el => _.isEqual(el, neighbourPixels[pixel]))) {
pixels.set(weakPixel[0], weakPixel[1], 3, 255)
weakEdgePixels.splice(weakPixel, weakPixel)
break
}
}
}
weakEdgePixels.forEach(pix => pixels.set(pix[0], pix[1], 3, 0))
return pixels
} */

View File

@@ -1,67 +1,56 @@
/*
* Detect Edges in an Image
*/
* Detect Edges in an Image
*/
module.exports = function edgeDetect(options,UI) {
options = options || {};
options.blur = options.blur || 2
options.highThresholdRatio = options.highThresholdRatio||0.2
options.lowThresholdRatio = options.lowThresholdRatio||0.15
// Tell UI that a step has been set up.
UI.onSetup(options.step);
var output;
// The function which is called on every draw.
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
options.blur = options.blur || 2;
options.highThresholdRatio = options.highThresholdRatio||0.2;
options.lowThresholdRatio = options.lowThresholdRatio||0.15;
// Tell UI that a step is being drawn.
UI.onDraw(options.step);
var step = this;
var output;
// The function which is called on every draw.
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
function extraManipulation(pixels){
pixels = require('ndarray-gaussian-filter')(pixels,options.blur)
return require('./EdgeUtils')(pixels,options.highThresholdRatio,options.lowThresholdRatio,options.inBrowser)
}
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){
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step has been drawn.
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
function extraManipulation(pixels){
pixels = require('ndarray-gaussian-filter')(pixels,options.blur);
return require('./EdgeUtils')(pixels,options.highThresholdRatio,options.lowThresholdRatio,options.inBrowser);
}
return {
options: options,
draw: draw,
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){
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
UI: UI
}
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -3,18 +3,12 @@
*/
module.exports = function DoNothing(options,UI) {
options = options || {};
var output;
// Tell the UI that a step has been set up.
UI.onSetup(options.step);
require('fisheyegl');
function draw(input,callback) {
// Tell the UI that the step is being drawn
UI.onDraw(options.step);
var step = this;
if (!options.inBrowser) { // This module is only for browser
@@ -59,13 +53,9 @@ module.exports = function DoNothing(options,UI) {
// this output is accessible to Image Sequencer
step.output = {src: canvas.toDataURL(), format: input.format};
// This output is accessible to the UI
options.step.output = step.output.src;
// Tell Image Sequencer and UI that step has been drawn
callback();
UI.onComplete(options.step);
});
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,62 @@
module.exports = function Invert(options, UI) {
var output;
// The function which is called on every draw.
function draw(input, callback, progressObj) {
var getPixels = require('get-pixels');
var savePixels = require('save-pixels');
var step = this;
getPixels(input.src, function(err, pixels) {
if (err) {
console.log("Bad Image path");
return;
}
var width = 0;
for (var i = 0; i < pixels.shape[0]; i++) width++;
for (var i = 0; i < pixels.shape[0]; i++) {
for (var j = 0; j < pixels.shape[1]; j++) {
let val = (i / width) * 255;
pixels.set(i, j, 0, val);
pixels.set(i, j, 1, val);
pixels.set(i, j, 2, val);
pixels.set(i, j, 3, 255);
}
}
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 {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,5 @@
{
"name": "Gradient",
"description": "Gives a gradient of the image",
"inputs": {}
}

View File

@@ -8,14 +8,12 @@
*/
module.exports = function ImportImageModule(options, UI) {
options = options || {};
options.imageUrl = options.url || "./images/monarch.png";
var output,
imgObj = new Image();
// Tell the UI that a step has been added
UI.onSetup(options.step); // we should get UI to return the image thumbnail so we can attach our own UI extensions
// we should get UI to return the image thumbnail so we can attach our own UI extensions
// add our custom in-module html ui:
if (options.step.inBrowser) {
@@ -26,8 +24,6 @@ module.exports = function ImportImageModule(options, UI) {
// This function is caled everytime the step has to be redrawn
function draw(input,callback) {
// Tell the UI that the step has been triggered
UI.onDraw(options.step);
var step = this;
step.metadata = step.metadata || {};
@@ -43,12 +39,6 @@ module.exports = function ImportImageModule(options, UI) {
format: options.format
}
// This output is accessible to the UI
options.step.output = imgObj.src;
// Tell the UI that the step has been drawn
UI.onComplete(options.step);
// Tell Image Sequencer that step has been drawn
callback();
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -1,12 +1,8 @@
/*
* Invert the image
*/
module.exports = function Invert(options, UI) {
function Invert(options, UI) {
options = options || {};
// Tell UI that a step has been set up.
UI.onSetup(options.step);
var output;
// The function which is called on every draw.
@@ -14,8 +10,6 @@ module.exports = function Invert(options, UI) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn.
UI.onDraw(options.step);
var step = this;
@@ -28,14 +22,9 @@ module.exports = function Invert(options, UI) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step has been drawn.
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {
return input.pixelManipulation({
output: output,
changePixel: changePixel,
format: input.format,
@@ -53,3 +42,10 @@ module.exports = function Invert(options, UI) {
UI: UI
}
}
var info = {
"name": "Invert",
"description": "Inverts the image.",
"inputs": {
}
}
module.exports = [Invert, info];

View File

@@ -1,23 +1,20 @@
/*
* NDVI with red filter (blue channel is infrared)
*/
module.exports = function Ndvi(options,UI) {
module.exports = function Ndvi(options, UI) {
if (options.step.inBrowser) var ui = require('./Ui.js')(options.step, UI);
options = options || {};
options.filter = options.filter || "red";
// Tell the UI that a step has been set up.
UI.onSetup(options.step);
var output;
// The function which is called on every draw.
function draw(input,callback,progressObj) {
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that a step is being drawn.
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a) {
@@ -27,25 +24,27 @@ module.exports = function Ndvi(options,UI) {
return [x, x, x, a];
}
function output(image,datauri,mimetype){
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
step.output = { src: datauri, format: mimetype };
// This output is accessible by the UI.
options.step.output = datauri;
// Tell the UI that step has been drawn succesfully.
UI.onComplete(options.step);
}
function modifiedCallback() {
if (options.step.inBrowser) {
ui.setup();
}
callback();
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
callback: modifiedCallback
});
}
@@ -54,6 +53,6 @@ module.exports = function Ndvi(options,UI) {
options: options,
draw: draw,
output: output,
UI:UI
UI: UI
}
}

31
src/modules/Ndvi/Ui.js Normal file
View File

@@ -0,0 +1,31 @@
// hide on save
module.exports = function CropModuleUi(step, ui) {
/* sets the pixel value under the mouse pointer
* on the title attribute of the image element.
*/
function setup() {
var ndviImage = $(imgEl());
ndviImage.mousemove(function ndviMousemove(e) {
var canvas = document.createElement("canvas");
canvas.width = ndviImage.width();
canvas.height = ndviImage.height();
canvas.getContext('2d').drawImage(this, 0, 0);
var offset = $(this).offset();
var xPos = e.pageX - offset.left;
var yPos = e.pageY - offset.top;
ndviImage[0].title = "NDVI: " + canvas.getContext('2d').getImageData(xPos, yPos, 1, 1).data[0];
});
}
// step.imgSelector is not defined, imgElement is:
function imgEl() {
return step.imgElement;
}
return {
setup: setup
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,9 @@
/*
* Sample Meta Module for demonstration purpose only
*/
module.exports = function NdviColormapfunction() {
this.expandSteps([{ 'name': 'ndvi', 'options': {} }, { 'name': 'colormap', options: {} }]);
return {
isMeta: true
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,6 @@
{
"name": "NDVI-Colormap",
"description": "Sequentially Applies NDVI and Colormap steps",
"inputs": {},
"length": 2
}

View File

@@ -0,0 +1,70 @@
module.exports = function Dynamic(options, UI, util) {
options.x = options.x || 0;
options.y = options.y || 0;
var output;
// This function is called on every draw.
function draw(input, callback, progressObj) {
options.offset = options.offset || -2;
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
// save the pixels of the base image
var baseStepImage = this.getStep(options.offset).image;
var baseStepOutput = this.getOutput(options.offset);
var getPixels = require('get-pixels');
getPixels(input.src, function(err, pixels) {
options.secondImagePixels = pixels;
function changePixel(r1, g1, b1, a1, x, y) {
// overlay
var p = options.secondImagePixels;
if (x >= options.x
&& x < p.shape[0]
&& y >= options.y
&& y < p.shape[1])
return [
p.get(x, y, 0),
p.get(x, y, 1),
p.get(x, y, 2),
p.get(x, y, 3)
];
else
return [r1, g1, b1, a1];
}
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
}
// run PixelManipulation on first Image pixels
return require('../_nomodule/PixelManipulation.js')(baseStepOutput, {
output: output,
changePixel: changePixel,
format: baseStepOutput.format,
image: baseStepImage,
inBrowser: options.inBrowser,
callback: callback
});
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -0,0 +1,21 @@
{
"name": "Overlay",
"description": "Overlays an Image over another at a given position(x,y)",
"inputs": {
"x": {
"type": "integer",
"desc": "X-position of the image on which the new image is overlayed",
"default": 0
},
"y": {
"type": "integer",
"desc": "Y-position of the image on which the new image is overlayed",
"default": 0
},
"offset": {
"type": "integer",
"desc": "offset to the output of the step on which the output of the last step is overlayed",
"default": -2
}
}
}

View File

@@ -3,10 +3,6 @@
*/
module.exports = function Saturation(options,UI) {
options = options || {};
// Tell UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback,progressObj) {
@@ -14,8 +10,6 @@ module.exports = function Saturation(options,UI) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn
UI.onDraw(options.step);
var step = this;
function changePixel(r, g, b, a) {
@@ -39,11 +33,6 @@ module.exports = function Saturation(options,UI) {
// This output is accesible by Image Sequencer
step.output = {src:datauri,format:mimetype};
// This output is accessible by UI
options.step.output = datauri;
// Tell UI that step ahs been drawn
UI.onComplete(options.step);
}
return require('../_nomodule/PixelManipulation.js')(input, {

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
]

View File

@@ -3,6 +3,14 @@
* accepting a changePixel() method to remix a pixel's channels
*/
module.exports = function PixelManipulation(image, options) {
// To handle the case where pixelmanipulation is called on the input object itself
// like input.pixelManipulation(options)
if(arguments.length <= 1){
options = image;
image = this;
}
options = options || {};
options.changePixel =
options.changePixel ||

7
src/ui/prepareDynamic.js Normal file
View File

@@ -0,0 +1,7 @@
var sequencer = [];
var module = {
exports: {},
set exports(val){
sequencer.push(val);
}
}

View File

@@ -7,9 +7,9 @@ module.exports = function GetFormat(src) {
var format = undefined; // haha default
// EXAMPLE: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
// EXAMPLE: "http://example.com/example.png"
// EXAMPLE: "/example.png"
// EXAMPLE: "http://example.com/example.png"
// EXAMPLE: "/example.png"
if (isDataUrl(src)) {
format = src.split(';')[0].split('/').pop();
} else {
@@ -24,6 +24,17 @@ module.exports = function GetFormat(src) {
if (format === "jpeg") format = "jpg";
return format;
function validateFormat(data){
let supportedFormats = [
'jpg',
'jpeg',
'png',
'gif',
'canvas',
];
return supportedFormats.includes(data);
}
return validateFormat(format)?format:'jpg';
}

View File

@@ -1,18 +1,49 @@
module.exports = {
getPreviousStep : function () {
getPreviousStep: function() {
return this.getStep(-1);
},
getNextStep : function() {
getNextStep: function() {
return this.getStep(1);
},
getInput : function(offset){
if(offset + this.getIndex() === 0) offset++;
getInput: function(offset) {
if (offset + this.getIndex() === 0) offset++;
return this.getStep(offset - 1).output;
},
getOuput : function(offset){
getOutput: function(offset) {
return this.getStep(offset).output;
},
getOptions: function() {
return this.getStep(0).options;
},
setOptions: function(optionsObj) {
let options = this.getStep(0).options;
for (let key in optionsObj) {
if (options[key]) options[key] = optionsObj[key];
}
},
getFormat: function() {
return this.getStep(-1).output.format;
},
getHeight: function(callback) {
let img = new Image();
img.onload = function() {
callback(img.height);
};
img.src = this.getInput(0).src;
},
getWidth: function(callback) {
let img = new Image();
img.onload = function() {
callback(img.width);
};
img.src = this.getInput(0).src;
}
}

View File

@@ -0,0 +1,19 @@
var test = require('tape');
require('../../src/ImageSequencer.js');
var sequencer = ImageSequencer({ ui: false });
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
test('Dynamically add a test Module', function(t) {
sequencer.loadNewModule('test', { func: require('./testModule/Module'), info: require('./testModule/info') });
sequencer.loadImages('image1', red);
t.deepEqual(sequencer.modules['test'], [
require('./testModule/Module'),
require('./testModule/info')
], "test module was succesfully imported");
sequencer.addSteps('invert');
sequencer.addSteps('test');
sequencer.addSteps('invert');
sequencer.run();
t.end();
});

View File

@@ -17,64 +17,64 @@ var test_png = require('./images/test.png.js');
var test_gif = require('./images/test.gif.js');
sequencer.loadImages(test_png);
sequencer.addSteps(['invert','invert']);
sequencer.addSteps(['invert', 'invert']);
test("Preload", function(t) {
sequencer.run({mode:'test'},function(){
sequencer.run({ mode: 'test' }, function() {
t.end();
});
});
test("Inverted image isn't identical", function (t) {
test("Inverted image isn't identical", function(t) {
var step1 = sequencer.images.image1.steps[0].output.src;
var step2 = sequencer.images.image1.steps[1].output.src;
step1 = DataURItoBuffer(step1);
step2 = DataURItoBuffer(step2);
looksSame(step1,step2,function(err,res){
if(err) console.log(err);
t.equal(res,false);
looksSame(step1, step2, function(err, res) {
if (err) console.log(err);
t.equal(res, false);
t.end();
});
});
test("Twice inverted image is identical to original image", function (t) {
test("Twice inverted image is identical to original image", function(t) {
var step1 = sequencer.images.image1.steps[0].output.src;
var step3 = sequencer.images.image1.steps[2].output.src;
step1 = DataURItoBuffer(step1);
step3 = DataURItoBuffer(step3);
looksSame(step1,step3,function(err,res){
if(err) console.log(err);
t.equal(res,true);
looksSame(step1, step3, function(err, res) {
if (err) console.log(err);
t.equal(res, true);
t.end();
});
});
test("Decode QR module works properly :: setup", function (t) {
sequencer.loadImage(qr,function(){
this.addSteps('decode-qr').run({mode:'test'},function(){
test("Decode QR module works properly :: setup", function(t) {
sequencer.loadImage(qr, function() {
this.addSteps('decode-qr').run({ mode: 'test' }, function() {
t.end();
});
})
});
test("Decode QR module works properly :: teardown", function (t) {
t.equal("http://github.com/publiclab/image-sequencer",sequencer.images.image2.steps[1].output.data);
test("Decode QR module works properly :: teardown", function(t) {
t.equal("http://github.com/publiclab/image-sequencer", sequencer.images.image2.steps[1].output.data);
t.end();
});
test("PixelManipulation works for PNG images", function (t) {
sequencer.loadImages(test_png,function(){
this.addSteps('invert').run({mode:'test'},function(out){
t.equal(1,1)
test("PixelManipulation works for PNG images", function(t) {
sequencer.loadImages(test_png, function() {
this.addSteps('invert').run({ mode: 'test' }, function(out) {
t.equal(1, 1)
t.end();
});
});
});
test("PixelManipulation works for GIF images", function (t) {
sequencer.loadImages(test_gif,function(){
this.addSteps('invert').run({mode:'test'},function(out){
t.equal(1,1)
test("PixelManipulation works for GIF images", function(t) {
sequencer.loadImages(test_gif, function() {
this.addSteps('invert').run({ mode: 'test' }, function(out) {
t.equal(1, 1)
t.end();
});
});

View File

@@ -13,12 +13,12 @@ function copy(g, a) {
var b = {};
var i = 0;
for (var v in a)
if (g) {
if (v != "sequencer") b[v] = a[v];
}
else {
if (v != "sequencer" && v != "global1" && v != "global2" && !global1.hasOwnProperty(v)) i++;
}
if (g) {
if (v != "sequencer") b[v] = a[v];
}
else {
if (v != "sequencer" && v != "global1" && v != "global2" && !global1.hasOwnProperty(v)) i++;
}
if (g) return b;
else return i;
}
@@ -28,19 +28,19 @@ var global1 = copy(true, parent);
var sequencer = ImageSequencer({ ui: false });
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
test('Image Sequencer has tests', function (t) {
test('Image Sequencer has tests', function(t) {
t.equal(true, true, "Initial Test");
t.end();
});
test('loadImages loads a DataURL image and creates a step.', function (t) {
test('loadImages loads a DataURL image and creates a step.', function(t) {
sequencer.loadImages('test', red);
t.equal(sequencer.images.test.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.test.steps[0].output.src), "string", "Initial output exists");
t.end();
});
test('modulesInfo() returns info for each module', function (t) {
test('modulesInfo() returns info for each module', function(t) {
var info = sequencer.modulesInfo();
t.equal(Object.keys(info).length, Object.keys(sequencer.modules).length);
t.equal(info.hasOwnProperty(Object.keys(sequencer.modules)[0]), true);
@@ -50,38 +50,38 @@ test('modulesInfo() returns info for each module', function (t) {
});
if (!sequencer.options.inBrowser)
test('loadImage loads an image from URL and creates a step. (NodeJS)', function (t) {
require('dns').resolve('www.github.com', function (err) {
if (err) {
console.log("Test aborted due to no internet");
t.end();
}
else {
sequencer.loadImage('URL', 'https://ccpandhare.github.io/image-sequencer/examples/images/red.jpg', function () {
t.equal(sequencer.images.URL.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.URL.steps[0].output.src), "string", "Initial output exists");
t.end();
test('loadImage loads an image from URL and creates a step. (NodeJS)', function(t) {
require('dns').resolve('www.github.com', function(err) {
if (err) {
console.log("Test aborted due to no internet");
t.end();
}
else {
sequencer.loadImage('URL', 'https://ccpandhare.github.io/image-sequencer/examples/images/red.jpg', function() {
t.equal(sequencer.images.URL.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.URL.steps[0].output.src), "string", "Initial output exists");
t.end();
});
}
});
}
});
});
});
if (!sequencer.options.inBrowser)
test('loadImages loads an image from PATH and creates a step. (NodeJS)', function (t) {
sequencer.loadImages('examples/images/red.jpg');
t.equal(sequencer.images.image1.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.image1.steps[0].output.src), "string", "Initial output exists");
t.end();
});
test('loadImages loads an image from PATH and creates a step. (NodeJS)', function(t) {
sequencer.loadImages('examples/images/red.jpg');
t.equal(sequencer.images.image1.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.image1.steps[0].output.src), "string", "Initial output exists");
t.end();
});
test('loadImage works too.', function (t) {
test('loadImage works too.', function(t) {
sequencer.loadImage('test2', red);
t.equal(sequencer.images.test2.steps.length, 1, "Initial Step Created");
t.equal(typeof (sequencer.images.test2.steps[0].output.src), "string", "Initial output exists");
t.end();
});
test('addSteps("image","name") adds a step', function (t) {
test('addSteps("image","name") adds a step', function(t) {
sequencer.addSteps('test', 'channel');
t.equal(sequencer.images.test.steps.length, 2, "Length of steps increased")
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Added");
@@ -89,14 +89,14 @@ test('addSteps("image","name") adds a step', function (t) {
t.end();
});
test('addSteps("name") adds a step', function (t) {
test('addSteps("name") adds a step', function(t) {
sequencer.addSteps('channel');
t.equal(sequencer.images.test.steps.length, 3, "Length of steps increased");
t.equal(sequencer.images.test.steps[2].options.name, "channel", "Correct Step Added");
t.end();
});
test('addSteps(["name"]) adds a step', function (t) {
test('addSteps(["name"]) adds a step', function(t) {
sequencer.addSteps(['channel', 'invert']);
t.equal(sequencer.images.test.steps.length, 5, "Length of steps increased by two")
t.equal(sequencer.images.test.steps[3].options.name, "channel", "Correct Step Added");
@@ -104,60 +104,60 @@ test('addSteps(["name"]) adds a step', function (t) {
t.end();
});
test('addSteps("name",o) adds a step', function (t) {
test('addSteps("name",o) adds a step', function(t) {
sequencer.addSteps('channel', {});
t.equal(sequencer.images.test.steps.length, 6, "Length of steps increased");
t.equal(sequencer.images.test.steps[5].options.name, "channel", "Correct Step Added");
t.end();
});
test('addSteps("image","name",o) adds a step', function (t) {
test('addSteps("image","name",o) adds a step', function(t) {
sequencer.addSteps('test', 'channel', {});
t.equal(sequencer.images.test.steps.length, 7, "Length of steps increased");
t.equal(sequencer.images.test.steps[6].options.name, "channel", "Correct Step Added");
t.end();
});
test('removeSteps("image",position) removes a step', function (t) {
test('removeSteps("image",position) removes a step', function(t) {
sequencer.removeSteps('test', 1);
t.equal(sequencer.images.test.steps.length, 6, "Length of steps reduced");
t.end();
});
test('removeSteps({image: [positions]}) removes steps', function (t) {
test('removeSteps({image: [positions]}) removes steps', function(t) {
sequencer.removeSteps('test', [1, 2]);
t.equal(sequencer.images.test.steps.length, 4, "Length of steps reduced");
t.end();
});
test('removeSteps(position) removes steps', function (t) {
test('removeSteps(position) removes steps', function(t) {
sequencer.removeSteps([1, 2]);
t.equal(sequencer.images.test.steps.length, 2, "Length of steps reduced");
t.end();
});
test('insertSteps("image",position,"module",options) inserts a step', function (t) {
test('insertSteps("image",position,"module",options) inserts a step', function(t) {
sequencer.insertSteps('test', 1, 'channel', {});
t.equal(sequencer.images.test.steps.length, 3, "Length of Steps increased");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
t.end();
});
test('insertSteps("image",position,"module") inserts a step', function (t) {
test('insertSteps("image",position,"module") inserts a step', function(t) {
sequencer.insertSteps('test', 1, 'channel');
t.equal(sequencer.images.test.steps.length, 4, "Length of Steps increased");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
t.end();
});
test('insertSteps(position,"module") inserts a step', function (t) {
test('insertSteps(position,"module") inserts a step', function(t) {
sequencer.insertSteps(1, 'channel');
t.equal(sequencer.images.test.steps.length, 5, "Length of Steps increased");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
t.end();
});
test('insertSteps({image: {index: index, name: "module", o: options} }) inserts a step', function (t) {
test('insertSteps({image: {index: index, name: "module", o: options} }) inserts a step', function(t) {
sequencer.insertSteps({ test: { index: 1, name: 'channel', o: {} } });
t.equal(sequencer.images.test.steps.length, 6, "Length of Steps increased");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
@@ -165,8 +165,8 @@ test('insertSteps({image: {index: index, name: "module", o: options} }) inserts
});
test('run() runs the sequencer and returns output to callback', function (t) {
sequencer.run({mode:'test'}, function (out) {
test('run() runs the sequencer and returns output to callback', function(t) {
sequencer.run({ mode: 'test' }, function(out) {
t.equal(typeof (sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output), "object", "Output is Generated");
t.equal(out, sequencer.images.test.steps[sequencer.images.test.steps.length - 1].output.src, "Output callback works");
t.end();
@@ -174,23 +174,28 @@ test('run() runs the sequencer and returns output to callback', function (t) {
});
test('getStep(offset) returns the step at offset distance relative to current step',function(t){
sequencer.addSteps('test','invert',{});
sequencer.addSteps('test','blend',{});
sequencer.run({mode:'test'},function(out){
t.equal(!!out,true,"Blend generates output");
test('getStep(offset) returns the step at offset distance relative to current step', function(t) {
sequencer.addSteps('test', 'invert', {});
sequencer.addSteps('test', 'blend', {});
sequencer.run({ mode: 'test' }, function(out) {
t.equal(!!out, true, "Blend generates output");
t.end();
});
});
test('replaceImage returns false in NodeJS', function (t) {
test('toCliString() returns the CLI command for the sequence', function(t) {
t.deepEqual(sequencer.toCliString(), `sequencer -i [PATH] -s "channel channel channel channel channel invert blend" -d '{"channel":"green"}'`, "works correctly");
t.end();
});
test('replaceImage returns false in NodeJS', function(t) {
var returnvalue = (sequencer.options.inBrowser) ? false : sequencer.replaceImage("#selector", "test");
t.equal(returnvalue, false, "It does.");
t.end();
});
var global2 = copy(false, parent);
test('No Global Variables Created', function (t) {
test('No Global Variables Created', function(t) {
t.equal(0, global2, "None Created.");
t.end();
});

View File

@@ -4,37 +4,37 @@ require('../../src/ImageSequencer.js');
var sequencer = ImageSequencer({ ui: false });
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
test('toString() and stepToString() return the step/steps in string format', function(t){
test('toString() and stepToString() return the step/steps in string format', function(t) {
sequencer.loadImages('image1', red);
sequencer.addSteps('channel');
sequencer.addSteps('invert');
t.equal(sequencer.toString(),"channel(channel:green),invert()","toString works");
t.equal(sequencer.stepToString(sequencer.steps[1]),"channel(channel:green)","stepToString works");
t.equal(sequencer.toString(), "channel{channel:green},invert{}", "toString works");
t.equal(sequencer.stepToString(sequencer.steps[1]), "channel{channel:green}", "stepToString works");
t.end();
});
test('stringToJSON() and stringToJSONstep() return the step/steps from a string', function(t){
t.deepEqual(sequencer.stringToJSON('channel(channel:green),invert(),crop(x:0|y:0|w:50%25|h:50%25),blend(blend:function(r1%2C%20g1%2C%20b1%2C%20a1%2C%20r2%2C%20g2%2C%20b2%2C%20a2)%20%7B%20return%20%5B%20r1%2C%20g2%2C%20b2%2C%20a2%20%5D%20%7D)'),[
test('stringToJSON() and stringToJSONstep() return the step/steps from a string', function(t) {
t.deepEqual(sequencer.stringToJSON('channel{channel:green},invert{},crop{x:0|y:0|w:50%25|h:50%25},blend{blend:function(r1%2C%20g1%2C%20b1%2C%20a1%2C%20r2%2C%20g2%2C%20b2%2C%20a2)%20%7B%20return%20%5B%20r1%2C%20g2%2C%20b2%2C%20a2%20%5D%20%7D}'), [
{ name: 'channel', options: { channel: 'green' } },
{ name: 'invert', options: {} },
{ name: 'crop', options: { x: '0', y: '0', w: '50%', h: '50%'} },
{ name: 'crop', options: { x: '0', y: '0', w: '50%', h: '50%' } },
{ name: 'blend', options: { blend: 'function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }' } }
]);
t.deepEqual(sequencer.stringToJSONstep("channel(channel:green)"),{ name: 'channel', options: { channel: 'green' } });
t.deepEqual(sequencer.stringToJSONstep("channel{channel:green}"), { name: 'channel', options: { channel: 'green' } });
t.end();
});
test('stringToJSON() and stringToJSONstep() return the step/steps from a string without configs, using defaults', function(t){
t.deepEqual(sequencer.stringToJSON('channel,blur,invert,blend'),[
{ name: 'channel', options: { } },
{ name: 'blur', options: { } },
{ name: 'invert', options: { } },
{ name: 'blend', options: { } }
test('stringToJSON() and stringToJSONstep() return the step/steps from a string without configs, using defaults', function(t) {
t.deepEqual(sequencer.stringToJSON('channel,blur,invert,blend'), [
{ name: 'channel', options: {} },
{ name: 'blur', options: {} },
{ name: 'invert', options: {} },
{ name: 'blend', options: {} }
]);
t.end();
});
test('toJSON() returns the right sequence of steps',function(t){
test('toJSON() returns the right sequence of steps', function(t) {
t.deepEqual(sequencer.toJSON(), [
{ name: 'channel', options: { channel: 'green' } },
{ name: 'invert', options: {} }
@@ -42,17 +42,17 @@ test('toJSON() returns the right sequence of steps',function(t){
t.end();
});
test('importString() imports a string of steps into the sequencer', function(t){
sequencer.importString('brightness(brightness:50),invert');
t.equal(sequencer.toString(),"channel(channel:green),invert(),brightness(brightness:50),invert()");
test('importString() imports a string of steps into the sequencer', function(t) {
sequencer.importString('brightness{brightness:50},invert');
t.equal(sequencer.toString(), "channel{channel:green},invert{},brightness{brightness:50},invert{}");
t.end();
});
test('importJSON() imports a JSON array of steps into the sequencer', function(t){
test('importJSON() imports a JSON array of steps into the sequencer', function(t) {
sequencer.importJSON([
{ name: 'blur',options: {}}
{ name: 'blur', options: {} }
]);
t.equal(sequencer.toString(),"channel(channel:green),invert(),brightness(brightness:50),invert(),blur(blur:2)")
t.equal(sequencer.toString(), "channel{channel:green},invert{},brightness{brightness:50},invert{},blur{blur:2}")
t.end();
});

View File

@@ -0,0 +1,16 @@
var test = require('tape');
require('../../src/ImageSequencer.js');
var sequencer1 = ImageSequencer({ ui: false });
var sequencer2 = ImageSequencer({ ui: false });
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
test('Load ndvi-colormap meta module', function(t) {
sequencer1.loadImages('image1', red);
sequencer2.loadImages('image1', red);
sequencer1.addSteps('ndvi-colormap');
sequencer2.addSteps(['ndvi', 'colormap']);
t.equal(sequencer1.images.image1.steps[0].options.name, sequencer2.steps[0].options.name, "First step OK");
t.equal(sequencer1.images.image1.steps[1].options.name, sequencer2.steps[1].options.name, "Second step OK");
t.end();
});

View File

@@ -0,0 +1,33 @@
module.exports = function Dynamic(options, UI, util) {
// Tests for functions that are available inside modules only
var output;
// This function is called on every draw.
function draw(input, callback) {
var step = this;
if (this.getPreviousStep().options.name != "invert") throw new Error("getPreviousStep not working");
if (this.getNextStep().options.name != "invert") throw new Error("getNextStep not working");
if (this.getInput(0) != this.getOutput(-1)) throw new Error("getInput and getOuput now working")
if (this.getFormat() != "jpeg") throw new Error("getFormat not working");
if (this.getOptions().name != "test") throw new Error("getOptions not working");
this.setOptions({ name: "test-1" });
if (this.getOptions().name != "test-1") throw new Error("setOptions not working not working");
this.getHeight(((h) => { if (h != 16) throw new Error("getHeight not working") }));
this.getWidth((w) => { if (w != 16) throw new Error("getWidth not working") });
function output(image, datauri, mimetype) {
step.output = input;
}
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "test",
"description": "Functions that are scoped inside modules are tested with this. This Module is dynamically loaded.",
"inputs": {}
}