From 2256fa7fbac7426ac05d4356a4741e859778a475 Mon Sep 17 00:00:00 2001 From: jywarren Date: Sat, 4 Mar 2017 11:18:11 -0500 Subject: [PATCH 1/3] continued test expansions --- dist/image-sequencer.js | 28 +++++++++++++++++----------- package.json | 2 +- src/modules/GreenChannel.js | 6 +++++- src/modules/ImageSelect.js | 6 ++++-- src/modules/ImageThreshold.js | 8 ++------ src/modules/NdviRed.js | 7 +++++-- src/modules/Plot.js | 1 + src/modules/Resize.js | 7 +++++-- test/image-sequencer.js | 11 +++++++++-- 9 files changed, 49 insertions(+), 27 deletions(-) diff --git a/dist/image-sequencer.js b/dist/image-sequencer.js index caf3c8023b..40011fd943 100644 --- a/dist/image-sequencer.js +++ b/dist/image-sequencer.js @@ -184263,9 +184263,12 @@ module.exports = function GreenChannel(options) { options.title = "Green channel only"; options.description = "Displays only the green channel of an image"; + var image; + //function setup() {} // optional - function draw(image) { + function draw(_image) { + image = _image; function changePixel(r, g, b, a) { return [0, g, 0, a]; } @@ -184277,6 +184280,7 @@ module.exports = function GreenChannel(options) { return { options: options, + image: image, //setup: setup, // optional draw: draw } @@ -184351,13 +184355,15 @@ module.exports = function ImageSelect(options) { } // this module is unique because it creates the image - function draw(image) { - el.html(image); + function draw(_image) { + image = _image; + options.el.html(image); if (options.output) options.output(image); } return { options: options, + image: image, draw: draw, setup: setup } @@ -184402,14 +184408,10 @@ module.exports = function ImageThreshold(options) { }); } - function get() { - return image; - } - return { options: options, - draw: draw, - get: get + image: image, + draw: draw } } @@ -184421,15 +184423,17 @@ module.exports = function NdviRed(options) { options = options || {}; options.title = "NDVI for red-filtered cameras (blue is infrared)"; + var image; //function setup() {} // optional - function draw(image) { + function draw(_image) { + image = _image; function changePixel(r, g, b, a) { var ndvi = 255 * (b - r) / (1.00 * b + r); return [ndvi, ndvi, ndvi, a]; } - return require('./PixelManipulation.js')(image, { + require('./PixelManipulation.js')(image, { output: options.output, changePixel: changePixel }); @@ -184437,6 +184441,7 @@ module.exports = function NdviRed(options) { return { options: options, + image: image, //setup: setup, // optional draw: draw } @@ -184576,6 +184581,7 @@ module.exports = function Plot(options) { return { options: options, + image: image, draw: draw } } diff --git a/package.json b/package.json index 0021c47bc2..d2251c0a8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "image-sequencer", - "version": "0.0.1", + "version": "0.1.0", "description": "A modular JavaScript image manipulation library modeled on a storyboard.", "main": "dist/image-sequencer.js", "scripts": { diff --git a/src/modules/GreenChannel.js b/src/modules/GreenChannel.js index 28a887f031..5e06f32fce 100644 --- a/src/modules/GreenChannel.js +++ b/src/modules/GreenChannel.js @@ -7,9 +7,12 @@ module.exports = function GreenChannel(options) { options.title = "Green channel only"; options.description = "Displays only the green channel of an image"; + var image; + //function setup() {} // optional - function draw(image) { + function draw(_image) { + image = _image; function changePixel(r, g, b, a) { return [0, g, 0, a]; } @@ -21,6 +24,7 @@ module.exports = function GreenChannel(options) { return { options: options, + image: image, //setup: setup, // optional draw: draw } diff --git a/src/modules/ImageSelect.js b/src/modules/ImageSelect.js index d375fd79c5..40fc6c09bc 100644 --- a/src/modules/ImageSelect.js +++ b/src/modules/ImageSelect.js @@ -66,13 +66,15 @@ module.exports = function ImageSelect(options) { } // this module is unique because it creates the image - function draw(image) { - el.html(image); + function draw(_image) { + image = _image; + options.el.html(image); if (options.output) options.output(image); } return { options: options, + image: image, draw: draw, setup: setup } diff --git a/src/modules/ImageThreshold.js b/src/modules/ImageThreshold.js index 65b304efbf..54fdaa5e96 100644 --- a/src/modules/ImageThreshold.js +++ b/src/modules/ImageThreshold.js @@ -35,13 +35,9 @@ module.exports = function ImageThreshold(options) { }); } - function get() { - return image; - } - return { options: options, - draw: draw, - get: get + image: image, + draw: draw } } diff --git a/src/modules/NdviRed.js b/src/modules/NdviRed.js index b42763bfcf..496f8a058f 100644 --- a/src/modules/NdviRed.js +++ b/src/modules/NdviRed.js @@ -5,15 +5,17 @@ module.exports = function NdviRed(options) { options = options || {}; options.title = "NDVI for red-filtered cameras (blue is infrared)"; + var image; //function setup() {} // optional - function draw(image) { + function draw(_image) { + image = _image; function changePixel(r, g, b, a) { var ndvi = 255 * (b - r) / (1.00 * b + r); return [ndvi, ndvi, ndvi, a]; } - return require('./PixelManipulation.js')(image, { + require('./PixelManipulation.js')(image, { output: options.output, changePixel: changePixel }); @@ -21,6 +23,7 @@ module.exports = function NdviRed(options) { return { options: options, + image: image, //setup: setup, // optional draw: draw } diff --git a/src/modules/Plot.js b/src/modules/Plot.js index 9e2af7929c..f2f5741027 100644 --- a/src/modules/Plot.js +++ b/src/modules/Plot.js @@ -68,6 +68,7 @@ module.exports = function Plot(options) { return { options: options, + image: image, draw: draw } } diff --git a/src/modules/Resize.js b/src/modules/Resize.js index f80da85e50..68b55f7de7 100644 --- a/src/modules/Resize.js +++ b/src/modules/Resize.js @@ -2,16 +2,18 @@ * Resize image */ module.exports = function Resize(options) { + + options = options || {}; options.title = "Resize image"; options.description = "Resizes image to given width"; - options = options || {}; + var image; function setup() { } - function draw(image) { + function draw(_image) { @@ -19,6 +21,7 @@ module.exports = function Resize(options) { return { options: options, + image: image, setup: setup, draw: draw } diff --git a/test/image-sequencer.js b/test/image-sequencer.js index 1a91043c0e..9b6693f441 100644 --- a/test/image-sequencer.js +++ b/test/image-sequencer.js @@ -27,7 +27,9 @@ test('Image Sequencer has tests', function (t) { test('addStep adds a step', function (t) { t.equal(sequencer.steps.length, 0); sequencer.addStep('ndvi-red'); - t.equal(sequencer.steps.length, 1); + sequencer.addStep('green-channel'); + sequencer.addStep('image-threshold'); + t.equal(sequencer.steps.length, 3); t.end(); }); @@ -37,13 +39,18 @@ test('each module conforms to base API except image-select', function (t) { }); // should already have image-select: t.equal(sequencer.steps.length, Object.keys(sequencer.modules).length); + var images = []; sequencer.steps.forEach(function(step, i) { //t.equal(step.test(step.testInput),step.testOutput); // or check that it's equal with a diff method? // we could also test each type of output - t.equal(step.setup === 'undefined', false); t.equal(step.draw === 'undefined', false); + step.options.output = function moduleOutput(img) { images.push(image); } + t.equal(step.draw(image)); }); + +//and be sure they're all real images + t.equal(images.length, steps.length); t.end(); }); From d8216c60d73e75a68c09eab21e02d49217ef859c Mon Sep 17 00:00:00 2001 From: jywarren Date: Sat, 4 Mar 2017 13:17:49 -0500 Subject: [PATCH 2/3] module image pass-through tests, but image-filter-core module not compatible with node-canvas --- Gruntfile.js | 3 +++ README.md | 15 +++++++++----- dist/image-sequencer.js | 14 ++++++++----- package.json | 4 +++- src/modules/ImageThreshold.js | 12 ++++++++---- test/dancing-cactus.png | Bin 0 -> 392 bytes test/image-sequencer.js | 36 +++++++++++++++++++--------------- 7 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 test/dancing-cactus.png diff --git a/Gruntfile.js b/Gruntfile.js index df8d96662d..8cd5f53992 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,6 +28,9 @@ module.exports = function(grunt) { 'src/ImageSequencer.js' ], dest: 'dist/image-sequencer.js' + }, + options: { + exclude: [ 'canvas' ] } } diff --git a/README.md b/README.md index 1961fe7563..449d7902be 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Image Sequencer aka "Consequencer" -[![Build Status](https://travis-ci.org/jywarren/image-sequencer.svg?branch=master)](https://travis-ci.org/jywarren/image-sequencer) +[![Build Status](https://travis-ci.org/publiclab/image-sequencer.svg?branch=master)](https://travis-ci.org/publiclab/image-sequencer) ## Why @@ -25,8 +25,8 @@ It is also for prototyping some other related ideas: Examples: -* [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) +* [Basic example](https://publiclab.github.io/image-sequencer/) +* [NDVI example](https://publiclab.github.io/image-sequencer/examples/ndvi/) - related to [Infragram.org](http://infragram.org) ## Contributing @@ -62,7 +62,7 @@ For display in the web-based UI, each module may also have a title like `options #### Module example -See existing module `green-channel` for an example: https://github.com/jywarren/image-sequencer/tree/master/src/modules/GreenChannel.js +See existing module `green-channel` for an example: https://github.com/publiclab/image-sequencer/tree/master/src/modules/GreenChannel.js For help integrating, please open an issue. @@ -94,7 +94,7 @@ Notes on development next steps: * [ ] 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 + * see https://github.com/publiclab/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 @@ -103,8 +103,13 @@ Notes on development next steps: ### Testing * [ ] tests - modules headless; unit tests + * some modules won't work headlessly; make this part of the required API + * plotly experimental headless: https://gist.github.com/etpinard/bee7d62b43b6bb286950 * [ ] 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 +* lots of classic test images are problematic, objectifying and sexist: http://www.hlevkin.com/06testimages.htm + * more http://sipi.usc.edu/database/ + * https://en.wikipedia.org/wiki/Standard_test_image ### Use cases diff --git a/dist/image-sequencer.js b/dist/image-sequencer.js index 40011fd943..ef0226fe86 100644 --- a/dist/image-sequencer.js +++ b/dist/image-sequencer.js @@ -184384,11 +184384,15 @@ module.exports = function ImageThreshold(options) { function draw(inputImage) { $(inputImage).load(function(){ - var canvas = document.createElement('canvas'); - canvas.width = inputImage.naturalWidth; - canvas.height = inputImage.naturalHeight; + if (typeof window !== 'undefined') var canvas = document.createElement('canvas'); + else { + var Canvas = require("canvas"); + var canvas = new Canvas(inputImage.width, inputImage.height); + } + canvas.width = inputImage.naturalWidth || inputImage.width; // node-canvas doesn't provide naturalWidth + canvas.height = inputImage.naturalHeight || inputImage.height; var context = canvas.getContext('2d'); - context.drawImage(inputImage, 0, 0); + context.drawImage(inputImage, 0, 0 ); var imageData = context.getImageData(0, 0, canvas.width, canvas.height); @@ -184415,7 +184419,7 @@ module.exports = function ImageThreshold(options) { } } -},{"image-filter-core":85,"image-filter-threshold":86}],1613:[function(require,module,exports){ +},{"canvas":undefined,"image-filter-core":85,"image-filter-threshold":86}],1613:[function(require,module,exports){ /* * NDVI with red filter (blue channel is infrared) */ diff --git a/package.json b/package.json index d2251c0a8a..f999974289 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A modular JavaScript image manipulation library modeled on a storyboard.", "main": "dist/image-sequencer.js", "scripts": { - "test": "tape test/*.js" + "test": "tape test/*.js | tap-summary" }, "repository": { "type": "git", @@ -25,6 +25,7 @@ "jquery": "~2" }, "devDependencies": { + "canvas": "~1.6.4", "get-pixels": "~3.3.0", "save-pixels": "~2.3.4", "base64-stream": "~0.1.3", @@ -34,6 +35,7 @@ "image-filter-core": "~1.0.0", "tape": "^3.5.0", + "tap-summary": "~3.0.1", "browserify": "13.0.0", "grunt": "^0.4.5", "grunt-browserify": "^5.0.0", diff --git a/src/modules/ImageThreshold.js b/src/modules/ImageThreshold.js index 54fdaa5e96..686a0d00af 100644 --- a/src/modules/ImageThreshold.js +++ b/src/modules/ImageThreshold.js @@ -11,11 +11,15 @@ module.exports = function ImageThreshold(options) { function draw(inputImage) { $(inputImage).load(function(){ - var canvas = document.createElement('canvas'); - canvas.width = inputImage.naturalWidth; - canvas.height = inputImage.naturalHeight; + if (typeof window !== 'undefined') var canvas = document.createElement('canvas'); + else { + var Canvas = require("canvas"); + var canvas = new Canvas(inputImage.width, inputImage.height); + } + canvas.width = inputImage.naturalWidth || inputImage.width; // node-canvas doesn't provide naturalWidth + canvas.height = inputImage.naturalHeight || inputImage.height; var context = canvas.getContext('2d'); - context.drawImage(inputImage, 0, 0); + context.drawImage(inputImage, 0, 0 ); var imageData = context.getImageData(0, 0, canvas.width, canvas.height); diff --git a/test/dancing-cactus.png b/test/dancing-cactus.png new file mode 100644 index 0000000000000000000000000000000000000000..6f8ca5772b111c3243bb07eb2ad84b4548028c5e GIT binary patch literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@mUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UV>C6dj$D1FjT2AFf_C$!z|2Mjn^0({Q;)W79nyS)DJl{c@`vLsS&hFNXoe$;uIRVi&( z(c%kjJXiKPxzt}=Vl0%iV!fJ#LWF|+jLc&X%@4Th zER%S@X*~mam_fC~HKHUXu_VKYq_7@Aud8d@1!=o*+>85nT# kZ`MH3kei>9nO2FZ!N?S%;i`VtDWC=hPgg&ebxsLQ03kDSB>(^b literal 0 HcmV?d00001 diff --git a/test/image-sequencer.js b/test/image-sequencer.js index 9b6693f441..e300a82dbc 100644 --- a/test/image-sequencer.js +++ b/test/image-sequencer.js @@ -6,9 +6,7 @@ var test = require('tape'); // We should only test headless code here. // http://stackoverflow.com/questions/21358015/error-jquery-requires-a-window-with-a-document#25622933 -require('../dist/image-sequencer.js'); - -var sequencer = ImageSequencer({ ui: false }); +require('../src/ImageSequencer.js'); //function read (file) { // return fs.readFileSync('./test/fixtures/' + file, 'utf8').trim(); @@ -18,13 +16,10 @@ var sequencer = ImageSequencer({ ui: false }); // return fs.writeFileSync('./test/fixtures/' + file, data + '\n', 'utf8'); //} -test('Image Sequencer has tests', function (t) { - // read('something.html') - t.equal(true, true); - t.end(); -}); +// read('something.html') test('addStep adds a step', function (t) { + var sequencer = ImageSequencer({ ui: false }); t.equal(sequencer.steps.length, 0); sequencer.addStep('ndvi-red'); sequencer.addStep('green-channel'); @@ -33,24 +28,33 @@ test('addStep adds a step', function (t) { t.end(); }); -test('each module conforms to base API except image-select', function (t) { +test('each core module has a draw() method which outputs an image via options.output()', function (t) { + t.plan(12); + var sequencer = ImageSequencer({ ui: false }); + t.equal(sequencer.steps.length, 0); Object.keys(sequencer.modules).forEach(function(moduleName, i) { - if (moduleName != "image-select") sequencer.addStep(moduleName); + // some modules don't work headlessly; we should make stating this a module API requirement + if (moduleName !== "image-select" && moduleName !== "plot") sequencer.addStep(moduleName); }); // should already have image-select: - t.equal(sequencer.steps.length, Object.keys(sequencer.modules).length); + t.equal(sequencer.steps.length, Object.keys(sequencer.modules).length - 2); var images = []; + var Image = require("canvas").Image; + var image = new Image(); + // dancing cactus test image: + image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAxQTFRFAAAABQkEaqiYV8E6eKx7SQAAAAF0Uk5TAEDm2GYAAAABYktHRAH/Ai3eAAAACXBIWXMAAABIAAAASABGyWs+AAAAXUlEQVQY04XOoQ7AMAgEUEzNTH/tDAbTr8PU7Otmahhk2VY6sQviicsFIma6cqPQhgzUvkJEf9GkMxJsqK8mGCBYIB+YacbhN6HUgD83w1pCtPcxgz3SlV/4UhgPToo5Yg32KuZBAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTAxLTMwVDE3OjExOjM4LTA1OjAwCQ+zKAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wMS0zMFQxNzoxMToyNS0wNTowMNUvasoAAAAASUVORK5CYII="; sequencer.steps.forEach(function(step, i) { - //t.equal(step.test(step.testInput),step.testOutput); - // or check that it's equal with a diff method? - // we could also test each type of output t.equal(step.draw === 'undefined', false); step.options.output = function moduleOutput(img) { images.push(image); } t.equal(step.draw(image)); }); - -//and be sure they're all real images t.equal(images.length, steps.length); + // and be sure they're all real images + images.forEach(function forEachImage(img) { + t.equal('Image', typeof img); + t.ok(img.src); + }); +console.log("GOT HERE") t.end(); }); From 3d43eceff9a51ed2f85c3ce42277017c7890e435 Mon Sep 17 00:00:00 2001 From: jywarren Date: Mon, 6 Mar 2017 14:29:49 -0500 Subject: [PATCH 3/3] more work on tests and shimming Canvas --- README.md | 2 ++ dist/image-sequencer.js | 5 +++-- src/modules/GreenChannel.js | 2 +- src/modules/PixelManipulation.js | 3 ++- test/image-sequencer.js | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 449d7902be..fa0a82d60b 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ For help integrating, please open an issue. Notes on development next steps: +* use `Marty.isBrowser` + ### UI * [ ] add createUserInterface() which is set up by default to draw on ImageBoardUI, but could be swapped for nothing, or an equiv. lib diff --git a/dist/image-sequencer.js b/dist/image-sequencer.js index ef0226fe86..7903bc4eed 100644 --- a/dist/image-sequencer.js +++ b/dist/image-sequencer.js @@ -184272,7 +184272,7 @@ module.exports = function GreenChannel(options) { function changePixel(r, g, b, a) { return [0, g, 0, a]; } - return require('./PixelManipulation.js')(image, { + return require('./PixelManipulation.js')(_image, { output: options.output, changePixel: changePixel }); @@ -184471,7 +184471,8 @@ module.exports = function PixelManipulation(image, options) { getPixels(image.src, function(err, pixels) { if(err) { - console.log("Bad image path") +console.log('image src',image.src) + console.log("getPixels: Bad image path") return } diff --git a/src/modules/GreenChannel.js b/src/modules/GreenChannel.js index 5e06f32fce..0d2f5f40d7 100644 --- a/src/modules/GreenChannel.js +++ b/src/modules/GreenChannel.js @@ -16,7 +16,7 @@ module.exports = function GreenChannel(options) { function changePixel(r, g, b, a) { return [0, g, 0, a]; } - return require('./PixelManipulation.js')(image, { + return require('./PixelManipulation.js')(_image, { output: options.output, changePixel: changePixel }); diff --git a/src/modules/PixelManipulation.js b/src/modules/PixelManipulation.js index de7b2a70e8..4aea7be96a 100644 --- a/src/modules/PixelManipulation.js +++ b/src/modules/PixelManipulation.js @@ -17,7 +17,8 @@ module.exports = function PixelManipulation(image, options) { getPixels(image.src, function(err, pixels) { if(err) { - console.log("Bad image path") +console.log('image src',image.src) + console.log("getPixels: Bad image path") return } diff --git a/test/image-sequencer.js b/test/image-sequencer.js index e300a82dbc..4ca8aa46ae 100644 --- a/test/image-sequencer.js +++ b/test/image-sequencer.js @@ -48,7 +48,7 @@ test('each core module has a draw() method which outputs an image via options.ou step.options.output = function moduleOutput(img) { images.push(image); } t.equal(step.draw(image)); }); - t.equal(images.length, steps.length); + t.equal(images.length, sequencer.steps.length); // and be sure they're all real images images.forEach(function forEachImage(img) { t.equal('Image', typeof img);