mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-14 04:10:04 +01:00
Merge Commit
This commit is contained in:
260
CONTRIBUTING.md
Normal file
260
CONTRIBUTING.md
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
Contributing to Image Sequencer
|
||||||
|
===
|
||||||
|
|
||||||
|
Happily accepting pull requests; to edit the core library, modify files in `./src/`. To build, run `npm install` followed by `grunt build`.
|
||||||
|
|
||||||
|
Most contribution (we imagine) would be in the form of API-compatible modules, which need not be directly included.
|
||||||
|
|
||||||
|
## Contributing modules
|
||||||
|
|
||||||
|
Any module must look like this :
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = function ModuleName(options,UI) {
|
||||||
|
options = options || {};
|
||||||
|
options.title = "Title of the Module";
|
||||||
|
UI.onSetup();
|
||||||
|
var output;
|
||||||
|
|
||||||
|
function draw(input,callback) {
|
||||||
|
UI.onDraw();
|
||||||
|
|
||||||
|
var output = /*do something with the input*/ ;
|
||||||
|
|
||||||
|
this.output = output;
|
||||||
|
callback();
|
||||||
|
UI.onComplete(this.output.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
options: options,
|
||||||
|
draw: draw,
|
||||||
|
output: output,
|
||||||
|
UI: UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### options
|
||||||
|
|
||||||
|
The object `options` stores some important information. This is how you can accept
|
||||||
|
input from users. If you require a variable "x" from the user and the user passes
|
||||||
|
it in, you will be able to access it as `options.x`.
|
||||||
|
|
||||||
|
Options also has some in-built properties. The `options.inBrowser` boolean denotes
|
||||||
|
whether your module is being run on a browser.
|
||||||
|
|
||||||
|
### draw()
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
What is not in the draw method, but is in the `module.exports` is executed only
|
||||||
|
when the step is added. So whatever external npm modules are to be loaded, or
|
||||||
|
constant definitions must be done **outside** the `draw()` method's definition.
|
||||||
|
|
||||||
|
`draw()` receives two arguments - `input` and `callback` :
|
||||||
|
* `input` is an object which is essentially the output of the previous step.
|
||||||
|
```js
|
||||||
|
input = {
|
||||||
|
src: "<$DataURL>",
|
||||||
|
format: "<png|jpeg|gif>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* `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, and call
|
||||||
|
`callback()`.
|
||||||
|
|
||||||
|
### UI Methods
|
||||||
|
|
||||||
|
The module is responsible to emit various events for the UI to capture. There are
|
||||||
|
four events in all:
|
||||||
|
|
||||||
|
* `UI.onSetup()` must be emitted when the module is added. So it must be emitted
|
||||||
|
outside the draw method's definition as shown above.
|
||||||
|
* `UI.onDraw()` must be emitted whenever the `draw()` method is called. So it should
|
||||||
|
ideally be the first line of the definition of the `draw` method.
|
||||||
|
* `UI.onComplete(output_src)` must be emitted whenever the output of a draw call
|
||||||
|
is ready. An argument, that is the DataURL of the output image must be passed in.
|
||||||
|
* `UI.onRemove()` 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:
|
||||||
|
|
||||||
|
* `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`.
|
||||||
|
|
||||||
|
### Module example
|
||||||
|
|
||||||
|
See existing module `green-channel` for an example: https://github.com/publiclab/image-sequencer/tree/master/src/modules/GreenChannel/Module.js
|
||||||
|
|
||||||
|
For help integrating, please open an issue.
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Notes on development next steps:
|
||||||
|
|
||||||
|
### UI
|
||||||
|
|
||||||
|
* [ ] add createUserInterface() which is set up by default to draw on ImageBoardUI, but could be swapped for nothing, or an equiv. lib
|
||||||
|
* [ ] it could create the interface and use event listeners like module.on('draw', fn()); to update the interface
|
||||||
|
|
||||||
|
* [ ] spinners before panels are complete
|
||||||
|
* [ ] is there a module for generating forms from parameters?
|
||||||
|
* [ ] click to expand for all images
|
||||||
|
* [ ] `ImageSequencer.Renderer` class to manage image output formats and adapters
|
||||||
|
* [ ] remove step
|
||||||
|
|
||||||
|
* [ ] output besides an image -- like `message(txt)` to display to the step's UI
|
||||||
|
|
||||||
|
|
||||||
|
### Modularization
|
||||||
|
|
||||||
|
* [ ] remotely includable modules, not compiled in -- see plugin structures in other libs
|
||||||
|
* [x] ability to start running at any point -- already works?
|
||||||
|
* [x] commandline runnability?
|
||||||
|
* [x] Make available as browserified OR `require()` includable...
|
||||||
|
* [ ] standardize panel addition with submodule that offers Panel.display(image)
|
||||||
|
* [ ] allow passing data as data-uri or Image object, or stream, or ndarray or ImageData array, if both of neighboring pair has ability?
|
||||||
|
* see https://github.com/jywarren/image-sequencer/issues/1
|
||||||
|
* [ ] ...could we directly include package.json for module descriptions? At least as a fallback.
|
||||||
|
* [ ] (for node-and-line style UIs) non-linear sequences with Y-splitters
|
||||||
|
* [ ] `sequencer.addModule('path/to/module.js')` style module addition -- also to avoid browserifying all of Plotly :-P
|
||||||
|
* [x] remove step
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
* [x] tests - modules headless; unit tests
|
||||||
|
* [ ] comparisons with diff
|
||||||
|
* [ ] testing a module's promised functionality: each module could offer before/after images as part of their API; by running the module on the before image, you should get exactly the after image, comparing with an image diff
|
||||||
|
|
||||||
|
### Use cases
|
||||||
|
|
||||||
|
* [ ] make an Infragram module that accepts a math expression
|
||||||
|
|
||||||
|
### Bugs
|
||||||
|
|
||||||
|
* [x] BUG: this doesn't work for defaults: imageboard.loadImage('examples/grid.png', function() {});
|
||||||
|
* we should make defaults a config of the first module
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## Module Candidates
|
||||||
|
|
||||||
|
* https://github.com/linuxenko/rextract.js
|
||||||
|
* https://www.npmjs.com/package/histogram
|
||||||
|
* https://github.com/hughsk/flood-fill
|
||||||
|
* https://www.npmjs.com/package/blink-diff
|
||||||
|
* smaller and faster: https://www.npmjs.com/package/@schornio/pixelmatch
|
||||||
|
* https://github.com/yahoo/pngjs-image has lots of useful general-purpose image getters like `image.getLuminosityAtIndex(idx)`
|
||||||
|
* some way to add in a new image (respecting alpha) -- `add-image` (with blend mode, default `normal`?)
|
||||||
|
* https://github.com/yuta1984/CannyJS - edge detection
|
||||||
|
* http://codepen.io/taylorcoffelt/pen/EsCcr - more edge detection
|
||||||
|
|
||||||
|
## Ideas
|
||||||
|
|
||||||
|
* https://github.com/vicapow/jsqrcode
|
||||||
|
* https://github.com/jadnco/whirl - scrubbable image sequence player
|
||||||
|
* non graphics card GL functions could be shimmed with https://github.com/Overv/JSGL
|
||||||
|
* or this: https://github.com/stackgl/headless-gl
|
||||||
|
* https://github.com/mattdesl/fontpath-simple-renderer
|
||||||
|
* output in animated Gif? as a module
|
||||||
|
|
||||||
|
### Referencing earlier states
|
||||||
|
|
||||||
|
Complex sequences with masking could require accessing previous states (or nonlinearity):
|
||||||
|
|
||||||
|
* flood-fill an area
|
||||||
|
* select only the flooded area
|
||||||
|
* roundabout: lighten everything to <50%, then flood-fill with black? Not 100% reliable.
|
||||||
|
* roundabout 2: `flood fill`, then `blink-diff` with original
|
||||||
|
* then add step which recovers original image, repeat `flood-fill`/`blink-diff` for second region
|
||||||
|
* reference above masked states in a `mask` module, with `maskModule.draw(image, { getMask: function() { return maskImg } })`
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
|
||||||
|
`pattern-fill` module to use patterns in JS canvas:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var c=document.getElementById("myCanvas");
|
||||||
|
var ctx=c.getContext("2d");
|
||||||
|
var img=document.getElementById("lamp");
|
||||||
|
var pat=ctx.createPattern(img,"repeat");
|
||||||
|
ctx.rect(0,0,150,100);
|
||||||
|
ctx.fillStyle=pat;
|
||||||
|
ctx.fill();
|
||||||
|
```
|
||||||
|
|
||||||
|
Masking:
|
||||||
|
|
||||||
|
```js
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 0);
|
||||||
|
ctx.lineTo(160, 600);
|
||||||
|
ctx.rect(0, 0, 160, 600);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
ctx.restore();
|
||||||
|
```
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## UI notes:
|
||||||
|
|
||||||
|
* visual nodes-and-lines UI: https://github.com/flowhub/the-graph
|
||||||
|
* https://flowhub.github.io/the-graph/examples/demo-simple.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
'threshold': {
|
||||||
|
type: 'slider',
|
||||||
|
label: 'Threshold',
|
||||||
|
default: 50,
|
||||||
|
min: 0,
|
||||||
|
max: 100
|
||||||
|
},
|
||||||
|
'colors': {
|
||||||
|
type: 'select',
|
||||||
|
label: 'Colors',
|
||||||
|
options: [
|
||||||
|
{ name: '0', value: '0', default: true },
|
||||||
|
{ name: '1', value: '1' },
|
||||||
|
{ name: '2', value: '2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible web-based commandline interface: https://hyper.is/?
|
||||||
|
|
||||||
|
|
||||||
|
### Path cutting
|
||||||
|
|
||||||
|
* threshold
|
||||||
|
* vectorize
|
||||||
|
* edge detect
|
||||||
|
* direction find (vectorize and colorize)
|
||||||
272
README.md
272
README.md
@@ -26,6 +26,14 @@ It is also for prototyping some other related ideas:
|
|||||||
* [Basic example](https://jywarren.github.io/image-sequencer/)
|
* [Basic example](https://jywarren.github.io/image-sequencer/)
|
||||||
* [NDVI example](https://jywarren.github.io/image-sequencer/examples/ndvi/) - related to [Infragram.org](http://infragram.org)
|
* [NDVI example](https://jywarren.github.io/image-sequencer/examples/ndvi/) - related to [Infragram.org](http://infragram.org)
|
||||||
|
|
||||||
|
## Jump to:
|
||||||
|
|
||||||
|
* [Quick Usage](#quick-usage)
|
||||||
|
* [Classic Usage](#classic-usage)
|
||||||
|
* [Method Chaining](#method-chaining)
|
||||||
|
* [Multiple Images](#multiple-images)
|
||||||
|
* [Creating a User Interface](#creating-a-user-interface)
|
||||||
|
|
||||||
## Quick Usage
|
## Quick Usage
|
||||||
|
|
||||||
Image Sequencer can be used to run modules on an HTML Image Element using the
|
Image Sequencer can be used to run modules on an HTML Image Element using the
|
||||||
@@ -68,11 +76,26 @@ a name and an image. The method also accepts an optional callback.
|
|||||||
```js
|
```js
|
||||||
sequencer.loadImage(image_src,optional_callback);
|
sequencer.loadImage(image_src,optional_callback);
|
||||||
```
|
```
|
||||||
On `Node.js` the `image_src` may be a DataURI or a local path. On browsers, it
|
On `Node.js` the `image_src` may be a DataURI or a local path or a URL.
|
||||||
must be a DatURI (or 'selector to image' -- Work in Progress)
|
|
||||||
|
|
||||||
return value: **`sequencer`** (To allow method chaining)
|
On browsers, it may be a DatURI, a local image or a URL (Unless this violates
|
||||||
|
CORS Restrictions). To sum up, these are accepted:
|
||||||
|
* Images in the same domain (or directory - for a local implementation)
|
||||||
|
* CORS-Proof images in another domain.
|
||||||
|
* DataURLs
|
||||||
|
|
||||||
|
return value: **none** (A callback should be used to ensure the image gets loaded)
|
||||||
|
The callback is called within the scope of a the sequencer. For example:
|
||||||
|
(addSteps is defined later)
|
||||||
|
|
||||||
|
```js
|
||||||
|
sequencer.loadImage('SRC',function(){
|
||||||
|
this.addSteps('module-name');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The `this` refers to all the images added in the parent `loadImages` function only.
|
||||||
|
In this case, only `'SRC'`.
|
||||||
|
|
||||||
### Adding steps to the image
|
### Adding steps to the image
|
||||||
|
|
||||||
@@ -156,6 +179,7 @@ return value: **`sequencer`** (To allow method chaining)
|
|||||||
|
|
||||||
## Method Chaining
|
## Method Chaining
|
||||||
Methods can be chained on the Image Sequencer:
|
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.
|
* run() can not be in the middle of the chain.
|
||||||
* If the chain starts with loadImage() or loadImages(), the following methods are
|
* If the chain starts with loadImage() or loadImages(), the following methods are
|
||||||
applied only to the newly loaded images.
|
applied only to the newly loaded images.
|
||||||
@@ -164,9 +188,11 @@ be of the form "image<number>". For ex: "image1", "image2", "image3", etc.
|
|||||||
|
|
||||||
Valid Chains:
|
Valid Chains:
|
||||||
```js
|
```js
|
||||||
sequencer.loadImage('red').addSteps('invert').run(function(out){
|
sequencer.loadImage('red',function(){
|
||||||
//do something with otuput.
|
this.addSteps('invert').run(function(out){
|
||||||
});
|
//do something with ouptut.
|
||||||
|
});
|
||||||
|
})
|
||||||
sequencer.addSteps(['ndvi-red','invert']).run();
|
sequencer.addSteps(['ndvi-red','invert']).run();
|
||||||
et cetra.
|
et cetra.
|
||||||
```
|
```
|
||||||
@@ -209,7 +235,7 @@ with each image. This is a string literal.
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
return value: **`sequencer`** (To allow method chaining)
|
return value: **none**
|
||||||
|
|
||||||
|
|
||||||
### Adding Steps on Multiple Images
|
### Adding Steps on Multiple Images
|
||||||
@@ -325,204 +351,50 @@ sequencer.insertSteps({
|
|||||||
return value: **`sequencer`** (To allow method chaining)
|
return value: **`sequencer`** (To allow method chaining)
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Creating a User Interface
|
||||||
|
|
||||||
Happily accepting pull requests; to edit the core library, modify files in `/src/`. To build, run `npm install` and `grunt build`.
|
Image Sequencer provides the following events which can be used to generate a UI:
|
||||||
|
|
||||||
### Contributing modules
|
* `onSetup` : this event is triggered when a new module is set up. This can be used,
|
||||||
|
for instance, to generate a DIV element to store the generated image for that step.
|
||||||
|
* `onDraw` : This event is triggered when Image Sequencer starts drawing the output
|
||||||
|
for a module. This can be used, for instance, to overlay a loading GIF over the DIV
|
||||||
|
generated above.
|
||||||
|
* `onComplete` : This event is triggered when Image Sequencer has drawn the output
|
||||||
|
for a module. This can be used, for instance, to update the DIV with the new image
|
||||||
|
and remove the loading GIF generated above.
|
||||||
|
* `onRemove` : This event is triggered when a module is removed. This can be used,
|
||||||
|
for instance, to remove the DIV generated above.
|
||||||
|
|
||||||
Most contribution (we imagine) would be in the form of API-compatible modules, which need not be directly included.
|
How to define these functions:
|
||||||
|
|
||||||
#### draw()
|
|
||||||
|
|
||||||
To add a module to Image Sequencer, it must have the following method; you can wrap an existing module to add them:
|
|
||||||
|
|
||||||
* `module.draw()`
|
|
||||||
|
|
||||||
The `draw(input,callback)` method should accept an `input` parameter, which will be an object of the form:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
input = {
|
sequencer.setUI({
|
||||||
src: "datauri here",
|
onSetup: function() {},
|
||||||
format: "jpeg/png/etc"
|
onDraw: function() {},
|
||||||
|
onComplete: function(output) {},
|
||||||
|
onRemove: function() {}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
These methods can be defined and re-defined at any time, but it is advisable to
|
||||||
|
set them before any module is added and not change it thereafter. This is because
|
||||||
|
the `setUI` method will only affect the modules added after `setUI` is called.
|
||||||
|
|
||||||
|
The `onComplete` event is passed on the output of the module.
|
||||||
|
|
||||||
|
In the scope of all these events, the following variables are present, which
|
||||||
|
may be used in generating the UI:
|
||||||
|
* The object `identity`
|
||||||
|
```
|
||||||
|
identity = {
|
||||||
|
stepName: "Name of the Step",
|
||||||
|
stepID: "A unique ID given to the step",
|
||||||
|
imageName: "The name of the image to which the step is added."
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* The variable `options.inBrowser` which is a Boolean and is `true` if the client is a browser and `false` otherwise.
|
||||||
|
|
||||||
The `image` object is essentially the output of the previous step.
|
Note: `identity.imageName` is the "name" of that particular image. This name can be specified
|
||||||
|
while loading the image via `sequencer.loadImage("name","SRC")`. If not specified,
|
||||||
The draw method must, when it is complete, pass the output image to the method `this.output = modified_input`, which will send the output to the next module in the chain. For example:
|
the name of a loaded image defaults to a name like "image1", "image2", et cetra.
|
||||||
|
|
||||||
```js
|
|
||||||
function draw(image) {
|
|
||||||
|
|
||||||
// do some stuff with the image
|
|
||||||
|
|
||||||
this.output = image;
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Title
|
|
||||||
|
|
||||||
For display in the web-based UI, each module may also have a title like `options.title`.
|
|
||||||
|
|
||||||
#### Module example
|
|
||||||
|
|
||||||
See existing module `green-channel` for an example: https://github.com/jywarren/image-sequencer/tree/master/src/modules/GreenChannel.js
|
|
||||||
|
|
||||||
For help integrating, please open an issue.
|
|
||||||
|
|
||||||
****
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
Notes on development next steps:
|
|
||||||
|
|
||||||
### UI
|
|
||||||
|
|
||||||
* [ ] add createUserInterface() which is set up by default to draw on ImageBoardUI, but could be swapped for nothing, or an equiv. lib
|
|
||||||
* [ ] it could create the interface and use event listeners like module.on('draw', fn()); to update the interface
|
|
||||||
|
|
||||||
* [ ] spinners before panels are complete
|
|
||||||
* [ ] is there a module for generating forms from parameters?
|
|
||||||
* [ ] click to expand for all images
|
|
||||||
* [ ] `ImageSequencer.Renderer` class to manage image output formats and adapters
|
|
||||||
* [ ] remove step
|
|
||||||
|
|
||||||
* [ ] output besides an image -- like `message(txt)` to display to the step's UI
|
|
||||||
|
|
||||||
|
|
||||||
### Modularization
|
|
||||||
|
|
||||||
* [ ] remotely includable modules, not compiled in -- see plugin structures in other libs
|
|
||||||
* [x] ability to start running at any point -- already works?
|
|
||||||
* [x] commandline runnability?
|
|
||||||
* [x] Make available as browserified OR `require()` includable...
|
|
||||||
* [ ] standardize panel addition with submodule that offers Panel.display(image)
|
|
||||||
* [ ] allow passing data as data-uri or Image object, or stream, or ndarray or ImageData array, if both of neighboring pair has ability?
|
|
||||||
* see https://github.com/jywarren/image-sequencer/issues/1
|
|
||||||
* [ ] ...could we directly include package.json for module descriptions? At least as a fallback.
|
|
||||||
* [ ] (for node-and-line style UIs) non-linear sequences with Y-splitters
|
|
||||||
* [ ] `sequencer.addModule('path/to/module.js')` style module addition -- also to avoid browserifying all of Plotly :-P
|
|
||||||
* [x] remove step
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
* [ ] tests - modules headless; unit tests
|
|
||||||
* [ ] comparisons with diff
|
|
||||||
* [ ] testing a module's promised functionality: each module could offer before/after images as part of their API; by running the module on the before image, you should get exactly the after image, comparing with an image diff
|
|
||||||
|
|
||||||
### Use cases
|
|
||||||
|
|
||||||
* [ ] make an Infragram module that accepts a math expression
|
|
||||||
|
|
||||||
### Bugs
|
|
||||||
|
|
||||||
* [x] BUG: this doesn't work for defaults: imageboard.loadImage('examples/grid.png', function() {});
|
|
||||||
* we should make defaults a config of the first module
|
|
||||||
|
|
||||||
****
|
|
||||||
|
|
||||||
## Module Candidates
|
|
||||||
|
|
||||||
* https://github.com/linuxenko/rextract.js
|
|
||||||
* https://www.npmjs.com/package/histogram
|
|
||||||
* https://github.com/hughsk/flood-fill
|
|
||||||
* https://www.npmjs.com/package/blink-diff
|
|
||||||
* smaller and faster: https://www.npmjs.com/package/@schornio/pixelmatch
|
|
||||||
* https://github.com/yahoo/pngjs-image has lots of useful general-purpose image getters like `image.getLuminosityAtIndex(idx)`
|
|
||||||
* some way to add in a new image (respecting alpha) -- `add-image` (with blend mode, default `normal`?)
|
|
||||||
* https://github.com/yuta1984/CannyJS - edge detection
|
|
||||||
* http://codepen.io/taylorcoffelt/pen/EsCcr - more edge detection
|
|
||||||
|
|
||||||
## Ideas
|
|
||||||
|
|
||||||
* https://github.com/vicapow/jsqrcode
|
|
||||||
* https://github.com/jadnco/whirl - scrubbable image sequence player
|
|
||||||
* non graphics card GL functions could be shimmed with https://github.com/Overv/JSGL
|
|
||||||
* or this: https://github.com/stackgl/headless-gl
|
|
||||||
* https://github.com/mattdesl/fontpath-simple-renderer
|
|
||||||
* output in animated Gif? as a module
|
|
||||||
|
|
||||||
### Referencing earlier states
|
|
||||||
|
|
||||||
Complex sequences with masking could require accessing previous states (or nonlinearity):
|
|
||||||
|
|
||||||
* flood-fill an area
|
|
||||||
* select only the flooded area
|
|
||||||
* roundabout: lighten everything to <50%, then flood-fill with black? Not 100% reliable.
|
|
||||||
* roundabout 2: `flood fill`, then `blink-diff` with original
|
|
||||||
* then add step which recovers original image, repeat `flood-fill`/`blink-diff` for second region
|
|
||||||
* reference above masked states in a `mask` module, with `maskModule.draw(image, { getMask: function() { return maskImg } })`
|
|
||||||
|
|
||||||
****
|
|
||||||
|
|
||||||
**Notes:**
|
|
||||||
|
|
||||||
`pattern-fill` module to use patterns in JS canvas:
|
|
||||||
|
|
||||||
```js
|
|
||||||
var c=document.getElementById("myCanvas");
|
|
||||||
var ctx=c.getContext("2d");
|
|
||||||
var img=document.getElementById("lamp");
|
|
||||||
var pat=ctx.createPattern(img,"repeat");
|
|
||||||
ctx.rect(0,0,150,100);
|
|
||||||
ctx.fillStyle=pat;
|
|
||||||
ctx.fill();
|
|
||||||
```
|
|
||||||
|
|
||||||
Masking:
|
|
||||||
|
|
||||||
```js
|
|
||||||
ctx.save();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(0, 0);
|
|
||||||
ctx.lineTo(160, 600);
|
|
||||||
ctx.rect(0, 0, 160, 600);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.clip();
|
|
||||||
ctx.drawImage(img, 0, 0);
|
|
||||||
ctx.restore();
|
|
||||||
```
|
|
||||||
|
|
||||||
****
|
|
||||||
|
|
||||||
## UI notes:
|
|
||||||
|
|
||||||
* visual nodes-and-lines UI: https://github.com/flowhub/the-graph
|
|
||||||
* https://flowhub.github.io/the-graph/examples/demo-simple.html
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
|
||||||
|
|
||||||
settings: {
|
|
||||||
'threshold': {
|
|
||||||
type: 'slider',
|
|
||||||
label: 'Threshold',
|
|
||||||
default: 50,
|
|
||||||
min: 0,
|
|
||||||
max: 100
|
|
||||||
},
|
|
||||||
'colors': {
|
|
||||||
type: 'select',
|
|
||||||
label: 'Colors',
|
|
||||||
options: [
|
|
||||||
{ name: '0', value: '0', default: true },
|
|
||||||
{ name: '1', value: '1' },
|
|
||||||
{ name: '2', value: '2' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Possible web-based commandline interface: https://hyper.is/?
|
|
||||||
|
|
||||||
|
|
||||||
### Path cutting
|
|
||||||
|
|
||||||
* threshold
|
|
||||||
* vectorize
|
|
||||||
* edge detect
|
|
||||||
* direction find (vectorize and colorize)
|
|
||||||
|
|||||||
998
dist/image-sequencer.js
vendored
998
dist/image-sequencer.js
vendored
File diff suppressed because one or more lines are too long
BIN
examples/red.png
Normal file
BIN
examples/red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 277 B |
BIN
examples/test.gif
Normal file
BIN
examples/test.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
1
examples/test.gif.js
Normal file
1
examples/test.gif.js
Normal file
File diff suppressed because one or more lines are too long
1
examples/test.png.js
Normal file
1
examples/test.png.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "image-sequencer",
|
"name": "image-sequencer",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
||||||
"main": "dist/image-sequencer.js",
|
"main": "src/ImageSequencer.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "tape test/*.js | tap-spec; browserify test/image-sequencer.js test/chain.js | tape-run --render=\"tap-spec\""
|
"test": "tape test/*.js | tap-spec; browserify test/image-sequencer.js test/chain.js | tape-run --render=\"tap-spec\""
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
"urify": "^2.1.0"
|
"urify": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"base64-stream": "~0.1.3",
|
|
||||||
"browserify": "13.0.0",
|
"browserify": "13.0.0",
|
||||||
"buffer": "~5.0.2",
|
"buffer": "~5.0.2",
|
||||||
"get-pixels": "~3.3.0",
|
"get-pixels": "~3.3.0",
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
function AddStep(ref, image, name, o) {
|
function AddStep(ref, image, name, o) {
|
||||||
|
|
||||||
function addStep(image, name, o_) {
|
function addStep(image, name, o_) {
|
||||||
ref.log('\x1b[36m%s\x1b[0m','adding step \"' + name + '\" to \"' + image + '\".');
|
var o = ref.copy(o_);
|
||||||
|
o.number = ref.options.sequencerCounter++; //Gives a Unique ID to each step
|
||||||
o = ref.copy(o_);
|
|
||||||
o.id = ref.options.sequencerCounter++; //Gives a Unique ID to each step
|
|
||||||
o.name = o_.name || name;
|
o.name = o_.name || name;
|
||||||
o.selector = o_.selector || 'ismod-' + name;
|
o.selector = o_.selector || 'ismod-' + name;
|
||||||
o.container = o_.container || ref.options.selector;
|
o.container = o_.container || ref.options.selector;
|
||||||
o.image = image;
|
o.image = image;
|
||||||
o.inBrowser = ref.options.inBrowser;
|
o.inBrowser = ref.options.inBrowser;
|
||||||
|
|
||||||
var module = ref.modules[name].call(ref.images,o);
|
var UI = ref.UI({
|
||||||
|
stepName: o.name,
|
||||||
|
stepID: o.number,
|
||||||
|
imageName: o.image
|
||||||
|
});
|
||||||
|
var module = ref.modules[name](o,UI);
|
||||||
ref.images[image].steps.push(module);
|
ref.images[image].steps.push(module);
|
||||||
|
|
||||||
|
|
||||||
function defaultSetupModule() {
|
|
||||||
if (ref.options.ui && ref.options.ui!="none") module.options.ui = ref.options.ui({
|
|
||||||
selector: o.selector,
|
|
||||||
title: module.options.title,
|
|
||||||
id: o.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (module.hasOwnProperty('setup')) module.setup(); // add a default UI, unless the module has one specified
|
|
||||||
else defaultSetupModule.apply(module); // run default setup() in scope of module (is this right?)
|
|
||||||
|
|
||||||
// tell the UI that a step has been added.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function copy(a) {
|
|||||||
if (objTypeOf(a) == "Array") return a.slice();
|
if (objTypeOf(a) == "Array") return a.slice();
|
||||||
if (objTypeOf(a) == "Object") {
|
if (objTypeOf(a) == "Object") {
|
||||||
var b = {};
|
var b = {};
|
||||||
for (v in a) {
|
for (var v in a) {
|
||||||
b[v] = copy(a[v]);
|
b[v] = copy(a[v]);
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
@@ -25,11 +25,11 @@ function copy(a) {
|
|||||||
|
|
||||||
function formatInput(args,format,images) {
|
function formatInput(args,format,images) {
|
||||||
images = [];
|
images = [];
|
||||||
for (image in this.images) {
|
for (var image in this.images) {
|
||||||
images.push(image);
|
images.push(image);
|
||||||
}
|
}
|
||||||
json_q = {};
|
var json_q = {};
|
||||||
format_i = format;
|
var format_i = format;
|
||||||
if (format == "+")
|
if (format == "+")
|
||||||
format = ['o_string_a', 'string_a', 'o_object'];
|
format = ['o_string_a', 'string_a', 'o_object'];
|
||||||
else if (format == "-")
|
else if (format == "-")
|
||||||
@@ -76,8 +76,8 @@ function formatInput(args,format,images) {
|
|||||||
|
|
||||||
if(format[0] == "o_string_a") {
|
if(format[0] == "o_string_a") {
|
||||||
if(args.length == format.length - 1) {
|
if(args.length == format.length - 1) {
|
||||||
insert = false;
|
var insert = false;
|
||||||
for (i in args) {
|
for (var i in args) {
|
||||||
if (format[parseInt(i)+1].includes( typeof(getPrimitive(args[i])) )){
|
if (format[parseInt(i)+1].includes( typeof(getPrimitive(args[i])) )){
|
||||||
insert = true;
|
insert = true;
|
||||||
}
|
}
|
||||||
@@ -89,15 +89,15 @@ function formatInput(args,format,images) {
|
|||||||
}
|
}
|
||||||
else if (format[0] == "o_string" && format_i == "l" && args.length == 2) {
|
else if (format[0] == "o_string" && format_i == "l" && args.length == 2) {
|
||||||
if (typeof(args[0]) == "string") {
|
if (typeof(args[0]) == "string") {
|
||||||
identifier = "image";
|
var identifier = "image";
|
||||||
number = 1;
|
var number = 1;
|
||||||
while (this.images.hasOwnProperty(identifier+number)) number++;
|
while (this.images.hasOwnProperty(identifier+number)) number++;
|
||||||
args.splice(0,0,identifier+number);
|
args.splice(0,0,identifier+number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(args.length == format.length) {
|
if(args.length == format.length) {
|
||||||
for (i in format) {
|
for (var i in format) {
|
||||||
if (format[i].substr(format[i].length-2,2)=="_a")
|
if (format[i].substr(format[i].length-2,2)=="_a")
|
||||||
args[i] = makeArray(args[i]);
|
args[i] = makeArray(args[i]);
|
||||||
}
|
}
|
||||||
@@ -106,12 +106,12 @@ function formatInput(args,format,images) {
|
|||||||
if (args.length == 1) {
|
if (args.length == 1) {
|
||||||
json_q = copy(args[0]);
|
json_q = copy(args[0]);
|
||||||
if(!(format_i == "r" || format_i == "l")) {
|
if(!(format_i == "r" || format_i == "l")) {
|
||||||
for (img in json_q)
|
for (var img in json_q)
|
||||||
json_q[img] = makeArray(json_q[img]);
|
json_q[img] = makeArray(json_q[img]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (format_i == "r") {
|
else if (format_i == "r") {
|
||||||
for (img in args[0]) json_q[args[0][img]] = args[1];
|
for (var img in args[0]) json_q[args[0][img]] = args[1];
|
||||||
}
|
}
|
||||||
else if (format_i == "l") {
|
else if (format_i == "l") {
|
||||||
json_q = {
|
json_q = {
|
||||||
@@ -121,12 +121,12 @@ function formatInput(args,format,images) {
|
|||||||
json_q.images[args[0]] = args[1];
|
json_q.images[args[0]] = args[1];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (img in args[0]) {
|
for (var img in args[0]) {
|
||||||
image = args[0][img];
|
var image = args[0][img];
|
||||||
json_q[image] = [];
|
json_q[image] = [];
|
||||||
|
|
||||||
if(format_i == "+") {
|
if(format_i == "+") {
|
||||||
for(s in args[1]) {
|
for(var s in args[1]) {
|
||||||
json_q[image].push({
|
json_q[image].push({
|
||||||
name: args[1][s],
|
name: args[1][s],
|
||||||
o: args[2]
|
o: args[2]
|
||||||
@@ -139,8 +139,8 @@ function formatInput(args,format,images) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(format_i == "^") {
|
if(format_i == "^") {
|
||||||
size = this.images[image].steps.length;
|
var size = this.images[image].steps.length;
|
||||||
index = args[1];
|
var index = args[1];
|
||||||
index = (index==size)?index:index%size;
|
index = (index==size)?index:index%size;
|
||||||
if (index<0) index += size+1;
|
if (index<0) index += size+1;
|
||||||
json_q[image].push({
|
json_q[image].push({
|
||||||
@@ -155,7 +155,7 @@ function formatInput(args,format,images) {
|
|||||||
|
|
||||||
if(format_i == "l") {
|
if(format_i == "l") {
|
||||||
json_q.loadedimages = [];
|
json_q.loadedimages = [];
|
||||||
for (i in json_q.images) json_q.loadedimages.push(i);
|
for (var i in json_q.images) json_q.loadedimages.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_q;
|
return json_q;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
if (objTypeOf(a) == "Array") return a.slice();
|
if (objTypeOf(a) == "Array") return a.slice();
|
||||||
if (objTypeOf(a) == "Object") {
|
if (objTypeOf(a) == "Object") {
|
||||||
var b = {};
|
var b = {};
|
||||||
for (v in a) {
|
for (var v in a) {
|
||||||
b[v] = copy(a[v]);
|
b[v] = copy(a[v]);
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
@@ -36,13 +36,15 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
return (objTypeOf(input)=="Array")?input:[input];
|
return (objTypeOf(input)=="Array")?input:[input];
|
||||||
}
|
}
|
||||||
|
|
||||||
formatInput = require('./FormatInput');
|
|
||||||
|
|
||||||
var image,
|
var image,
|
||||||
steps = [],
|
steps = [],
|
||||||
modules = require('./Modules'),
|
modules = require('./Modules'),
|
||||||
|
formatInput = require('./FormatInput'),
|
||||||
images = {},
|
images = {},
|
||||||
inputlog = [];
|
inputlog = [],
|
||||||
|
UI;
|
||||||
|
|
||||||
|
setUI();
|
||||||
|
|
||||||
// if in browser, prompt for an image
|
// if in browser, prompt for an image
|
||||||
// if (options.imageSelect || options.inBrowser) addStep('image-select');
|
// if (options.imageSelect || options.inBrowser) addStep('image-select');
|
||||||
@@ -50,15 +52,15 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
|
|
||||||
function addSteps(){
|
function addSteps(){
|
||||||
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||||
args = (this.name == "ImageSequencer")?[]:[this.images];
|
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||||
json_q = {};
|
var json_q = {};
|
||||||
for(arg in arguments){args.push(copy(arguments[arg]));}
|
for(var arg in arguments){args.push(copy(arguments[arg]));}
|
||||||
json_q = formatInput.call(this_,args,"+");
|
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 (i in json_q)
|
for (var i in json_q)
|
||||||
for (j in json_q[i])
|
for (var j in json_q[i])
|
||||||
require("./AddStep")(this_,i,json_q[i][j].name,json_q[i][j].o);
|
require("./AddStep")(this_,i,json_q[i][j].name,json_q[i][j].o);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@@ -67,25 +69,25 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
function removeStep(image,index) {
|
function removeStep(image,index) {
|
||||||
//remove the step from images[image].steps and redraw remaining images
|
//remove the step from images[image].steps and redraw remaining images
|
||||||
if(index>0) {
|
if(index>0) {
|
||||||
log('\x1b[31m%s\x1b[0m',"Removing "+index+" from "+image);
|
images[image].steps[index].UI.onRemove();
|
||||||
images[image].steps.splice(index,1);
|
images[image].steps.splice(index,1);
|
||||||
}
|
}
|
||||||
//tell the UI a step has been removed
|
//tell the UI a step has been removed
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSteps(image,index) {
|
function removeSteps(image,index) {
|
||||||
run = {};
|
var run = {}, indices;
|
||||||
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||||
args = (this.name == "ImageSequencer")?[]:[this.images];
|
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||||
for(arg in arguments) args.push(copy(arguments[arg]));
|
for(var arg in arguments) args.push(copy(arguments[arg]));
|
||||||
|
|
||||||
json_q = formatInput.call(this_,args,"-");
|
var json_q = formatInput.call(this_,args,"-");
|
||||||
inputlog.push({method:"removeSteps", json_q:copy(json_q)});
|
inputlog.push({method:"removeSteps", json_q:copy(json_q)});
|
||||||
|
|
||||||
for (img in json_q) {
|
for (var img in json_q) {
|
||||||
indices = json_q[img].sort(function(a,b){return b-a});
|
indices = json_q[img].sort(function(a,b){return b-a});
|
||||||
run[img] = indices[indices.length-1];
|
run[img] = indices[indices.length-1];
|
||||||
for (i in indices)
|
for (var i in indices)
|
||||||
removeStep(img,indices[i]);
|
removeStep(img,indices[i]);
|
||||||
}
|
}
|
||||||
// this.run(run); // This is creating problems
|
// this.run(run); // This is creating problems
|
||||||
@@ -93,18 +95,18 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function insertSteps(image, index, name, o) {
|
function insertSteps(image, index, name, o) {
|
||||||
run = {};
|
var run = {};
|
||||||
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||||
args = (this.name == "ImageSequencer")?[]:[this.images];
|
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||||
for (arg in arguments) args.push(arguments[arg]);
|
for (var arg in arguments) args.push(arguments[arg]);
|
||||||
|
|
||||||
json_q = formatInput.call(this_,args,"^");
|
var json_q = formatInput.call(this_,args,"^");
|
||||||
inputlog.push({method:"insertSteps", json_q:copy(json_q)});
|
inputlog.push({method:"insertSteps", json_q:copy(json_q)});
|
||||||
|
|
||||||
for (img in json_q) {
|
for (var img in json_q) {
|
||||||
var details = json_q[img];
|
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 (i in details)
|
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;
|
run[img] = details[details.length-1].index;
|
||||||
}
|
}
|
||||||
@@ -113,17 +115,16 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function run(t_image,t_from) {
|
function run(t_image,t_from) {
|
||||||
log('\x1b[32m%s\x1b[0m',"Running the Sequencer!");
|
|
||||||
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
const this_ = (this.name == "ImageSequencer")?this:this.sequencer;
|
||||||
args = (this.name == "ImageSequencer")?[]:[this.images];
|
var args = (this.name == "ImageSequencer")?[]:[this.images];
|
||||||
for (var arg in arguments) args.push(copy(arguments[arg]));
|
for (var arg in arguments) args.push(copy(arguments[arg]));
|
||||||
|
|
||||||
callback = function() {};
|
var callback = function() {};
|
||||||
for (var arg in args)
|
for (var arg in args)
|
||||||
if(objTypeOf(args[arg]) == "Function")
|
if(objTypeOf(args[arg]) == "Function")
|
||||||
callback = args.splice(arg,1)[0];
|
callback = args.splice(arg,1)[0];
|
||||||
|
|
||||||
json_q = formatInput.call(this_,args,"r");
|
var json_q = formatInput.call(this_,args,"r");
|
||||||
|
|
||||||
require('./Run')(this_, json_q, callback);
|
require('./Run')(this_, json_q, callback);
|
||||||
|
|
||||||
@@ -131,26 +132,39 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadImages() {
|
function loadImages() {
|
||||||
args = [];
|
var args = [];
|
||||||
for (arg in arguments) args.push(copy(arguments[arg]));
|
var sequencer = this;
|
||||||
json_q = formatInput.call(this,args,"l");
|
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)});
|
inputlog.push({method:"loadImages", json_q:copy(json_q)});
|
||||||
loadedimages = this.copy(json_q.loadedimages);
|
var loadedimages = this.copy(json_q.loadedimages);
|
||||||
|
// require('./LoadImage')(this,i,json_q.images[i]);
|
||||||
|
|
||||||
for (i in json_q.images)
|
var ret = {
|
||||||
require('./LoadImage')(this,i,json_q.images[i])
|
|
||||||
|
|
||||||
json_q.callback();
|
|
||||||
return {
|
|
||||||
name: "ImageSequencer Wrapper",
|
name: "ImageSequencer Wrapper",
|
||||||
sequencer: this,
|
sequencer: this,
|
||||||
addSteps: this.addSteps,
|
addSteps: this.addSteps,
|
||||||
removeSteps: this.removeSteps,
|
removeSteps: this.removeSteps,
|
||||||
insertSteps: this.insertSteps,
|
insertSteps: this.insertSteps,
|
||||||
run: this.run,
|
run: this.run,
|
||||||
|
UI: this.UI,
|
||||||
|
setUI: this.setUI,
|
||||||
images: loadedimages
|
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(){
|
||||||
|
load(++i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
load(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceImage(selector,steps,options) {
|
function replaceImage(selector,steps,options) {
|
||||||
@@ -158,9 +172,21 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
return require('./ReplaceImage')(this,selector,steps);
|
return require('./ReplaceImage')(this,selector,steps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setUI(_UI) {
|
||||||
|
UI = require('./UserInterface')(_UI,options);
|
||||||
|
return UI;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
//literals and objects
|
||||||
name: "ImageSequencer",
|
name: "ImageSequencer",
|
||||||
options: options,
|
options: options,
|
||||||
|
inputlog: inputlog,
|
||||||
|
modules: modules,
|
||||||
|
images: images,
|
||||||
|
UI: UI,
|
||||||
|
|
||||||
|
//user functions
|
||||||
loadImages: loadImages,
|
loadImages: loadImages,
|
||||||
loadImage: loadImages,
|
loadImage: loadImages,
|
||||||
addSteps: addSteps,
|
addSteps: addSteps,
|
||||||
@@ -168,10 +194,9 @@ ImageSequencer = function ImageSequencer(options) {
|
|||||||
insertSteps: insertSteps,
|
insertSteps: insertSteps,
|
||||||
replaceImage: replaceImage,
|
replaceImage: replaceImage,
|
||||||
run: run,
|
run: run,
|
||||||
inputlog: inputlog,
|
setUI: setUI,
|
||||||
modules: modules,
|
|
||||||
images: images,
|
//other functions
|
||||||
ui: options.ui,
|
|
||||||
log: log,
|
log: log,
|
||||||
objTypeOf: objTypeOf,
|
objTypeOf: objTypeOf,
|
||||||
copy: copy
|
copy: copy
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
function InsertStep(ref, image, index, name, o) {
|
function InsertStep(ref, image, index, name, o) {
|
||||||
|
|
||||||
function insertStep(image, index, name, o_) {
|
function insertStep(image, index, name, o_) {
|
||||||
ref.log('\x1b[36m%s\x1b[0m','inserting step \"' + name + '\" to \"' + image + '\" at \"'+index+'\".');
|
var o = ref.copy(o_);
|
||||||
|
o.number = ref.options.sequencerCounter++; //Gives a Unique ID to each step
|
||||||
o = ref.copy(o_);
|
o.name = o_.name || name;
|
||||||
o.id = ref.options.sequencerCounter++; //Gives a Unique ID to each step
|
o.selector = o_.selector || 'ismod-' + name;
|
||||||
o.name = o.name || name;
|
o.container = o_.container || ref.options.selector;
|
||||||
o.selector = o.selector || 'ismod-' + name;
|
|
||||||
o.container = o.container || ref.options.selector;
|
|
||||||
o.image = image;
|
o.image = image;
|
||||||
|
|
||||||
if(index==-1) index = ref.images[image].steps.length;
|
if(index==-1) index = ref.images[image].steps.length;
|
||||||
|
|
||||||
var module = ref.modules[name](o);
|
var UI = ref.UI({
|
||||||
ref.images[image].steps.splice(index, 0, module);
|
stepName: o.name,
|
||||||
|
stepID: o.number,
|
||||||
function defaultSetupModule() {
|
imageName: o.image
|
||||||
if (ref.options.ui && ref.options.ui!="none") module.options.ui = ref.options.ui({
|
});
|
||||||
selector: o.selector,
|
var module = ref.modules[name](o,UI);
|
||||||
title: module.options.title,
|
ref.images[image].steps.splice(index,0,module);
|
||||||
id: o.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (module.hasOwnProperty('setup')) module.setup(); // add a default UI, unless the module has one specified
|
|
||||||
else defaultSetupModule.apply(module); // run default setup() in scope of module (is this right?)
|
|
||||||
|
|
||||||
// tell the UI that a step has been inserted.
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,50 @@
|
|||||||
function LoadImage(ref, name, src) {
|
function LoadImage(ref, name, src, main_callback) {
|
||||||
function CImage(src) {
|
function makeImage(datauri) {
|
||||||
datauri = (ref.options.inBrowser || src.substring(0,11) == "data:image/")?(src):require('urify')(src);
|
var image = {
|
||||||
image = {
|
|
||||||
src: datauri,
|
src: datauri,
|
||||||
format: datauri.split(':')[1].split(';')[0].split('/')[1]
|
format: datauri.split(':')[1].split(';')[0].split('/')[1]
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
function CImage(src, callback) {
|
||||||
|
var datauri;
|
||||||
|
if (!!src.match(/^data:/i)) {
|
||||||
|
datauri = src;
|
||||||
|
callback(datauri);
|
||||||
|
}
|
||||||
|
else if (!ref.options.inBrowser && !!src.match(/^https?:\/\//i)) {
|
||||||
|
require( src.match(/^(https?):\/\//i)[1] ).get(src,function(res){
|
||||||
|
var data = '';
|
||||||
|
var contentType = res.headers['content-type'];
|
||||||
|
res.setEncoding('base64');
|
||||||
|
res.on('data',function(chunk) {data += chunk;});
|
||||||
|
res.on('end',function() {
|
||||||
|
callback("data:"+contentType+";base64,"+data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ref.options.inBrowser) {
|
||||||
|
var ext = src.split('.').pop();
|
||||||
|
var image = document.createElement('img');
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
image.onload = function() {
|
||||||
|
canvas.width = image.naturalWidth;
|
||||||
|
canvas.height = image.naturalHeight;
|
||||||
|
context.drawImage(image,0,0);
|
||||||
|
datauri = canvas.toDataURL(ext);
|
||||||
|
callback(datauri);
|
||||||
|
}
|
||||||
|
image.src = src;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
datauri = require('urify')(src);
|
||||||
|
callback(datauri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadImage(name, src) {
|
function loadImage(name, src) {
|
||||||
image = {
|
var image = {
|
||||||
src: src,
|
src: src,
|
||||||
steps: [{
|
steps: [{
|
||||||
options: {
|
options: {
|
||||||
@@ -17,6 +52,11 @@ function LoadImage(ref, name, src) {
|
|||||||
name: "load-image",
|
name: "load-image",
|
||||||
title: "Load Image"
|
title: "Load Image"
|
||||||
},
|
},
|
||||||
|
UI: ref.UI({
|
||||||
|
stepName: "load-image",
|
||||||
|
stepID: ref.options.sequencerCounter++,
|
||||||
|
imageName: name
|
||||||
|
}),
|
||||||
draw: function() {
|
draw: function() {
|
||||||
if(arguments.length==1){
|
if(arguments.length==1){
|
||||||
this.output = CImage(arguments[0]);
|
this.output = CImage(arguments[0]);
|
||||||
@@ -29,10 +69,18 @@ function LoadImage(ref, name, src) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
output: CImage(src)
|
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
ref.images[name] = image;
|
CImage(src, function(datauri) {
|
||||||
|
var output = makeImage(datauri);
|
||||||
|
image.steps[0].output = output;
|
||||||
|
ref.images[name] = image;
|
||||||
|
ref.images[name].steps[0].UI.onSetup();
|
||||||
|
ref.images[name].steps[0].UI.onDraw();
|
||||||
|
ref.images[name].steps[0].UI.onComplete(image.steps[0].output.src);
|
||||||
|
main_callback();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return loadImage(name,src);
|
return loadImage(name,src);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
function ReplaceImage(ref,selector,steps,options) {
|
function ReplaceImage(ref,selector,steps,options) {
|
||||||
if(!ref.options.inBrowser) return false; // This isn't for Node.js
|
if(!ref.options.inBrowser) return false; // This isn't for Node.js
|
||||||
this_ = ref;
|
var this_ = ref;
|
||||||
var input = document.querySelectorAll(selector);
|
var input = document.querySelectorAll(selector);
|
||||||
var images = [];
|
var images = [];
|
||||||
for (i = 0; i < input.length; i++)
|
for (var i = 0; i < input.length; i++)
|
||||||
if (input[i] instanceof HTMLImageElement) images.push(input[i]);
|
if (input[i] instanceof HTMLImageElement) images.push(input[i]);
|
||||||
for (i in images) {
|
for (var i in images) {
|
||||||
the_image = images[i];
|
var the_image = images[i];
|
||||||
var url = images[i].src;
|
var url = images[i].src;
|
||||||
var ext = url.split('.').pop();
|
var ext = url.split('.').pop();
|
||||||
|
|
||||||
|
|||||||
30
src/Run.js
30
src/Run.js
@@ -1,47 +1,47 @@
|
|||||||
function Run(ref, json_q, callback) {
|
function Run(ref, json_q, callback) {
|
||||||
function drawStep(drawarray,pos) {
|
function drawStep(drawarray,pos) {
|
||||||
if(pos==drawarray.length) {
|
if(pos==drawarray.length) {
|
||||||
image = drawarray[pos-1].image;
|
var image = drawarray[pos-1].image;
|
||||||
if(ref.objTypeOf(callback)=='Function'){
|
if(ref.objTypeOf(callback)=='Function'){
|
||||||
steps = ref.images[image].steps;
|
var steps = ref.images[image].steps;
|
||||||
out = steps[steps.length-1].output.src;
|
var out = steps[steps.length-1].output.src;
|
||||||
callback(out);
|
callback(out);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image = drawarray[pos].image;
|
var image = drawarray[pos].image;
|
||||||
i = drawarray[pos].i;
|
var i = drawarray[pos].i;
|
||||||
input = ref.images[image].steps[i-1].output;
|
var input = ref.images[image].steps[i-1].output;
|
||||||
ref.images[image].steps[i].draw(ref.copy(input),function(){
|
ref.images[image].steps[i].draw(ref.copy(input),function(){
|
||||||
drawStep(drawarray,++pos);
|
drawStep(drawarray,++pos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function drawSteps(json_q) {
|
function drawSteps(json_q) {
|
||||||
drawarray = [];
|
var drawarray = [];
|
||||||
for (image in json_q) {
|
for (var image in json_q) {
|
||||||
no_steps = ref.images[image].steps.length;
|
var no_steps = ref.images[image].steps.length;
|
||||||
init = json_q[image];
|
var init = json_q[image];
|
||||||
for(i = 0; i < no_steps-init; i++) {
|
for(var i = 0; i < no_steps-init; i++) {
|
||||||
drawarray.push({image: image,i: init+i});
|
drawarray.push({image: image,i: init+i});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawStep(drawarray,0);
|
drawStep(drawarray,0);
|
||||||
}
|
}
|
||||||
function filter(json_q){
|
function filter(json_q){
|
||||||
for (image in json_q) {
|
for (var image in json_q) {
|
||||||
if (json_q[image]==0 && ref.images[image].steps.length==1)
|
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]++;
|
else if (json_q[image]==0) json_q[image]++;
|
||||||
}
|
}
|
||||||
for (image in json_q) {
|
for (var image in json_q) {
|
||||||
prevstep = ref.images[image].steps[json_q[image]-1];
|
var prevstep = ref.images[image].steps[json_q[image]-1];
|
||||||
while (typeof(prevstep) == "undefined" || typeof(prevstep.output) == "undefined") {
|
while (typeof(prevstep) == "undefined" || typeof(prevstep.output) == "undefined") {
|
||||||
prevstep = ref.images[image].steps[(--json_q[image]) - 1];
|
prevstep = ref.images[image].steps[(--json_q[image]) - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return json_q;
|
return json_q;
|
||||||
}
|
}
|
||||||
json_q = filter(json_q);
|
var json_q = filter(json_q);
|
||||||
return drawSteps(json_q);
|
return drawSteps(json_q);
|
||||||
}
|
}
|
||||||
module.exports = Run;
|
module.exports = Run;
|
||||||
|
|||||||
@@ -1,41 +1,72 @@
|
|||||||
/*
|
/*
|
||||||
* Default UI for each image-sequencer module
|
* Default UI for each image-sequencer module
|
||||||
*/
|
*/
|
||||||
module.exports = function UserInterface(options) {
|
module.exports = function UserInterface(UI,options) {
|
||||||
|
|
||||||
options = options || {};
|
return function userInterface(identity) {
|
||||||
options.container = options.container || ".panels";
|
|
||||||
options.id = options.id;
|
|
||||||
options.instanceName = options.instanceName;
|
|
||||||
options.random = options.random || parseInt(Math.random() * (new Date()).getTime() / 1000000);
|
|
||||||
options.uniqueSelector = options.uniqueSelector || options.selector + '-' + options.random;
|
|
||||||
$(options.container).append('<div class="panel ' + options.selector + ' ' + options.uniqueSelector + '" id="sequencer-'+options.id+'"><div class="image"></div></div>');
|
|
||||||
options.el = options.el || $('.' + options.uniqueSelector);
|
|
||||||
createLabel(options.el);
|
|
||||||
|
|
||||||
// method to remove the UI for a given method, and remove the step
|
var UI = UI || {};
|
||||||
function display(image) {
|
|
||||||
options.el.find('.image').html(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
// method to remove the UI for a given method, and remove the step
|
UI.onSetup = UI.onSetup || function() {
|
||||||
function remove() {
|
if(options.ui == false) {
|
||||||
$('div#sequencer-'+options.id).remove();
|
// No UI
|
||||||
}
|
}
|
||||||
|
else if(options.inBrowser) {
|
||||||
|
// Create and append an HTML Element
|
||||||
|
console.log("Added Step \""+identity.stepName+"\" to \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Create a NodeJS Object
|
||||||
|
console.log('\x1b[36m%s\x1b[0m',"Added Step \""+identity.stepName+"\" to \""+identity.imageName+"\".");
|
||||||
|
|
||||||
// method to reorder steps, and update the UI
|
}
|
||||||
//function move() {}
|
}
|
||||||
|
|
||||||
function createLabel(el) {
|
UI.onDraw = UI.onDraw || function() {
|
||||||
if (options.title) el.prepend('<h3 class="title">' + options.title + '</h3> <button class="btn btn-default" onclick="'+options.instanceName+'.removeStep('+options.id+')">Remove Step</button>');
|
if (options.ui == false) {
|
||||||
}
|
// No UI
|
||||||
|
}
|
||||||
|
else if(options.inBrowser) {
|
||||||
|
// Overlay a loading spinner
|
||||||
|
console.log("Drawing Step \""+identity.stepName+"\" on \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Don't do anything
|
||||||
|
console.log('\x1b[33m%s\x1b[0m',"Drawing Step \""+identity.stepName+"\" on \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.onComplete = UI.onComplete || function(output) {
|
||||||
|
if (options.ui == false) {
|
||||||
|
// No UI
|
||||||
|
}
|
||||||
|
else if(options.inBrowser) {
|
||||||
|
// Update the DIV Element
|
||||||
|
// Hide the laoding spinner
|
||||||
|
console.log("Drawn Step \""+identity.stepName+"\" on \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Update the NodeJS Object
|
||||||
|
console.log('\x1b[32m%s\x1b[0m',"Drawn Step \""+identity.stepName+"\" on \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.onRemove = UI.onRemove || function(callback) {
|
||||||
|
if(options.ui == false){
|
||||||
|
// No UI
|
||||||
|
}
|
||||||
|
else if(options.inBrowser) {
|
||||||
|
// Remove the DIV Element
|
||||||
|
console.log("Removing Step \""+identity.stepName+"\" of \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Delete the NodeJS Object
|
||||||
|
console.log('\x1b[31m%s\x1b[0m',"Removing Step \""+identity.stepName+"\" of \""+identity.imageName+"\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UI;
|
||||||
|
|
||||||
return {
|
|
||||||
el: options.el,
|
|
||||||
uniqueSelector: options.uniqueSelector,
|
|
||||||
selector: options.selector,
|
|
||||||
display: display,
|
|
||||||
remove: remove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
module.exports = function Crop(input,options,callback) {
|
module.exports = function Crop(input,options,callback) {
|
||||||
|
|
||||||
var getPixels = require("get-pixels"),
|
var getPixels = require('get-pixels'),
|
||||||
savePixels = require("save-pixels"),
|
savePixels = require('save-pixels');
|
||||||
base64 = require('base64-stream');
|
|
||||||
|
|
||||||
getPixels(input.src,function(err,pixels){
|
getPixels(input.src,function(err,pixels){
|
||||||
var newdata = [];
|
|
||||||
var ox = options.x || 0;
|
var ox = options.x || 0;
|
||||||
var oy = options.y || 0;
|
var oy = options.y || 0;
|
||||||
var w = options.w || Math.floor(0.5*pixels.shape[0]);
|
var w = options.w || Math.floor(0.5*pixels.shape[0]);
|
||||||
var h = options.h || Math.floor(0.5*pixels.shape[1]);
|
var h = options.h || Math.floor(0.5*pixels.shape[1]);
|
||||||
var iw = pixels.shape[0]; //Width of Original Image
|
var iw = pixels.shape[0]; //Width of Original Image
|
||||||
newarray = new Uint8Array(4*w*h);
|
var newarray = new Uint8Array(4*w*h);
|
||||||
for (var n = oy; n < oy + h; n++) {
|
for (var n = oy; n < oy + h; n++) {
|
||||||
newarray.set(pixels.data.slice(n*4*iw + ox, n*4*iw + ox + 4*w),4*w*(n-oy));
|
newarray.set(pixels.data.slice(n*4*iw + ox, n*4*iw + ox + 4*w),4*w*(n-oy));
|
||||||
}
|
}
|
||||||
@@ -19,15 +17,19 @@ module.exports = function Crop(input,options,callback) {
|
|||||||
pixels.shape = [w,h,4];
|
pixels.shape = [w,h,4];
|
||||||
pixels.stride[1] = 4*w;
|
pixels.stride[1] = 4*w;
|
||||||
|
|
||||||
options.format = "jpeg";
|
var chunks = [];
|
||||||
|
var totalLength = 0;
|
||||||
w = base64.encode();
|
|
||||||
var r = savePixels(pixels, options.format);
|
var r = savePixels(pixels, options.format);
|
||||||
r.pipe(w).on('finish',function(){
|
|
||||||
data = w.read().toString();
|
r.on('data', function(chunk){
|
||||||
datauri = 'data:image/' + options.format + ';base64,' + data;
|
totalLength += chunk.length;
|
||||||
|
chunks.push(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
r.on('end', function(){
|
||||||
|
var data = Buffer.concat(chunks, totalLength).toString('base64');
|
||||||
|
var datauri = 'data:image/' + options.format + ';base64,' + data;
|
||||||
callback(datauri,options.format);
|
callback(datauri,options.format);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,21 +13,23 @@
|
|||||||
* y = options.y
|
* y = options.y
|
||||||
* y = options.y + options.h
|
* y = options.y + options.h
|
||||||
*/
|
*/
|
||||||
module.exports = function CropModule(options) {
|
module.exports = function CropModule(options,UI) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Crop Image";
|
options.title = "Crop Image";
|
||||||
this_ = this;
|
UI.onSetup();
|
||||||
var output
|
var output
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
|
|
||||||
const this_ = this;
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
require('./Crop')(input,options,function(out,format){
|
require('./Crop')(input,options,function(out,format){
|
||||||
this_.output = {
|
step.output = {
|
||||||
src: out,
|
src: out,
|
||||||
format: format
|
format: format
|
||||||
}
|
}
|
||||||
|
UI.onComplete(out);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Demo Module. Does nothing. Adds a step where output is equal to input.
|
* Demo Module. Does nothing. Adds a step where output is equal to input.
|
||||||
*/
|
*/
|
||||||
module.exports = function DoNothing(options) {
|
module.exports = function DoNothing(options,UI) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Do Nothing";
|
options.title = "Do Nothing";
|
||||||
this_ = this;
|
UI.onSetup();
|
||||||
var output
|
var output;
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
|
UI.onDraw();
|
||||||
this.output = input;
|
this.output = input;
|
||||||
callback();
|
callback();
|
||||||
|
UI.onComplete(this.output.src);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
/*
|
/*
|
||||||
* This module extracts pixels and saves them as it is.
|
* This module extracts pixels and saves them as it is.
|
||||||
*/
|
*/
|
||||||
module.exports = function DoNothingPix(options) {
|
module.exports = function DoNothingPix(options,UI) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Do Nothing with pixels";
|
options.title = "Do Nothing with pixels";
|
||||||
|
UI.onSetup();
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
this_ = this;
|
|
||||||
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
return [r, g, b, a];
|
return [r, g, b, a];
|
||||||
}
|
}
|
||||||
function output(image,datauri,mimetype){
|
function output(image,datauri,mimetype){
|
||||||
this_.output = {src:datauri,format:mimetype}
|
step.output = {src:datauri,format:mimetype}
|
||||||
|
UI.onComplete(datauri);
|
||||||
}
|
}
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
@@ -22,11 +27,13 @@ module.exports = function DoNothingPix(options) {
|
|||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback
|
callback: callback
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Display only the green channel
|
* Display only the green channel
|
||||||
*/
|
*/
|
||||||
module.exports = function GreenChannel(options) {
|
module.exports = function GreenChannel(options,UI) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Green channel only";
|
options.title = "Green channel only";
|
||||||
options.description = "Displays only the green channel of an image";
|
options.description = "Displays only the green channel of an image";
|
||||||
|
UI.onSetup();
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
//function setup() {} // optional
|
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
this_ = this;
|
|
||||||
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
return [0, g, 0, a];
|
return [0, g, 0, a];
|
||||||
}
|
}
|
||||||
function output(image,datauri,mimetype){
|
function output(image,datauri,mimetype){
|
||||||
this_.output = {src:datauri,format:mimetype}
|
step.output = {src:datauri,format:mimetype};
|
||||||
|
UI.onComplete(datauri);
|
||||||
}
|
}
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
@@ -25,12 +28,14 @@ module.exports = function GreenChannel(options) {
|
|||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback
|
callback: callback
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
//setup: setup, // optional
|
//setup: setup, // optional
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* Display only the green channel
|
* Display only the green channel
|
||||||
*/
|
*/
|
||||||
module.exports = function GreenChannel(options) {
|
module.exports = function GreenChannel(options,UI) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Invert Colors";
|
options.title = "Invert Colors";
|
||||||
options.description = "Inverts the colors of the image";
|
options.description = "Inverts the colors of the image";
|
||||||
|
UI.onSetup();
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
//function setup() {} // optional
|
//function setup() {} // optional
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
this_ = this;
|
|
||||||
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
return [255-r, 255-g, 255-b, a];
|
return [255-r, 255-g, 255-b, a];
|
||||||
}
|
}
|
||||||
function output(image,datauri,mimetype){
|
function output(image,datauri,mimetype){
|
||||||
this_.output = {src:datauri,format:mimetype}
|
step.output = {src:datauri,format:mimetype};
|
||||||
|
UI.onComplete(datauri);
|
||||||
}
|
}
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
@@ -25,12 +30,14 @@ module.exports = function GreenChannel(options) {
|
|||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback
|
callback: callback
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
//setup: setup, // optional
|
//setup: setup, // optional
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
* NDVI with red filter (blue channel is infrared)
|
* NDVI with red filter (blue channel is infrared)
|
||||||
*/
|
*/
|
||||||
module.exports = function NdviRed(options) {
|
module.exports = function NdviRed(options,UI) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "NDVI for red-filtered cameras (blue is infrared)";
|
options.title = "NDVI for red-filtered cameras (blue is infrared)";
|
||||||
|
UI.onSetup();
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
this_ = this;
|
|
||||||
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
var ndvi = (b - r) / (1.00 * b + r);
|
var ndvi = (b - r) / (1.00 * b + r);
|
||||||
var x = 255 * (ndvi + 1) / 2;
|
var x = 255 * (ndvi + 1) / 2;
|
||||||
return [x, x, x, a];
|
return [x, x, x, a];
|
||||||
}
|
}
|
||||||
function output(image,datauri,mimetype){
|
function output(image,datauri,mimetype){
|
||||||
this_.output = {src:datauri,format:mimetype}
|
step.output = {src:datauri,format:mimetype};
|
||||||
|
UI.onComplete(datauri);
|
||||||
}
|
}
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
@@ -24,10 +29,13 @@ module.exports = function NdviRed(options) {
|
|||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback
|
callback: callback
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
draw: draw
|
draw: draw,
|
||||||
|
output: output,
|
||||||
|
UI:UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
module.exports = function SegmentedColormap(options) {
|
module.exports = function SegmentedColormap(options,UI) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.title = "Segmented Colormap";
|
options.title = "Segmented Colormap";
|
||||||
|
UI.onSetup();
|
||||||
var output;
|
var output;
|
||||||
|
|
||||||
function draw(input,callback) {
|
function draw(input,callback) {
|
||||||
this_ = this;
|
|
||||||
|
UI.onDraw();
|
||||||
|
const step = this;
|
||||||
|
|
||||||
function changePixel(r, g, b, a) {
|
function changePixel(r, g, b, a) {
|
||||||
var ndvi = (b - r) / (r + b);
|
var ndvi = (b - r) / (r + b);
|
||||||
var normalized = (ndvi + 1) / 2;
|
var normalized = (ndvi + 1) / 2;
|
||||||
@@ -13,7 +17,8 @@ module.exports = function SegmentedColormap(options) {
|
|||||||
return [res[0], res[1], res[2], 255];
|
return [res[0], res[1], res[2], 255];
|
||||||
}
|
}
|
||||||
function output(image,datauri,mimetype){
|
function output(image,datauri,mimetype){
|
||||||
this_.output = {src:datauri,format:mimetype}
|
step.output = {src:datauri,format:mimetype};
|
||||||
|
UI.onComplete(datauri);
|
||||||
}
|
}
|
||||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
output: output,
|
output: output,
|
||||||
@@ -22,11 +27,13 @@ module.exports = function SegmentedColormap(options) {
|
|||||||
image: options.image,
|
image: options.image,
|
||||||
callback: callback
|
callback: callback
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
options: options,
|
options: options,
|
||||||
draw: draw,
|
draw: draw,
|
||||||
output: output
|
output: output,
|
||||||
|
UI: UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ module.exports = function PixelManipulation(image, options) {
|
|||||||
options = options || {};
|
options = options || {};
|
||||||
options.changePixel = options.changePixel || function changePixel(r, g, b, a) {
|
options.changePixel = options.changePixel || function changePixel(r, g, b, a) {
|
||||||
return [r, g, b, a];
|
return [r, g, b, a];
|
||||||
}
|
};
|
||||||
var getPixels = require("get-pixels"),
|
var getPixels = require('get-pixels'),
|
||||||
savePixels = require("save-pixels"),
|
savePixels = require('save-pixels');
|
||||||
base64 = require('base64-stream');
|
|
||||||
|
|
||||||
getPixels(image.src, function(err, pixels) {
|
getPixels(image.src, function(err, pixels) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
console.log("Bad image path")
|
console.log('Bad image path');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate through pixels;
|
// iterate through pixels;
|
||||||
@@ -25,7 +24,7 @@ module.exports = function PixelManipulation(image, options) {
|
|||||||
for(var x = 0; x < pixels.shape[0]; x++) {
|
for(var x = 0; x < pixels.shape[0]; x++) {
|
||||||
for(var y = 0; y < pixels.shape[1]; y++) {
|
for(var y = 0; y < pixels.shape[1]; y++) {
|
||||||
|
|
||||||
pixel = options.changePixel(
|
var pixel = options.changePixel(
|
||||||
pixels.get(x, y, 0),
|
pixels.get(x, y, 0),
|
||||||
pixels.get(x, y, 1),
|
pixels.get(x, y, 1),
|
||||||
pixels.get(x, y, 2),
|
pixels.get(x, y, 2),
|
||||||
@@ -40,19 +39,22 @@ module.exports = function PixelManipulation(image, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.format = "jpeg";
|
|
||||||
|
|
||||||
// there may be a more efficient means to encode an image object,
|
// there may be a more efficient means to encode an image object,
|
||||||
// but node modules and their documentation are essentially arcane on this point
|
// but node modules and their documentation are essentially arcane on this point
|
||||||
w = base64.encode();
|
var chunks = [];
|
||||||
var r = savePixels(pixels, options.format);
|
var totalLength = 0;
|
||||||
r.pipe(w).on('finish',function(){
|
var r = savePixels(pixels, options.format, {quality: 100});
|
||||||
data = w.read().toString();
|
|
||||||
datauri = 'data:image/' + options.format + ';base64,' + data;
|
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/' + options.format + ';base64,' + data;
|
||||||
if (options.output) options.output(options.image,datauri,options.format);
|
if (options.output) options.output(options.image,datauri,options.format);
|
||||||
if (options.callback) options.callback();
|
if (options.callback) options.callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ var test = require('tape');
|
|||||||
|
|
||||||
require('../src/ImageSequencer.js');
|
require('../src/ImageSequencer.js');
|
||||||
|
|
||||||
var sequencer = ImageSequencer({ ui: "none" });
|
var sequencer = ImageSequencer({ ui: false });
|
||||||
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||||
|
|
||||||
test('loadImages/loadImage has a name generator.', function (t){
|
test('loadImages/loadImage has a name generator.', function (t){
|
||||||
@@ -17,22 +17,27 @@ test('loadImages/loadImage has a name generator.', function (t){
|
|||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loadImages/loadImage returns a wrapper.', function (t){
|
test('loadImages/loadImage returns a wrapper in the callback.', function (t){
|
||||||
var returnval = sequencer.loadImage(red);
|
sequencer.loadImage(red, function() {
|
||||||
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
var returnval = this;
|
||||||
t.equal(returnval.images[0],"image2","Image scope is defined");
|
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
||||||
t.end();
|
t.equal(returnval.images[0],"image2","Image scope is defined");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addSteps is two-way chainable.', function (t){
|
test('addSteps is two-way chainable.', function (t){
|
||||||
var returnval = sequencer.loadImage(red).addSteps('invert');
|
sequencer.loadImage(red, function(){
|
||||||
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
var returnval = this;
|
||||||
t.equal(returnval.images[0],"image3","Image scope is defined");
|
this.addSteps('invert');
|
||||||
t.equal(sequencer.images.image3.steps.length,2,"Loaded image is affected");
|
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
||||||
t.equal(sequencer.images.image3.steps[1].options.name,"invert","Correct Step Added");
|
t.equal(returnval.images[0],"image3","Image scope is defined");
|
||||||
t.equal(sequencer.images.image2.steps.length,1,"Other images are not affected");
|
t.equal(sequencer.images.image3.steps.length,2,"Loaded image is affected");
|
||||||
t.equal(sequencer.images.image1.steps.length,1,"Other images are not affected");
|
t.equal(sequencer.images.image3.steps[1].options.name,"invert","Correct Step Added");
|
||||||
t.end();
|
t.equal(sequencer.images.image2.steps.length,1,"Other images are not affected");
|
||||||
|
t.equal(sequencer.images.image1.steps.length,1,"Other images are not affected");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addSteps is two-way chainable without loadImages.', function (t){
|
test('addSteps is two-way chainable without loadImages.', function (t){
|
||||||
@@ -44,11 +49,14 @@ test('addSteps is two-way chainable without loadImages.', function (t){
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('removeSteps is two-way chainable.', function (t){
|
test('removeSteps is two-way chainable.', function (t){
|
||||||
var returnval = sequencer.loadImage(red).addSteps('invert').removeSteps(1);
|
sequencer.loadImage(red,function(){
|
||||||
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
var returnval = this;
|
||||||
t.equal(returnval.images[0],"image4","Image scope is defined");
|
this.addSteps('invert').removeSteps(1);
|
||||||
t.equal(sequencer.images.image4.steps.length,1);
|
t.equal(returnval.name,"ImageSequencer Wrapper", "Wrapper is returned");
|
||||||
t.end();
|
t.equal(returnval.images[0],"image4","Image scope is defined");
|
||||||
|
t.equal(sequencer.images.image4.steps.length,1);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('removeSteps is two-way chainable without loadImages.', function (t){
|
test('removeSteps is two-way chainable without loadImages.', function (t){
|
||||||
@@ -59,12 +67,15 @@ test('removeSteps is two-way chainable without loadImages.', function (t){
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('insertSteps is two-way chainable.', function (t){
|
test('insertSteps is two-way chainable.', function (t){
|
||||||
var returnval = sequencer.loadImage(red).insertSteps(1,'invert');
|
sequencer.loadImage(red,function() {
|
||||||
t.equal(returnval.name,"ImageSequencer Wrapper","Wrapper is returned");
|
var returnval = this;
|
||||||
t.equal(returnval.images[0],"image5","Image scope is defined");
|
this.insertSteps(1,'invert');
|
||||||
t.equal(sequencer.images.image5.steps.length,2);
|
t.equal(returnval.name,"ImageSequencer Wrapper","Wrapper is returned");
|
||||||
t.equal(sequencer.images.image5.steps[1].options.name,"invert","Correct Step Inserrted");
|
t.equal(returnval.images[0],"image5","Image scope is defined");
|
||||||
t.end();
|
t.equal(sequencer.images.image5.steps.length,2);
|
||||||
|
t.equal(sequencer.images.image5.steps[1].options.name,"invert","Correct Step Inserrted");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('insertSteps is two-way chainable without loadImages.', function (t){
|
test('insertSteps is two-way chainable without loadImages.', function (t){
|
||||||
|
|||||||
@@ -7,12 +7,20 @@ var test = require('tape');
|
|||||||
|
|
||||||
require('../src/ImageSequencer.js');
|
require('../src/ImageSequencer.js');
|
||||||
|
|
||||||
var sequencer = ImageSequencer({ ui: "none" });
|
//require image files as DataURLs so they can be tested alike on browser and Node.
|
||||||
var image = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
var sequencer = ImageSequencer({ ui: false });
|
||||||
sequencer.loadImages(image);
|
|
||||||
|
|
||||||
|
var test_png = require('../examples/test.png.js');
|
||||||
|
var test_gif = require('../examples/test.gif.js');
|
||||||
|
|
||||||
|
sequencer.loadImages(test_png);
|
||||||
sequencer.addSteps(['do-nothing-pix','invert','invert']);
|
sequencer.addSteps(['do-nothing-pix','invert','invert']);
|
||||||
sequencer.run();
|
|
||||||
|
test("Preload", function(t) {
|
||||||
|
sequencer.run(function(){
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("Inverted image isn't identical", function (t) {
|
test("Inverted image isn't identical", function (t) {
|
||||||
t.notEqual(sequencer.images.image1.steps[1].output.src, sequencer.images.image1.steps[2].output.src);
|
t.notEqual(sequencer.images.image1.steps[1].output.src, sequencer.images.image1.steps[2].output.src);
|
||||||
@@ -23,3 +31,21 @@ test("Twice inverted image is identical to original image", function (t) {
|
|||||||
t.equal(sequencer.images.image1.steps[1].output.src, sequencer.images.image1.steps[3].output.src);
|
t.equal(sequencer.images.image1.steps[1].output.src, sequencer.images.image1.steps[3].output.src);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("PixelManipulation works for PNG images", function (t) {
|
||||||
|
sequencer.loadImages(test_png,function(){
|
||||||
|
this.addSteps('invert').run(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(function(out){
|
||||||
|
t.equal(1,1)
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -8,7 +8,24 @@ var test = require('tape');
|
|||||||
|
|
||||||
require('../src/ImageSequencer.js');
|
require('../src/ImageSequencer.js');
|
||||||
|
|
||||||
var sequencer = ImageSequencer({ ui: "none" });
|
// This function is used to test whether or not any additional global variables are being created
|
||||||
|
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) return b;
|
||||||
|
else return i;
|
||||||
|
}
|
||||||
|
var parent = (typeof(global)==="undefined")?window:global;
|
||||||
|
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";
|
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) {
|
||||||
@@ -23,18 +40,22 @@ test('loadImages loads a DataURL image and creates a step.', function (t){
|
|||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loadImages loads a PATH image and creates a step. (NodeJS)', function (t){
|
if(!sequencer.options.inBrowser)
|
||||||
if(sequencer.options.inBrowser){
|
test('loadImage loads an image from URL and creates a step. (NodeJS)', function (t){
|
||||||
t.equal("not applicable","not applicable","Not applicable for Browser");
|
sequencer.loadImage('URL','https://ccpandhare.github.io/image-sequencer/examples/red.jpg', function(){
|
||||||
t.end();
|
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");
|
||||||
else {
|
t.end();
|
||||||
sequencer.loadImages(red);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!sequencer.options.inBrowser)
|
||||||
|
test('loadImages loads an image from PATH and creates a step. (NodeJS)', function (t){
|
||||||
|
sequencer.loadImages('examples/red.jpg');
|
||||||
t.equal(sequencer.images.image1.steps.length, 1, "Initial Step Created");
|
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.equal(typeof(sequencer.images.image1.steps[0].output.src), "string", "Initial output exists");
|
||||||
t.end();
|
t.end();
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
test('loadImage works too.', function (t){
|
test('loadImage works too.', function (t){
|
||||||
sequencer.loadImage('test2',red);
|
sequencer.loadImage('test2',red);
|
||||||
@@ -138,3 +159,9 @@ test('replaceImage returns false in NodeJS', function (t) {
|
|||||||
t.equal(returnvalue,false,"It does.");
|
t.equal(returnvalue,false,"It does.");
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var global2 = copy(false,parent);
|
||||||
|
test('No Global Variables Created', function (t) {
|
||||||
|
t.equal(0,global2,"None Created.");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user