Compare commits

..

17 Commits

Author SHA1 Message Date
Jeffrey Warren
415c62d9dc Drag to crop rebase step (#244)
* 1.1.0

* 1.2.0

* added plugin

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

* drag to crop enabled

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

* fix

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

* improvement

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

* done dragToCrop

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

* done with updated ui

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

* solved bug for multiple consecutive crops

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

* fixed and updated

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

* externalized image area select code

* major refactoring of crop drag and demo ui modules

* work on crop module and ui refactor

* completed Step drag ui refactor

* revert unbuilt to rebase
2018-05-04 09:20:03 -04:00
Jeffrey Warren
51524f747a uses jquery if available in replace (#240) 2018-05-03 18:02:25 -04:00
Jeffrey Warren
b16b45afa8 removed jquery (#239)
* removed jquery

* version bump
2018-05-03 17:33:01 -04:00
Jeffrey Warren
39418da187 Format detection fix (#238)
* better format detection

* version
2018-05-03 16:35:02 -04:00
Jeffrey Warren
8547c60873 Update PixelManipulation.js (#236) 2018-05-02 17:09:09 -04:00
Jeffrey Warren
adc21ae843 Rename colormap (#235) 2018-05-02 16:08:50 -04:00
Jeffrey Warren
2abf0fae09 Code cleanup, re-organizing and tidying (#226)
* initial cleanup

* build

* additions and build
2018-04-29 12:16:11 -04:00
Jeffrey Warren
711ae62ee3 simplified demo ui (#224)
* simplified demo ui

* show description

* smaller, labelled example image
2018-04-28 18:40:34 -04:00
Jeffrey Warren
4d75fb9640 Infos updated for new title/description format (#221)
* initial work

* working with failing test

* info modules

* compiled

* rework of CONTRIBUTING
2018-04-24 17:15:27 -04:00
Marzanna
5ac3ef008d demo images can be downloaded by clicking (#220)
* demo images can be downloaded by clicking

* modified download to extract image type from url

* refactored image output into standalone function

* updated to call fileExtension function
2018-04-20 19:56:32 -04:00
Varun Gupta
98f913b32e Material icon (#217)
* add pwa icon

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

* icon working

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-04-20 14:07:52 -04:00
Jeffrey Warren
c2756ffdbb Module info.json -based demo UI (WIP) (#219)
* initial work

* working with failing test

* now should pass test
2018-04-20 09:34:08 -04:00
Varun Gupta
d887f5eb61 Update version (#208)
* 1.1.0

* 1.2.0

* check in dist

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-04-09 16:53:40 -04:00
Varun Gupta
3b791ad58b add loading spinner fallback (#202)
* add loading spinner fallback fixes #189

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

* handle testing with progress spinners

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

* apply fixes and improvements

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

* add example

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

* bug fixes

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

* resolves #189

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

* fix

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2018-03-31 01:21:28 -04:00
Varun Gupta
fba80bb151 Dynamic fix as fun (#207)
getNeighborPixel(x, y) method for Dynamic module
2018-03-30 17:41:30 -04:00
Varun Gupta
0ceb36ffde 1.1.0 (#206) 2018-03-24 10:06:50 -04:00
Jeffrey Warren
edaa8895c7 Update info.json with NDVI description (#199)
* Update info.json

* compiled
2018-03-12 13:13:43 -04:00
56 changed files with 1592 additions and 11038 deletions

View File

@@ -12,6 +12,7 @@ Most contribution (we imagine) would be in the form of API-compatible modules, w
* [Info File](#info-file)
* [Ideas](#ideas)
****
## Contribution ideas
@@ -27,24 +28,30 @@ If you find a bug please list it here, and help us develop Image Sequencer by [o
Most contributions can happen in modules, rather than to core library code. Modules and their [corresponding info files](#info-file) are included into the library in this file: https://github.com/publiclab/image-sequencer/blob/master/src/Modules.js#L5-L7
Module names, descriptions, and parameters are set in the `info.json` file -- [see below](#info-file).
Any module must follow this basic format:
```js
module.exports = function ModuleName(options,UI) {
options = options || {};
options.title = "Title of the Module";
UI.onSetup(options.step);
var output;
function draw(input,callback) {
UI.onDraw(options.step);
var output = /*do something with the input*/ ;
UI.onDraw(options.step); // tell the UI to "draw"
this.output = output;
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);
UI.onComplete(options.step); // tell UI we are done
}
return {
@@ -56,6 +63,7 @@ module.exports = function ModuleName(options,UI) {
}
```
### options
The object `options` stores some important information. This is how you can accept
@@ -67,6 +75,19 @@ whether your module is being run on a browser.
### draw()
To add a module to Image Sequencer, it must have a `draw` method; you can wrap an existing module to add them:
* `module.draw(input, callback)`
The `draw` method should accept an `input` parameter, which will be an object of the form:
```js
input = {
src: "datauri of an image here",
format: "jpeg/png/etc"
}
```
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.
@@ -82,17 +103,17 @@ constant definitions must be done **outside** the `draw()` method's definition.
format: "<png|jpeg|gif>"
}
```
* `callback` is a function which is responsible to tell the sequencer that the
step has been "drawn".
* `callback` is a function which is responsible to tell the sequencer that the step has been "drawn".
When you have done your calculations and produced an image output, you are required
to set `this.output` to an object similar to what the input object was, call
`callback()`, and set `options.step.output` equal to the output DataURL
When you have done your calculations and produced an image output, you are required to set `this.output` to an object similar to what the input object was, call `callback()`, and set `options.step.output` equal to the output DataURL
* `progressObj` is an optional additional Object that can be passed in the format `draw(input, callback, progressObj)`, which handles the progress output; see [Progress reporting](#progress-reporting) below.
### UI Methods
The module is responsible for emitting various events for the UI to capture. There are
four events in all:
The module is responsible for emitting various events for the UI to capture.
There are four events in all:
* `UI.onSetup(options.step)` must be emitted when the module is added. So it must be emitted outside the draw method's definition as shown above.
* `UI.onDraw(options.step)` must be emitted whenever the `draw()` method is called. So it should ideally be the first line of the definition of the `draw` method.
@@ -100,26 +121,14 @@ four events in all:
is ready. An argument, that is the DataURL of the output image must be passed in.
* `UI.onRemove(options.step)` is emitted automatically and the module should not emit it.
To add a module to Image Sequencer, it must have the following method; you can wrap an existing module to add them:
### Name and description
* `module.draw()`
The `draw(input,callback)` method should accept an `input` parameter, which will be an object of the form:
```js
input = {
src: "datauri here",
format: "jpeg/png/etc"
}
```
### options.title
For display in the web-based UI, each module may also have a title `options.title`.
For display in the web-based demo UI, set the `name` and `description` fields in the `info.json` file for the module.
## Info file
All module folders must have an `info.json` file which looks like the following:
```json
{
"name": "Name of Module to be displayed",
@@ -155,6 +164,52 @@ Similarly, "Select" type inputs should have a `values` array.
Also, A module may have output values. These must be defined as shown above.
### Progress reporting
The default "loading spinner" can be optionally overriden with a custom progress object to draw progress on the CLI, following is a basic module format for the same:
```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
/* 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 */
return input;
};
this.output = output();
options.step.output = output.src;
callback();
UI.onComplete(options.step);
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}
```
The `progressObj` parameter of `draw()` is not consumed unless a custom progress bar needs to be drawn, for which this default spinner should be stopped with `progressObj.stop()` and image-sequencer is informed about the custom progress bar with `progressObj.overrideFlag = true;` following which this object can be overriden with custom progress object.
### Module example
See existing module `green-channel` for an example: https://github.com/publiclab/image-sequencer/tree/master/src/modules/GreenChannel/Module.js

View File

@@ -17,6 +17,7 @@ module.exports = function(grunt) {
files: [
'src/*.js',
'src/*/*.js',
'src/*/*/*.js',
'Gruntfile.js'
],
tasks: [ 'build:js' ]

11202
dist/image-sequencer.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,225 +1,28 @@
window.onload = function() {
sequencer = ImageSequencer();
// 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>'
for (var m in modulesInfo) {
$("#addStep select").append(
'<option value="' + m + '">' + modulesInfo[m].name + "</option>"
);
}
// Initial definitions
var steps = document.querySelector('#steps');
var parser = new DOMParser();
var reader = new FileReader();
// UI for each step:
sequencer.setUI(DefaultHtmlStepUi(sequencer));
// Set the UI in sequencer. This Will generate HTML based on
// Image Sequencer events :
// onSetup : Called every time a step is added
// onDraw : Called every time a step starts draw
// onComplete : Called every time a step finishes drawing
// onRemove : Called everytime a step is removed
// The variable 'step' stores useful data like input and
// output values, step information.
// See documetation for more details.
sequencer.setUI({
// UI for the overall demo:
var ui = DefaultHtmlSequencerUi(sequencer);
onSetup: function(step) {
if (step.options && step.options.description) step.description = step.options.description
step.ui = '\
<div class="row step">\
<div class="col-md-4 details">\
<h3>'+step.name+'</h3>\
<p><i>'+(step.description || '')+'</i></p>\
</div>\
<div class="col-md-8">\
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
<img alt="" class="img-thumbnail"/>\
</div>\
</div>\
';
var tools =
'<div class="tools btn-group">\
<button confirm="Are you sure?" class="remove btn btn-xs btn-default">\
<i class="fa fa-trash"></i>\
</button>\
</div>';
step.ui = parser.parseFromString(step.ui,'text/html');
step.ui = step.ui.querySelector('div.row');
step.imgElement = step.ui.querySelector('img');
if(sequencer.modulesInfo().hasOwnProperty(step.name)) {
var inputs = sequencer.modulesInfo(step.name).inputs;
var outputs = sequencer.modulesInfo(step.name).outputs;
var io = Object.assign(inputs, outputs);
for (var i in io) {
var isInput = inputs.hasOwnProperty(i);
var ioUI = "";
var inputDesc = (isInput)?inputs[i]:{};
if (!isInput) {
ioUI += "<span class=\"output\"></span>";
}
else if (inputDesc.type.toLowerCase() == "select") {
ioUI += "<select class=\"form-control\" name=\""+i+"\">";
for (var option in inputDesc.values) {
ioUI += "<option>"+inputDesc.values[option]+"</option>";
}
ioUI += "</select>";
}
else {
ioUI = "<input class=\"form-control\" type=\""+inputDesc.type+"\" name=\""+i+"\">";
}
var div = document.createElement('div');
div.className = "row";
div.setAttribute('name', i);
div.innerHTML = "<div class='det'>\
<label for='" + i + "'>" + i + "</label>\
"+ioUI+"\
</div>";
step.ui.querySelector('div.details').appendChild(div);
}
$(step.ui.querySelector('div.details')).append("<p><button class='btn btn-default btn-save'>Save</button></p>");
// on clicking Save in the details pane of the step
$(step.ui.querySelector('div.details .btn-save')).click(function saveOptions() {
$(step.ui.querySelector('div.details')).find('input,select').each(function(i, input) {
step.options[$(input).attr('name')] = input.value;
});
sequencer.run();
});
}
if(step.name != "load-image")
step.ui.querySelector('div.details').appendChild(
parser.parseFromString(tools,'text/html')
.querySelector('div')
);
steps.appendChild(step.ui);
},
onDraw: function(step) {
$(step.ui.querySelector('.load')).show();
$(step.ui.querySelector('img')).hide();
},
onComplete: function(step) {
$(step.ui.querySelector('.load')).hide();
$(step.ui.querySelector('img')).show();
step.imgElement.src = step.output;
if(sequencer.modulesInfo().hasOwnProperty(step.name)) {
var inputs = sequencer.modulesInfo(step.name).inputs;
var outputs = sequencer.modulesInfo(step.name).outputs;
for (var i in inputs) {
if (step.options[i] !== undefined &&
inputs[i].type.toLowerCase() === "input") step.ui.querySelector('div[name="' + i + '"] input')
.value = step.options[i];
if (step.options[i] !== undefined &&
inputs[i].type.toLowerCase() === "select") step.ui.querySelector('div[name="' + i + '"] select')
.value = step.options[i];
}
for (var i in outputs) {
if (step[i] !== undefined) step.ui.querySelector('div[name="'+i+'"] input')
.value = step[i];
}
}
},
onRemove: function(step) {
step.ui.remove();
}
});
sequencer.loadImage('images/tulips.png', function loadImageUI() {
// look up needed steps from Url Hash:
var hash = getUrlHashParameter('steps');
if (hash) {
var stepsFromHash = hash.split(',');
stepsFromHash.forEach(function eachStep(step) {
sequencer.addSteps(step);
});
sequencer.run();
}
});
// File handling
$('#addStep select').on('change', selectNewStepUI);
function selectNewStepUI() {
$('#options').html('');
var m = $('#addStep select').val();
for(var input in modulesInfo[m].inputs) {
var inputUI = "";
var inputDesc = modulesInfo[m].inputs[input];
if (inputDesc.type.toLowerCase() == "select") {
inputUI += "<select class=\"form-control\" name=\""+input+"\">";
for (var option in inputDesc.values) {
inputUI += "<option>"+inputDesc.values[option]+"</option>";
}
inputUI += "</select>";
}
else {
inputUI = "<input class=\"form-control\" type=\""+inputDesc.type+"\" name=\""+input+"\">";
}
$('#options').append(
'<div class="row">\
<div class="col-md-5 labels">\
'+input+':\
</div>\
<div class="col-md-5">\
'+inputUI+'\
</div>\
</div>'
);
}
}
function addStepUI() {
var options = {};
var inputs = $('#options input, #options select');
$.each(inputs, function() {
options[this.name] = $(this).val();
});
if($('#addStep select').val() == "none") return;
// add to URL hash too
var hash = getUrlHashParameter('steps') || '';
if (hash != '') hash += ',';
setUrlHashParameter('steps', hash + $('#addStep select').val())
sequencer.addSteps($('#addStep select').val(),options).run();
}
$('#addStep button').on('click', addStepUI);
function removeStepUI(){
var index = $('button.remove').index(this) + 1;
sequencer.removeSteps(index).run();
// remove from URL hash too
var urlHash = getUrlHashParameter('steps').split(',');
urlHash.splice(index - 1, 1);
setUrlHashParameter("steps", urlHash.join(','));
}
$('body').on('click','button.remove', removeStepUI);
sequencer.loadImage("images/tulips.png", ui.onLoad);
$("#addStep select").on("change", ui.selectNewStepUi);
$("#addStep button").on("click", ui.addStepUi);
$('body').on('click', 'button.remove', ui.removeStepUi);
// image selection and drag/drop handling from examples/lib/imageSelection.js
setupFileHandling(sequencer);
}
};

View File

@@ -29,6 +29,7 @@
}
</style>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../dist/image-sequencer.js"></script>
</head>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 KiB

After

Width:  |  Height:  |  Size: 489 KiB

View File

@@ -3,73 +3,93 @@
<html>
<head>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="content-type" content="text/html; charset=UTF8">
<title>Image Sequencer</title>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
<script src="lib/urlHash.js" charset="utf-8"></script>
<script src="lib/imageSelection.js" charset="utf-8"></script>
<script src="demo.js" charset="utf-8"></script>
</head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="content-type" content="text/html; charset=UTF8">
<link rel="icon" sizes="192x192" href="../icons/ic_192.png">
<body>
<title>Image Sequencer</title>
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="demo.css">
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
<script src="lib/urlHash.js" charset="utf-8"></script>
<script src="lib/imageSelection.js" charset="utf-8"></script>
<script src="lib/defaultHtmlStepUi.js" charset="utf-8"></script>
<script src="lib/defaultHtmlSequencerUi.js" charset="utf-8"></script>
<script src="demo.js" charset="utf-8"></script>
<!-- for crop module: -->
<script src="../node_modules/imgareaselect/jquery.imgareaselect.dev.js"></script>
<div class="container-fluid">
</head>
<header class="text-center">
<h1>Image Sequencer</h1>
<p>
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it creates a new image at each step in a sequence.
<a href="https://publiclab.org/image-sequencer">Learn more</a>
</p>
<p>
Open Source <a href="https://github.com/publiclab/image-sequencer"><i class="fa fa-github"></i></a> by <a href="https://publiclab.org">Public Lab</a>
</p>
</header>
<div id="dropzone">
<p><i>Select or drag in an image to start!</i></p>
<center><input type="file" id="fileInput" value=""></center>
</div>
<body>
<section id="steps" class="row"></section>
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<!-- for crop module: -->
<link href="../node_modules/imgareaselect/distfiles/css/imgareaselect-default.css" rel="stylesheet">
<link rel="stylesheet" href="demo.css">
<section id="addStep">
<div class="row">
<select class="form-control">
<option value="none" disabled selected>Select a new step...</option>
</select>
</div>
<hr />
<div class="row">
<div id="options"></div>
</div>
<div class="row add">
<button class="btn btn-success btn-lg" name="add">Add Step</button>
</div>
</section>
<div class="container-fluid">
<header class="text-center">
<h1>Image Sequencer</h1>
<p>
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it
creates a new image at each step in a sequence.
<a href="https://publiclab.org/image-sequencer">Learn more</a>
</p>
<p>
Open Source
<a href="https://github.com/publiclab/image-sequencer">
<i class="fa fa-github"></i>
</a> by
<a href="https://publiclab.org">Public Lab</a>
</p>
</header>
<div id="dropzone">
<p>
<i>Select or drag in an image to start!</i>
</p>
<center>
<input type="file" id="fileInput" value="">
</center>
</div>
<script type="text/javascript">
<section id="steps" class="row"></section>
<hr />
<section id="addStep" class="panel panel-primary">
<div class="form-inline">
<div class="panel-body">
<div style="text-align:center;">
<select class="form-control input-lg" id="selectStep">
<option value="none" disabled selected>Select a new step...</option>
</select>
<button class="btn btn-success btn-lg" name="add">Add Step</button>
</div>
<br />
<p class="info" style="padding:8px;">Select a new module to add to your sequence.</p>
</div>
</div>
</section>
</div>
<script type="text/javascript">
$(function () {
var sequencer;
})
</script>
</script>
</body>
</body>
</html>

View File

@@ -0,0 +1,60 @@
function DefaultHtmlSequencerUi(_sequencer, options) {
options = options || {};
var addStepSel = options.addStepSel = options.addStepSel || "#addStep";
var removeStepSel = options.removeStepSel = options.removeStepSel || "button.remove";
var selectStepSel = options.selectStepSel = options.selectStepSel || "#selectStep";
function onLoad() {
importStepsFromUrlHash();
}
// look up needed steps from Url Hash:
function importStepsFromUrlHash() {
var hash = getUrlHashParameter("steps");
if (hash) {
var stepsFromHash = hash.split(",");
stepsFromHash.forEach(function eachStep(step) {
_sequencer.addSteps(step);
});
_sequencer.run();
}
}
function selectNewStepUi() {
var m = $(addStepSel + " select").val();
$(addStepSel + " .info").html(_sequencer.modulesInfo(m).description);
}
function removeStepUi() {
var index = $(removeStepSel).index(this) + 1;
sequencer.removeSteps(index).run();
// remove from URL hash too
var urlHash = getUrlHashParameter("steps").split(",");
urlHash.splice(index - 1, 1);
setUrlHashParameter("steps", urlHash.join(","));
}
function addStepUi() {
if ($(addStepSel + " select").val() == "none") return;
// add to URL hash too
var hash = getUrlHashParameter("steps") || "";
if (hash != "") hash += ",";
setUrlHashParameter("steps", hash + $(addStepSel + " select").val());
var newStepName = $(addStepSel + " select").val();
_sequencer
.addSteps(newStepName, options)
.run(null);
}
return {
onLoad: onLoad,
importStepsFromUrlHash: importStepsFromUrlHash,
selectNewStepUi: selectNewStepUi,
removeStepUi: removeStepUi,
addStepUi: addStepUi
}
}

View File

@@ -0,0 +1,190 @@
// Set the UI in sequencer. This Will generate HTML based on
// Image Sequencer events :
// onSetup : Called every time a step is added
// onDraw : Called every time a step starts draw
// onComplete : Called every time a step finishes drawing
// onRemove : Called everytime a step is removed
// The variable 'step' stores useful data like input and
// output values, step information.
// See documetation for more details.
function DefaultHtmlStepUi(_sequencer, options) {
options = options || {};
var stepsEl = options.stepsEl || document.querySelector("#steps");
var selectStepSel = options.selectStepSel = options.selectStepSel || "#selectStep";
function onSetup(step) {
if (step.options && step.options.description)
step.description = step.options.description;
step.ui =
'\
<div class="row step">\
<div class="col-md-4 details">\
<h3>' +
step.name +
"</h3>\
<p><i>" +
(step.description || "") +
'</i></p>\
</div>\
<div class="col-md-8">\
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
<a><img alt="" style="max-width=100%" class="img-thumbnail" /></a>\
</div>\
</div>\
';
var tools =
'<div class="tools btn-group">\
<button confirm="Are you sure?" class="remove btn btn btn-default">\
<i class="fa fa-trash"></i>\
</button>\
</div>';
var parser = new DOMParser();
step.ui = parser.parseFromString(step.ui, "text/html");
step.ui = step.ui.querySelector("div.row");
step.linkElement = step.ui.querySelector("a");
step.imgElement = step.ui.querySelector("a img");
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
var inputs = _sequencer.modulesInfo(step.name).inputs;
var outputs = _sequencer.modulesInfo(step.name).outputs;
var merged = Object.assign(inputs, outputs); // combine outputs w inputs
for (var paramName in merged) {
var isInput = inputs.hasOwnProperty(paramName);
var html = "";
var inputDesc = isInput ? inputs[paramName] : {};
if (!isInput) {
html += '<span class="output"></span>';
} else if (inputDesc.type.toLowerCase() == "select") {
html += '<select class="form-control" name="' + paramName + '">';
for (var option in inputDesc.values) {
html += "<option>" + inputDesc.values[option] + "</option>";
}
html += "</select>";
} else {
html =
'<input class="form-control" type="' +
inputDesc.type +
'" name="' +
paramName +
'">';
}
var div = document.createElement("div");
div.className = "row";
div.setAttribute("name", paramName);
var description = inputs[paramName].desc || paramName;
div.innerHTML =
"<div class='det'>\
<label for='" +
paramName +
"'>" +
description +
"</label>\
" +
html +
"\
</div>";
step.ui.querySelector("div.details").appendChild(div);
}
$(step.ui.querySelector("div.details")).append(
"<p><button class='btn btn-default btn-save'>Save</button></p>"
);
function saveOptions() {
$(step.ui.querySelector("div.details"))
.find("input,select")
.each(function(i, input) {
step.options[$(input).attr("name")] = input.value;
});
_sequencer.run();
}
saveOptions();
// on clicking Save in the details pane of the step
$(step.ui.querySelector("div.details .btn-save")).click(
function saveOptions() {
$(step.ui.querySelector("div.details"))
.find("input,select")
.each(function(i, input) {
step.options[$(input).attr("name")] = input.value;
});
_sequencer.run();
}
);
}
if (step.name != "load-image")
step.ui
.querySelector("div.details")
.appendChild(
parser.parseFromString(tools, "text/html").querySelector("div")
);
stepsEl.appendChild(step.ui);
}
function onDraw(step) {
$(step.ui.querySelector(".load")).show();
$(step.ui.querySelector("img")).hide();
}
function onComplete(step) {
$(step.ui.querySelector(".load")).hide();
$(step.ui.querySelector("img")).show();
step.imgElement.src = step.output;
step.linkElement.href = step.output;
function fileExtension(output) {
return output.split("/")[1].split(";")[0];
}
step.linkElement.download = step.name + "." + fileExtension(step.output);
step.linkElement.target = "_blank";
// fill inputs with stored step options
if (_sequencer.modulesInfo().hasOwnProperty(step.name)) {
var inputs = _sequencer.modulesInfo(step.name).inputs;
var outputs = _sequencer.modulesInfo(step.name).outputs;
for (var i in inputs) {
if (
step.options[i] !== undefined &&
inputs[i].type.toLowerCase() === "input"
)
step.ui.querySelector('div[name="' + i + '"] input').value =
step.options[i];
if (
step.options[i] !== undefined &&
inputs[i].type.toLowerCase() === "select"
)
step.ui.querySelector('div[name="' + i + '"] select').value =
step.options[i];
}
for (var i in outputs) {
if (step[i] !== undefined)
step.ui.querySelector('div[name="' + i + '"] input').value =
step[i];
}
}
}
function onRemove(step) {
step.ui.remove();
}
function getPreview() {
return step.imgElement;
}
return {
getPreview: getPreview,
onSetup: onSetup,
onComplete: onComplete,
onRemove: onRemove,
onDraw: onDraw
}
}

BIN
icons/ic_144.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
icons/ic_192.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
icons/ic_96.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -2,6 +2,7 @@
require('./src/ImageSequencer');
sequencer = ImageSequencer({ui: false});
var Spinner = require('ora')
var program = require('commander');
var readlineSync = require('readline-sync');
@@ -109,12 +110,19 @@ sequencer.loadImages(program.image,function(){
sequencer.addSteps(step, options);
});
var spinnerObj = Spinner('Your Image is being processed..').start();
// Run the sequencer.
sequencer.run(function(){
sequencer.run(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!!`)
}
});
@@ -143,15 +151,15 @@ 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;
}
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;
}

View File

@@ -1,6 +1,6 @@
{
"name": "image-sequencer",
"version": "1.0.0",
"version": "1.3.3",
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
"main": "src/ImageSequencer.js",
"scripts": {
@@ -32,10 +32,12 @@
"jsqr": "^0.2.2",
"lodash": "^4.17.5",
"ndarray-gaussian-filter": "^1.0.0",
"ora": "^2.0.0",
"pace": "0.0.4",
"readline-sync": "^1.4.7",
"save-pixels": "~2.3.4",
"urify": "^2.1.0"
"urify": "^2.1.0",
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2"
},
"devDependencies": {
"browserify": "13.0.0",

View File

@@ -1,25 +1,30 @@
function AddStep(ref, image, name, o) {
// add steps to the sequencer
function AddStep(_sequencer, image, name, o) {
function addStep(image, name, o_) {
var o = ref.copy(o_);
o.number = ref.options.sequencerCounter++; //Gives a Unique ID to each step
o.name = o_.name || name;
var moduleInfo = _sequencer.modules[name][1];
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 || ref.options.selector;
o.container = o_.container || _sequencer.options.selector;
o.image = image;
o.inBrowser = ref.options.inBrowser;
o.inBrowser = _sequencer.options.inBrowser;
o.step = {
name: o.name,
description: o.description,
ID: o.number,
imageName: o.image,
inBrowser: ref.options.inBrowser,
ui: ref.options.ui,
inBrowser: _sequencer.options.inBrowser,
ui: _sequencer.options.ui,
options: o
};
var UI = ref.events;
var module = ref.modules[name][0](o,UI);
ref.images[image].steps.push(module);
var UI = _sequencer.events;
var module = _sequencer.modules[name][0](o,UI);
_sequencer.images[image].steps.push(module);
return true;
}

View File

@@ -1,24 +1,23 @@
if (typeof window !== 'undefined') {window.$ = window.jQuery = require('jquery'); isBrowser = true}
if (typeof window !== 'undefined') {isBrowser = true}
else {var isBrowser = false}
ImageSequencer = function ImageSequencer(options) {
options = options || {};
options.inBrowser = options.inBrowser || isBrowser;
// if (options.inBrowser) options.ui = options.ui || require('./UserInterface');
options.sequencerCounter = 0;
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 copy(a) {
if (!typeof(a) == "object") return a;
if (objTypeOf(a) == "Array") return a.slice();
@@ -31,40 +30,40 @@ ImageSequencer = function ImageSequencer(options) {
}
return a;
}
function makeArray(input) {
return (objTypeOf(input)=="Array")?input:[input];
}
var image,
steps = [],
modules = require('./Modules'),
formatInput = require('./FormatInput'),
images = {},
inputlog = [],
events = require('./UserInterface')(),
fs = require('fs');
steps = [],
modules = require('./Modules'),
formatInput = require('./FormatInput'),
images = {},
inputlog = [],
events = require('./ui/UserInterface')(),
fs = require('fs');
// 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];
var json_q = {};
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)});
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) {
//remove the step from images[image].steps and redraw remaining images
if(index>0) {
@@ -74,73 +73,78 @@ ImageSequencer = function ImageSequencer(options) {
}
//tell the UI a step has been removed
}
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 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];
for (var i in indices)
removeStep(img,indices[i]);
removeStep(img,indices[i]);
}
// this.run(run); // This is creating problems
return this;
}
function insertSteps(image, index, name, o) {
var run = {};
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)});
for (var img in json_q) {
var details = json_q[img];
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);
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;
}
function run(spinnerObj,t_image,t_from) {
let progressObj;
if(arguments[0] != 'test'){
progressObj = spinnerObj
delete arguments['0']
}
function run(t_image,t_from) {
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() {};
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");
require('./Run')(this_, json_q, callback);
require('./Run')(this_, json_q, callback,progressObj);
return true;
}
function loadImages() {
var args = [];
var sequencer = this;
for (var arg in arguments) args.push(copy(arguments[arg]));
var json_q = formatInput.call(this,args,"l");
inputlog.push({method:"loadImages", json_q:copy(json_q)});
var loadedimages = this.copy(json_q.loadedimages);
// require('./LoadImage')(this,i,json_q.images[i]);
var ret = {
name: "ImageSequencer Wrapper",
sequencer: this,
@@ -152,46 +156,46 @@ ImageSequencer = function ImageSequencer(options) {
setUI: this.setUI,
images: loadedimages
};
function load(i) {
if(i==loadedimages.length) {
json_q.callback.call(ret);
return;
}
var img = loadedimages[i];
require('./LoadImage')(sequencer,img,json_q.images[img],function(){
require('./ui/LoadImage')(sequencer,img,json_q.images[img],function(){
load(++i);
});
}
load(0);
}
function replaceImage(selector,steps,options) {
options = options || {};
options.callback = options.callback || function() {};
return require('./ReplaceImage')(this,selector,steps,options);
}
function setUI(UI) {
this.events = require('./UserInterface')(UI);
this.events = require('./ui/UserInterface')(UI);
}
var exportBin = function(dir,basic) {
return require('./ExportBin')(dir,this,basic);
}
function modulesInfo(name) {
var modulesdata = {}
if(name == "load-image") return {};
if(arguments.length==0)
for (var modulename in modules) {
modulesdata[modulename] = modules[modulename][1];
}
for (var modulename in modules) {
modulesdata[modulename] = modules[modulename][1];
}
else modulesdata = modules[name][1];
return modulesdata;
}
return {
//literals and objects
name: "ImageSequencer",
@@ -200,7 +204,7 @@ ImageSequencer = function ImageSequencer(options) {
modules: modules,
images: images,
events: events,
//user functions
loadImages: loadImages,
loadImage: loadImages,
@@ -212,12 +216,12 @@ ImageSequencer = function ImageSequencer(options) {
setUI: setUI,
exportBin: exportBin,
modulesInfo: modulesInfo,
//other functions
log: log,
objTypeOf: objTypeOf,
copy: copy
}
}
module.exports = ImageSequencer;

View File

@@ -1,3 +1,4 @@
// insert one or more steps at a given index in the sequencer
function InsertStep(ref, image, index, name, o) {
function insertStep(image, index, name, o_) {

View File

@@ -2,8 +2,8 @@
* Core modules and their info files
*/
module.exports = {
'green-channel': [
require('./modules/GreenChannel/Module'),require('./modules/GreenChannel/info')
'channel': [
require('./modules/Channel/Module'),require('./modules/Channel/info')
],
'brightness': [
require('./modules/Brightness/Module'),require('./modules/Brightness/info')
@@ -11,8 +11,8 @@ module.exports = {
'edge-detect':[
require('./modules/EdgeDetect/Module'),require('./modules/EdgeDetect/info')
],
'ndvi-red': [
require('./modules/NdviRed/Module'),require('./modules/NdviRed/info')
'ndvi': [
require('./modules/Ndvi/Module'),require('./modules/Ndvi/info')
],
'invert': [
require('./modules/Invert/Module'),require('./modules/Invert/info')
@@ -20,8 +20,8 @@ module.exports = {
'crop': [
require('./modules/Crop/Module'),require('./modules/Crop/info')
],
'segmented-colormap': [
require('./modules/SegmentedColormap/Module'),require('./modules/SegmentedColormap/info')
'colormap': [
require('./modules/Colormap/Module'),require('./modules/Colormap/info')
],
'decode-qr': [
require('./modules/DecodeQr/Module'),require('./modules/DecodeQr/info')

View File

@@ -1,8 +1,11 @@
// Uses a given image as input and replaces it with the output.
// 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});
var this_ = ref;
var input = document.querySelectorAll(selector);
if (window.hasOwnProperty('$')) var input = $(selector);
else var input = document.querySelectorAll(selector);
var images = [];
for (var i = 0; i < input.length; i++) {
if (input[i] instanceof HTMLImageElement) images.push(input[i]);
@@ -10,7 +13,8 @@ function ReplaceImage(ref,selector,steps,options) {
function replaceImage (img, steps) {
var url = img.src;
var ext = url.split('.').pop();
// refactor to filetypeFromUrl()
var ext = url.split('?')[0].split('.').pop();
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET', url, true);
@@ -28,7 +32,7 @@ function ReplaceImage(ref,selector,steps,options) {
function make(url) {
tempSequencer.loadImage(url, function(){
this.addSteps(steps).run(function(out){
this.addSteps(steps).run({stop:function(){}},function(out){
img.src = out;
});
});

View File

@@ -1,4 +1,5 @@
function Run(ref, json_q, callback) {
function Run(ref, json_q, callback,progressObj) {
if(!progressObj) progressObj = {stop: function(){}}
function drawStep(drawarray, pos) {
if (pos == drawarray.length && drawarray[pos - 1] !== undefined) {
@@ -17,7 +18,7 @@ function Run(ref, json_q, callback) {
var input = ref.images[image].steps[i - 1].output;
ref.images[image].steps[i].draw(ref.copy(input), function onEachStep() {
drawStep(drawarray, ++pos);
});
},progressObj);
}
}

View File

@@ -3,15 +3,16 @@
*/
module.exports = function Blur(options,UI){
options = options || {};
options.title = "Blur";
options.description = "Blur an Image";
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){
function draw(input,callback,progressObj){
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that a step is being drawn
UI.onDraw(options.step);

View File

@@ -1,6 +1,6 @@
{
"name": "blur",
"description": "Blur an image by a given value",
"name": "Blur",
"description": "Gaussian blur an image by a given value, typically 0-5",
"inputs": {
"blur": {
"type": "integer",

View File

@@ -4,15 +4,22 @@
module.exports = function Brightness(options,UI){
options = options || {};
options.title = "Brightness";
options.description = "Changes the brightness of the image";
//Tell the UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback){
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
For eg. progressObj = new SomeProgressModule()
*/
// Tell the UI that a step is being drawn
UI.onDraw(options.step);
@@ -55,4 +62,4 @@ module.exports = function Brightness(options,UI){
output: output,
UI: UI
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "Brightness",
"description": "Change the brightness of the image by given value",
"description": "Change the brightness of the image by given percent value",
"inputs": {
"brightness": {
"type": "integer",
@@ -8,4 +8,4 @@
"default": 0
}
}
}
}

View File

@@ -1,24 +1,28 @@
/*
* Display only the green channel
* Display only one color channel
*/
module.exports = function GreenChannel(options,UI) {
module.exports = function Channel(options,UI) {
options = options || {};
options.title = "Green channel only";
options.description = "Displays only the green channel of an image";
options.channel = options.channel || "green";
// Tell UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback) {
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) {
return [0, g, 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];
}
function output(image,datauri,mimetype){

View File

@@ -0,0 +1,12 @@
{
"name": "Channel",
"description": "Displays only one color channel of an image -- default is green",
"inputs": {
"channel": {
"type": "select",
"desc": "Color channel",
"default": "green",
"values": ["red", "green", "blue"]
}
}
}

View File

@@ -11,11 +11,11 @@
* Adapted from bgamari's work in Infragram: https://github.com/p-v-o-s/infragram-js/commit/346c97576a07b71a55671d17e0153b7df74e803b
*/
module.exports = function SegmentedColormap(value, options) {
module.exports = function Colormap(value, options) {
options.colormap = options.colormap || colormaps.default;
// if a lookup table is provided as an array:
if(typeof(options.colormap) == "object")
colormapFunction = segmented_colormap(options.colormap);
colormapFunction = colormap(options.colormap);
// if a stored colormap is named with a string like "fastie":
else if(colormaps.hasOwnProperty(options.colormap))
colormapFunction = colormaps[options.colormap];
@@ -23,7 +23,7 @@ module.exports = function SegmentedColormap(value, options) {
return colormapFunction(value / 255.00);
}
function segmented_colormap(segments) {
function colormap(segments) {
return function(x) {
var i, result, x0, x1, xstart, y0, y1, _i, _j, _len, _ref, _ref1, _ref2, _ref3;
_ref = [0, 0], y0 = _ref[0], y1 = _ref[1];
@@ -52,29 +52,29 @@ function segmented_colormap(segments) {
};
var colormaps = {
greyscale: segmented_colormap([
greyscale: colormap([
[0, [0, 0, 0], [255, 255, 255] ],
[1, [255, 255, 255], [255, 255, 255] ]
]),
default: segmented_colormap([
default: colormap([
[0, [0, 0, 255], [0, 255, 0] ],
[0.25, [0, 255, 0], [255, 255, 0] ],
[0.50, [0, 255, 255], [255, 255, 0] ],
[0.75, [255, 255, 0], [255, 0, 0] ]
]),
ndvi: segmented_colormap([
ndvi: colormap([
[0, [0, 0, 255], [38, 195, 195] ],
[0.5, [0, 150, 0], [255, 255, 0] ],
[0.75, [255, 255, 0], [255, 50, 50] ]
]),
stretched: segmented_colormap([
stretched: colormap([
[0, [0, 0, 255], [0, 0, 255] ],
[0.1, [0, 0, 255], [38, 195, 195] ],
[0.5, [0, 150, 0], [255, 255, 0] ],
[0.7, [255, 255, 0], [255, 50, 50] ],
[0.9, [255, 50, 50], [255, 50, 50] ]
]),
fastie: segmented_colormap([
fastie: colormap([
[0, [255, 255, 255], [0, 0, 0] ],
[0.167, [0, 0, 0], [255, 255, 255] ],
[0.33, [255, 255, 255], [0, 0, 0] ],

View File

@@ -1,14 +1,16 @@
module.exports = function SegmentedColormap(options,UI) {
module.exports = function Colormap(options,UI) {
options = options || {};
options.title = "Segmented Colormap";
// 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) {
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell the UI that the step is being drawn
UI.onDraw(options.step);
@@ -16,7 +18,7 @@ module.exports = function SegmentedColormap(options,UI) {
function changePixel(r, g, b, a) {
var combined = (r + g + b) / 3.000;
var res = require('./SegmentedColormap')(combined, options);
var res = require('./Colormap')(combined, options);
return [res[0], res[1], res[2], 255];
}

View File

@@ -1,6 +1,6 @@
{
"name": "Segmented Colormap",
"description": "Maps brightness values (average of red, green & blue) to a given color lookup table, made up of a set of one more color gradients.",
"name": "Colormap",
"description": "Maps brightness values (average of red, green & blue) to a given color lookup table, made up of a set of one more color gradients.\n\nFor example, 'cooler' colors like blue could represent low values, while 'hot' colors like red could represent high values.",
"inputs": {
"colormap": {
"type": "select",

View File

@@ -13,47 +13,61 @@
* y = options.y
* y = options.y + options.h
*/
module.exports = function CropModule(options,UI) {
module.exports = function CropModule(options, UI) {
options = options || {};
options.title = "Crop Image";
// 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);
var output;
// 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
// add our custom in-module html ui:
var ui = require('./Ui.js')(options.step, UI);
var output,
setupComplete = false;
// This function is caled everytime the step has to be redrawn
function draw(input,callback) {
// 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;
// Tell the UI that the step has been triggered
UI.onDraw(options.step);
var step = this;
require('./Crop')(input,options,function(out,format){
// save the input image;
// TODO: this should be moved to module API to persist the input image
options.step.input = input.src;
// This output is accessible to Image Sequencer
step.output = {
src: out,
format: format
}
require('./Crop')(input, options, function(out, format){
// This output is accessible to the UI
options.step.output = out;
// This output is accessible to Image Sequencer
step.output = {
src: out,
format: format
}
// Tell the UI that the step has been drawn
UI.onComplete(options.step);
// This output is accessible to the UI
options.step.output = out;
// Tell Image Sequencer that step has been drawn
callback();
// Tell the UI that the step has been drawn
UI.onComplete(options.step);
});
// start custom UI setup (draggable UI)
// only once we have an input image
if (setupComplete === false && options.step.inBrowser) {
setupComplete = true;
ui.setup();
}
}
// Tell Image Sequencer that step has been drawn
callback();
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

68
src/modules/Crop/Ui.js Normal file
View File

@@ -0,0 +1,68 @@
module.exports = function CropModuleUi(step, ui) {
// The problem is we don't have input image dimensions at the
// time of setting up the UI; that comes when draw() is triggered...
// so we're triggering setup only on first run of draw()
function setup() {
// display original input image on initial setup
showOriginal()
let width = Math.floor(imgEl.width),
height = Math.floor(imgEl.height),
x1 = 0,
y1 = 0,
x2 = width / 2,
y2 = height / 2;
// display with 50%/50% default crop:
setOptions(
x1,
y1,
x2 - x1,
y2 - y1
)
$(imgEl()).imgAreaSelect({
handles: true,
x1: x1,
y1: y1,
x2: x2,
y2: y2,
// when selection is complete
onSelectEnd: function onSelectEnd(img, selection) {
// assign crop values to module UI form inputs:
setOptions(
selection.x1,
selection.y1,
selection.x2 - selection.x1,
selection.y2 - selection.y1
)
// then hide the draggable UI
$(imgEl()).imgAreaSelect({
hide: true
});
}
});
}
// step.imgSelector is not defined, imgElement is:
function imgEl() {
return step.imgElement;
}
function setOptions(x1,y1,x2,y2) {
let options = $($(imgEl()).parents()[2]).find("input");
options[0].value = x1;
options[1].value = y1;
options[2].value = x2 - x1;
options[3].value = y2 - y1;
}
// replaces currently displayed output thumbnail with the input image, for ui dragging purposes
function showOriginal() {
step.imgElement.src = step.input;
}
return {
setup: setup
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "Crop",
"description": "Crop image to given x, y, w, h",
"description": "Crop image to given x, y, w, h in pixels, measured from top left",
"url": "https://github.com/publiclab/image-sequencer/tree/master/MODULES.md",
"inputs": {
"x": {

View File

@@ -4,7 +4,6 @@
module.exports = function DoNothing(options,UI) {
options = options || {};
options.title = "Decode QR Code";
// Tell the UI that a step has been added
UI.onSetup(options.step);

View File

@@ -1,5 +1,6 @@
{
"name": "Decode QR",
"description": "Search for and decode a QR code in the image",
"inputs": {
},
"outputs": {

View File

@@ -1,22 +1,24 @@
module.exports = function Dynamic(options,UI) {
options = options || {};
options.title = "Dynamic";
// 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) {
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 ';
@@ -25,48 +27,65 @@ 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
this.getNeighbourPixel.fun(x,y) or this.getNeighborPixel.fun(x,y)
*/
var combined = (r + g + b) / 3.000;
return [
options.red_function( r, g, b, a),
options.red_function(r, g, b, a),
options.green_function(r, g, b, a),
options.blue_function( r, g, b, a),
options.blue_function(r, g, b, a),
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 [
pixels.get(curX+distX,curY+distY,0)
,pixels.get(curX+distX,curY+distY,1)
,pixels.get(curX+distX,curY+distY,2)
,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,
changePixel: changePixel,
getNeighbourPixel: getNeighbourPixel,
getNeighborPixel: getNeighbourPixel,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
}
return {
options: options,
draw: draw,

View File

@@ -1,5 +1,4 @@
const _ = require('lodash')
var pace = require('pace')
//define kernels for the sobel filter
const kernelx = [[-1,0,1],[-2,0,2],[-1,0,1]],
@@ -12,7 +11,6 @@ let weakEdgePixels = []
let notInUI
module.exports = exports = function(pixels,highThresholdRatio,lowThresholdRatio,inBrowser){
notInUI = !inBrowser
if(notInUI) var progressbar1 = pace((pixels.shape[0] * pixels.shape[1]))
for(var x = 0; x < pixels.shape[0]; x++) {
angles.push([])
mags.push([])
@@ -33,7 +31,6 @@ module.exports = exports = function(pixels,highThresholdRatio,lowThresholdRatio
mags.slice(-1)[0].push(pixel[3])
angles.slice(-1)[0].push(result.angle)
if(notInUI)progressbar1.op()
}
}
@@ -75,7 +72,6 @@ function changePixel(pixels,val,a,x,y){
function nonMaxSupress(pixels) {
angles = angles.map((arr)=>arr.map(convertToDegrees))
if(notInUI) var progressbar2 = pace((pixels.shape[0] * pixels.shape[1]))
for(let i = 1;i<pixels.shape[0]-1;i++){
for(let j=1;j<pixels.shape[1]-1;j++){
@@ -118,7 +114,6 @@ function nonMaxSupress(pixels) {
else
pixels.set(i,j,3,0)
if(notInUI) progressbar2.op()
}
}
return pixels
@@ -133,7 +128,6 @@ var findMaxInMatrix = arr => Math.max(...arr.map(el=>el.map(val=>!!val?val:0)).m
function doubleThreshold(pixels,highThresholdRatio,lowThresholdRatio){
const highThreshold = findMaxInMatrix(mags) * 0.2
const lowThreshold = highThreshold * lowThresholdRatio
if(notInUI) var progressbar3 = pace((pixels.shape[0] * pixels.shape[1]))
for(let i =0;i<pixels.shape[0];i++){
for(let j=0;j<pixels.shape[1];j++){
@@ -144,7 +138,6 @@ function doubleThreshold(pixels,highThresholdRatio,lowThresholdRatio){
?strongEdgePixels.push(pixelPos)
:weakEdgePixels.push(pixelPos)
:pixels.set(i,j,3,0)
if(notInUI) progressbar3.op()
}
}

View File

@@ -4,8 +4,6 @@
module.exports = function edgeDetect(options,UI) {
options = options || {};
options.title = "Detect Edges";
options.description = "Detects the edges in an image";
options.blur = options.blur || 2
options.highThresholdRatio = options.highThresholdRatio||0.2
options.lowThresholdRatio = options.lowThresholdRatio||0.15
@@ -15,8 +13,11 @@ module.exports = function edgeDetect(options,UI) {
var output;
// The function which is called on every draw.
function draw(input,callback) {
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn.
UI.onDraw(options.step);
@@ -63,4 +64,4 @@ module.exports = function edgeDetect(options,UI) {
output: output,
UI: UI
}
}
}

View File

@@ -18,4 +18,4 @@
"default": 0.15
}
}
}
}

View File

@@ -4,8 +4,6 @@
module.exports = function DoNothing(options,UI) {
options = options || {};
options.title = "Fisheye GL";
var output;
// Tell the UI that a step has been set up.

View File

@@ -1,5 +1,7 @@
{
"name": "Fisheye GL",
"description": "Correct fisheye, or barrel distortion, in images (with WebGL -- adapted from fisheye-correction-webgl by @bluemir).",
"requires": [ "webgl" ],
"inputs": {
"a": {
"type": "float",

View File

@@ -1,5 +0,0 @@
{
"name": "Green Channel",
"inputs": {
}
}

View File

@@ -4,16 +4,16 @@
module.exports = function Invert(options,UI) {
options = options || {};
options.title = "Invert Colors";
options.description = "Inverts the colors of the image";
// 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) {
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn.
UI.onDraw(options.step);

View File

@@ -1,24 +1,28 @@
/*
* NDVI with red filter (blue channel is infrared)
*/
module.exports = function NdviRed(options,UI) {
module.exports = function Ndvi(options,UI) {
options = options || {};
options.title = "NDVI for red-filtered cameras (blue is infrared)";
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) {
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) {
var ndvi = (b - r) / (1.00 * b + r);
if (options.filter == "red") var ndvi = (b - r) / (1.00 * b + r);
if (options.filter == "blue") var ndvi = (r - b) / (1.00 * b + r);
var x = 255 * (ndvi + 1) / 2;
return [x, x, x, a];
}

View File

@@ -0,0 +1,12 @@
{
"name": "NDVI",
"description": "Normalized Difference Vegetation Index, or NDVI, is an image analysis technique used with aerial photography. It's a way to visualize the amounts of infrared and other wavelengths of light reflected from vegetation by comparing ratios of blue and red light absorbed versus green and IR light reflected. NDVI is used to evaluate the health of vegetation in satellite imagery, where it correlates with how much photosynthesis is happening. This is helpful in assessing vegetative health or stress. <a href='https://publiclab.org/ndvi'>Read more</a>.<br /><br/>This is designed for use with red-filtered single camera <a href='http://publiclab.org/infragram'>DIY Infragram cameras</a>; change to 'blue' for blue filters",
"inputs": {
"filter": {
"type": "select",
"desc": "Filter color",
"default": "red",
"values": ["red", "blue"]
}
}
}

View File

@@ -1,6 +0,0 @@
{
"name": "NDVI for red filters",
"description": "Normalized Difference Vegetation Index, or NDVI, is an image analysis technique used with aerial photography. It's a way to visualize the amounts of infrared and other wavelengths of light reflected from vegetation. Because both these methods compare ratios of blue and red light absorbed versus green and IR light reflected, they can be used to evaluate the health of vegetation. It's a snapshot of how much photosynthesis is happening. This is helpful in assessing vegetative health or stress. <a href='https://publiclab.org/ndvi'>Read more</a>.<br /><br/>This is designed for use with red-filtered single camera <a href='http://publiclab.org/infragram'>DIY Infragram cameras</a>.",
"inputs": {
}
}

View File

@@ -1,17 +1,18 @@
/*
* Saturate an image
* Saturate an image with a value from 0 to 1
*/
module.exports = function Saturation(options,UI) {
options = options || {};
options.title = "Saturation";
options.description = "Saturate an image";
// Tell UI that a step has been set up
UI.onSetup(options.step);
var output;
function draw(input,callback) {
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
// Tell UI that a step is being drawn
UI.onDraw(options.step);

View File

@@ -1,10 +1,10 @@
{
"name": "Saturation",
"description": "Change the saturation of the image by given value",
"description": "Change the saturation of the image by given value, from 0-1, with 1 being 100% saturated.",
"inputs": {
"saturation": {
"type": "integer",
"desc": "saturation for the new image between 0 and 2, 0 being black and white and 2 being fully saturated",
"desc": "saturation for the new image between 0 and 2, 0 being black and white and 2 being highly saturated",
"default": 0
}
}

View File

@@ -3,7 +3,6 @@
*/
module.exports = function ImageThreshold(options) {
options = options || {};
options.title = "Threshold image";
options.threshold = options.threshold || 30;
var image;

View File

@@ -0,0 +1,6 @@
{
"name": "Threshold image",
"description": "...",
"inputs": {
}
}

View File

@@ -18,26 +18,38 @@ module.exports = function PixelManipulation(image, options) {
getPixels(image.src, function(err, pixels) {
if(err) {
console.log('Bad image path');
console.log('Bad image path', image);
return;
}
if(options.getNeighbourPixel){
options.getNeighbourPixel.fun = function (distX,distY) {
return options.getNeighbourPixel(pixels,x,y,distX,distY);
};
}
// iterate through pixels;
// this could possibly be more efficient; see
// https://github.com/p-v-o-s/infragram-js/blob/master/public/infragram.js#L173-L181
if(!options.inBrowser)
var pace = require('pace')((pixels.shape[0] * pixels.shape[1]))
if(!options.inBrowser){
try{
var pace = require('pace')((pixels.shape[0] * pixels.shape[1]));
}
catch(e){
options.inBrowser = true;
}
}
for(var x = 0; x < pixels.shape[0]; x++) {
for(var y = 0; y < pixels.shape[1]; y++) {
var pixel = options.changePixel(
pixels.get(x, y, 0),
pixels.get(x, y, 1),
pixels.get(x, y, 2),
pixels.get(x, y, 3)
);
pixels.get(x, y, 0),
pixels.get(x, y, 1),
pixels.get(x, y, 2),
pixels.get(x, y, 3)
);
pixels.set(x, y, 0, pixel[0]);
pixels.set(x, y, 1, pixel[1]);
@@ -48,9 +60,9 @@ module.exports = function PixelManipulation(image, options) {
pace.op()
}
}
if(options.extraManipulation)
pixels = options.extraManipulation(pixels)
pixels = options.extraManipulation(pixels)
// there may be a more efficient means to encode an image object,
// but node modules and their documentation are essentially arcane on this point

View File

@@ -47,7 +47,7 @@ function LoadImage(ref, name, src, main_callback) {
function loadImage(name, src) {
var step = {
name: "load-image",
description: "This initial step loads and displays the original image without any modifications.",
description: "This initial step loads and displays the original image without any modifications.<br /><br />To work with a new or different image, drag one into the drop zone.",
ID: ref.options.sequencerCounter++,
imageName: name,
inBrowser: ref.options.inBrowser,

View File

@@ -5,14 +5,12 @@
module.exports = function UserInterface(events = {}) {
events.onSetup = events.onSetup || function(step) {
if(step.ui == false) {
if (step.ui == false) {
// No UI
}
else if(step.inBrowser) {
} else if(step.inBrowser) {
// Create and append an HTML Element
console.log("Added Step \""+step.name+"\" to \""+step.imageName+"\".");
}
else {
} else {
// Create a NodeJS Object
console.log('\x1b[36m%s\x1b[0m',"Added Step \""+step.name+"\" to \""+step.imageName+"\".");
}
@@ -21,12 +19,10 @@ module.exports = function UserInterface(events = {}) {
events.onDraw = events.onDraw || function(step) {
if (step.ui == false) {
// No UI
}
else if(step.inBrowser) {
} else if(step.inBrowser) {
// Overlay a loading spinner
console.log("Drawing Step \""+step.name+"\" on \""+step.imageName+"\".");
}
else {
} else {
// Don't do anything
console.log('\x1b[33m%s\x1b[0m',"Drawing Step \""+step.name+"\" on \""+step.imageName+"\".");
}
@@ -35,27 +31,23 @@ module.exports = function UserInterface(events = {}) {
events.onComplete = events.onComplete || function(step) {
if (step.ui == false) {
// No UI
}
else if(step.inBrowser) {
} else if(step.inBrowser) {
// Update the DIV Element
// Hide the laoding spinner
console.log("Drawn Step \""+step.name+"\" on \""+step.imageName+"\".");
}
else {
} else {
// Update the NodeJS Object
console.log('\x1b[32m%s\x1b[0m',"Drawn Step \""+step.name+"\" on \""+step.imageName+"\".");
}
}
events.onRemove = events.onRemove || function(step) {
if(step.ui == false){
if (step.ui == false){
// No UI
}
else if(step.inBrowser) {
} else if(step.inBrowser) {
// Remove the DIV Element
console.log("Removing Step \""+step.name+"\" of \""+step.imageName+"\".");
}
else {
} else {
// Delete the NodeJS Object
console.log('\x1b[31m%s\x1b[0m',"Removing Step \""+step.name+"\" of \""+step.imageName+"\".");
}

View File

@@ -41,10 +41,10 @@ test('addSteps is two-way chainable.', function (t){
});
test('addSteps is two-way chainable without loadImages.', function (t){
var returnval = sequencer.addSteps("image3","ndvi-red");
var returnval = sequencer.addSteps("image3","ndvi");
t.equal(returnval.name,"ImageSequencer","Sequencer is returned");
t.equal(sequencer.images.image3.steps.length,3,"Step length increased");
t.equal(sequencer.images.image3.steps[2].options.name,"ndvi-red","Correct Step Added");
t.equal(sequencer.images.image3.steps[2].options.name,"ndvi","Correct Step Added");
t.end();
});
@@ -79,9 +79,9 @@ test('insertSteps is two-way chainable.', function (t){
});
test('insertSteps is two-way chainable without loadImages.', function (t){
var returnval = sequencer.insertSteps("image5",1,"ndvi-red");
var returnval = sequencer.insertSteps("image5",1,"ndvi");
t.equal(returnval.name,"ImageSequencer","Sequencer is returned");
t.equal(sequencer.images.image5.steps.length,3);
t.equal(sequencer.images.image5.steps[1].options.name,"ndvi-red","Correct Step Inserrted");
t.equal(sequencer.images.image5.steps[1].options.name,"ndvi","Correct Step Inserrted");
t.end();
});

View File

@@ -15,12 +15,13 @@ var sequencer = ImageSequencer({ ui: false });
var qr = require('./images/IS-QR.js');
var test_png = require('./images/test.png.js');
var test_gif = require('./images/test.gif.js');
var spinner = require('ora')('').start()
sequencer.loadImages(test_png);
sequencer.addSteps(['invert','invert']);
test("Preload", function(t) {
sequencer.run(function(){
sequencer.run(spinner,function(){
t.end();
});
});
@@ -51,7 +52,7 @@ test("Twice inverted image is identical to original image", function (t) {
test("Decode QR module works properly :: setup", function (t) {
sequencer.loadImage(qr,function(){
this.addSteps('decode-qr').run(function(){
this.addSteps('decode-qr').run(spinner.start(),function(){
t.end();
});
})
@@ -64,7 +65,7 @@ test("Decode QR module works properly :: teardown", function (t) {
test("PixelManipulation works for PNG images", function (t) {
sequencer.loadImages(test_png,function(){
this.addSteps('invert').run(function(out){
this.addSteps('invert').run(spinner.start(),function(out){
t.equal(1,1)
t.end();
});
@@ -73,9 +74,10 @@ test("PixelManipulation works for PNG images", function (t) {
test("PixelManipulation works for GIF images", function (t) {
sequencer.loadImages(test_gif,function(){
this.addSteps('invert').run(function(out){
this.addSteps('invert').run(spinner,function(out){
t.equal(1,1)
t.end();
});
});
});
spinner.stop(true)

View File

@@ -82,38 +82,39 @@ test('loadImage works too.', function (t){
});
test('addSteps("image","name") adds a step', function (t) {
sequencer.addSteps('test','green-channel');
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, "green-channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[1].options.description, "Displays only one color channel of an image -- default is green", "Step description shown");
t.end();
});
test('addSteps("name") adds a step', function (t) {
sequencer.addSteps('green-channel');
sequencer.addSteps('channel');
t.equal(sequencer.images.test.steps.length, 3, "Length of steps increased");
t.equal(sequencer.images.test.steps[2].options.name, "green-channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[2].options.name, "channel", "Correct Step Added");
t.end();
});
test('addSteps(["name"]) adds a step', function (t) {
sequencer.addSteps(['green-channel','invert']);
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, "green-channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[3].options.name, "channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[4].options.name, "invert", "Correct Step Added");
t.end();
});
test('addSteps("name",o) adds a step', function (t) {
sequencer.addSteps('green-channel',{});
sequencer.addSteps('channel',{});
t.equal(sequencer.images.test.steps.length, 6, "Length of steps increased");
t.equal(sequencer.images.test.steps[5].options.name, "green-channel", "Correct Step Added");
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) {
sequencer.addSteps('test','green-channel',{});
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, "green-channel", "Correct Step Added");
t.equal(sequencer.images.test.steps[6].options.name, "channel", "Correct Step Added");
t.end();
});
@@ -136,30 +137,30 @@ test('removeSteps(position) removes steps', function (t) {
});
test('insertSteps("image",position,"module",options) inserts a step', function (t) {
sequencer.insertSteps('test',1,'green-channel',{});
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, "green-channel", "Correct Step Inserted");
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) {
sequencer.insertSteps('test',1,'green-channel');
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, "green-channel", "Correct Step Inserted");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
t.end();
});
test('insertSteps(position,"module") inserts a step', function (t) {
sequencer.insertSteps(1,'green-channel');
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, "green-channel", "Correct Step Inserted");
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) {
sequencer.insertSteps({test: {index:1, name:'green-channel', o:{} } });
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, "green-channel", "Correct Step Inserted");
t.equal(sequencer.images.test.steps[1].options.name, "channel", "Correct Step Inserted");
t.end();
});