Compare commits

...

136 Commits

Author SHA1 Message Date
Jeffrey Warren
ee4ba6fb41 Update diagram 2019-03-22 15:53:17 -04:00
Jeffrey Warren
ca8b7ecb95 Update CONTRIBUTING.md 2019-03-22 14:37:22 -04:00
Harshith pabbati
33dcb5794f clear cache offline button (#910) 2019-03-21 18:51:36 -04:00
Harshith pabbati
9ffd60c707 Seo tags for google search (#914)
* Seo tags for google search

* changes

* changes
2019-03-21 16:50:24 -04:00
Harsh Khandeparkar
32bd372139 Minify JS for production (#897)
* Create prod.js

* Delete prod.js

* add production task

* add grunt tasks to contributing file

* add default task.
2019-03-20 23:19:18 -04:00
Slytherin
bae2013243 Fixed quick buttons issue (#905) 2019-03-20 17:21:57 -04:00
Varun Gupta
cb53efbe21 redid import-image and fix ExportBin fixes #899 (#900)
* redid import-image and fix ExportBin fixes #899

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

* 3.0.1

* bypass import-image

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2019-03-20 13:53:02 -04:00
MAYANK CHHIPA
21ff486618 Add Dropdown Transitions (#881)
* Add Dropdown Transitions

* Add Dropdown Transitions

* Update defaultHtmlStepUi.js

* Add Dropdown Transitions

* Add Dropdown Transitions

* Add Dropdown Transitions
2019-03-19 15:28:05 -04:00
Harsh Khandeparkar
951afdbc4f add grunt build to setup script (#895) 2019-03-19 11:50:09 -04:00
aashna27
709df84cb2 Corrected view (#867) 2019-03-19 11:41:39 -04:00
Harsh Khandeparkar
f852730d98 Is Nightly Completed (#893)
* beta demo

* config.yml

* Update config.yml

* Update ISSUE_TEMPLATE.md

* Update package.json

* mistake

* mistake
2019-03-19 10:25:06 -04:00
Vibhor Gupta
e5cf6a311f Add: Exposure Module (#721)
* exposure module

* requested changes

* update module
2019-03-19 08:02:28 -04:00
Harsh Khandeparkar
b8beec96be Revise Gruntfile (#887) 2019-03-18 19:10:03 -04:00
Slytherin
5e33c69736 Add search functionality in choose Module select (#750)
* Add Search functionality in choose functionalityoption

* Adds grunt build changes

* Adds node module for selectize and changed path
2019-03-18 17:28:19 -04:00
Slytherin
bd68245a12 Update Readme.md that includes importJSON section (#744)
* Update Readme.md that includes importJSON docs

* Adds command JSON object step addition format to README.md

* Added reference to the appropriate test

* Resolved missing rebase

* Deleted images in test_outputs directory and put it in .gitignore file
2019-03-18 16:37:44 -04:00
Harsh Khandeparkar
c6457323cc Complete Edge Detect Module (#771)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* fix insert step button

* add icon

* add dist

* use normal functions

* use normal functions

* add new function

* add dist

* remove console logs

* changes

* refactor

* add dist

* Bump looks-same from 5.0.2 to 6.0.0 (#729)

Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 5.0.2 to 6.0.0.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v5.0.2...v6.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Setup UI testing (#720)

* default sequencer ui test

* default step ui test suite

* intermediate step ui test

* preview ui test suite

* url methods test suite

* add set url params method test suite

* argument call tests

* test directory refactor

* travis fix

* CLI code refactor (#665)

* CLI refactor

* es6 rollback

* Travis fix

* syntax fix

* clustered require statements

* travis debug

* travis debug

* Added line that selects the "More modules..." after adding a step. (#713)

* Added line making module selection correct after adding a step

* Added line with appropriate module selection

* Add: Module tests (#748)

* WIP

* module testing harness

* adjustments

* Fix choose file option and insert step functionality (#712)

* Fix choose file option

* changes

* Parse info.json to set module defaults (#650)

* changes

* changes

* changes

* changes

* changes

* changes

* Update package-lock.json

* Bump jsqr from 1.1.1 to 1.2.0 (#749)

Bumps [jsqr](https://github.com/cozmo/jsQR) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Bump jsqr from 1.1.1 to 1.2.0 (#749)

Bumps [jsqr](https://github.com/cozmo/jsQR) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* dist

* changes

* working

* final touches

* revert mapTypes

* proper revert

* Fix arctangent

* working hysteresis

* hysteresis optional
2019-03-17 17:05:24 -04:00
aashna27
e1a113cde1 Added tests for more modules (#752) 2019-03-16 12:26:40 -04:00
MAYANK CHHIPA
24fb0ca9e4 Adds a reference to sequencer in replaceImage() (#865)
* Fixes #864

* Adds a reference to sequencer in replaceImage()
2019-03-16 12:24:32 -04:00
Harshith pabbati
a2fb2689a6 Made the flip image work in correct way (#854) 2019-03-14 19:32:29 -04:00
Harsh Prabhu
0be9e3f33b Centered the cropped image (#705)
* Updated demo.css

* Updated defaultHtmlStepUI.js

* Updated defaultHtmlStepUI.js

* Updated defaultHtmlStepUI.js

* Updated demo.css

* Updated defaultHtmlStepUi.js

* Updated defaultHtmlStepUi.js

* Added semi-colons in demo.css

* removed the style attribute from the img element
2019-03-14 19:07:18 -04:00
Jeffrey Warren
30e9f359d5 change insertPreview quick add defaults (#855) 2019-03-14 18:59:00 -04:00
maks-ostashkevich
3c1b6f2019 Change Footer Links In index.html (#848)
* Lines 202, 205, 211 have been changed in examples/index.html.

* Update examples/index.html line 211

Co-Authored-By: maks-ostashkevich <33810779+maks-ostashkevich@users.noreply.github.com>

* Update examples/index.html line 202

Co-Authored-By: maks-ostashkevich <33810779+maks-ostashkevich@users.noreply.github.com>

* Update examples/index.html line 205

Co-Authored-By: maks-ostashkevich <33810779+maks-ostashkevich@users.noreply.github.com>
2019-03-14 11:06:28 -04:00
Varun Gupta
96299ed6f7 3.0.0 (#783) 2019-03-14 11:05:15 -04:00
Slytherin
2f21bec80a Restructure API (#824)
* Change addsteps(), loadImages(), run() and default UI

* Restructure API completely

* Add updated dist files

* Removed extra comments

*  Indentation improved

* Update README.md
2019-03-14 11:04:02 -04:00
dependabot[bot]
3d1bbe5940 Bump looks-same from 7.0.0 to 7.1.0 (#853)
Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v7.0.0...v7.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-14 10:58:08 -04:00
Lohitha
0865bc44d2 Document the new modules and docs-link for all modules (#841)
* Document the new modules and doc-link for all modules

This commit introdueces new modules to image-sequencer
	- Dither
	- Draw Rectangle
	- Paint Bucket
	- Tint
	- Threshold
	- WhiteBalance

fixes #719

* Update docs/MODULES.md

Co-Authored-By: lohitha02 <lohithabhagam123@gmail.com>

* Update MODULES.md

* Update info.json

* Update MODULES.md

* Update info.json

* Update info.json
2019-03-08 12:18:37 -05:00
Mridul97
055b17c75f Add replace color module (#724)
* Add color separation module

* changes

* changes

* changes

* changes

* Add test
2019-03-04 19:19:39 -05:00
dependabot[bot]
bf2fd7ede6 Bump ora from 3.1.0 to 3.2.0 (#830)
Bumps [ora](https://github.com/sindresorhus/ora) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/sindresorhus/ora/releases)
- [Commits](https://github.com/sindresorhus/ora/compare/v3.1.0...v3.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-04 17:55:14 -05:00
Harsh Khandeparkar
1e08d7c1da Map types to html types (#754)
* fix insert step button

* add dist

* add new function

* remove console logs

* refactor

* Setup UI testing (#720)

* default sequencer ui test

* default step ui test suite

* intermediate step ui test

* preview ui test suite

* url methods test suite

* add set url params method test suite

* argument call tests

* test directory refactor

* travis fix

* CLI code refactor (#665)

* CLI refactor

* es6 rollback

* Travis fix

* syntax fix

* clustered require statements

* travis debug

* travis debug

* Added line that selects the "More modules..." after adding a step. (#713)

* Added line making module selection correct after adding a step

* Added line with appropriate module selection

* Add: Module tests (#748)

* WIP

* module testing harness

* adjustments

* Update package-lock.json

* Bump jsqr from 1.1.1 to 1.2.0 (#749)

Bumps [jsqr](https://github.com/cozmo/jsQR) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* refactor without spread syntax
2019-03-04 17:54:28 -05:00
Harsh Khandeparkar
ac57b32ec6 Added New FlipImage Module (#696)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* rebase and rebuild

* add dist

* flip image

* Changes

* Changes

* add dist

* missed one
2019-03-02 13:40:04 -05:00
Aneal Sharma
9f5a7bb49a Update "Installation" section of README.md (#817)
* Update "Installation" section of README.md

The following changes make the section more concise by removing unnecessary words.

* Update README.md

* Updated README.md further

-made additional grammatical and word choice improvements
2019-03-01 13:28:54 -05:00
dependabot[bot]
af033d15c6 Bump looks-same from 5.1.0 to 7.0.0 (#823)
Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 5.1.0 to 7.0.0.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v5.1.0...v7.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 10:57:48 -05:00
Mary Ramirez
273f1eb494 Update index.html (#815)
Replaced line 214 with by changing the "improve this tool" button to primary.
2019-02-27 19:25:56 -05:00
Lokesh
6a450c743e Format the "Take a Photo" button (#739)
* Update index.html

added bootstrap classes btn brn-default btn-md for Take a photo button

* changed brn-default btn-primary

* changed button color and size
2019-02-23 07:45:55 -06:00
igorwilbert
bed23c8974 Adds data model diagram in README (#811) 2019-02-22 13:29:38 -06:00
aashna27
3c5f0a2a30 Typecast removed (#802) 2019-02-21 18:21:12 -06:00
aashna27
ffed3d1af1 Corrected default view for Saturation module (#772)
* Corrected default view for Saturation module

* package lock
2019-02-21 17:14:05 -06:00
aashna27
92aaaa9a42 RGB values on hover (#808) 2019-02-21 13:38:49 -08:00
Slytherin
6dd7738476 Adds "Add QR" module (#736)
* Adds Add QR module

* Grunt build changes

* Rremoved copyPixels function and added test for QR module

* Resolved test fail issues

* Adds look same test

* Change default to get from info.json
2019-02-21 10:02:24 -08:00
Lohitha
600ddece58 Fix "Save As" overflow on small screens (#795)
* Fixed #789 issue

* Fix #789 Save As overflow on small screens
2019-02-20 17:01:42 -08:00
Vibhor Gupta
29b0571c64 Add: UI test script to Travis (#791)
* codecov

* travis ui test fix

* undo

* fix
2019-02-20 16:48:14 -08:00
aashna27
cc0c5411d9 Fixes Crop module (#803) 2019-02-20 16:29:48 -08:00
Harshith pabbati
194e6fc441 Added a footer to the main page (#776)
* Added the footer to the main page

* Added the footer with link of the github issues
2019-02-20 10:37:54 -08:00
Harshith pabbati
fdb38f7fdf Added the resize quick button (#782)
* Added the resize module

* Added the resize quick button

* Made the icon to fit into the box

* Added the footer to the main page

* Added the resize quick button

* Added the resize quick button

* Added the resize quick button
2019-02-17 18:16:15 -06:00
Slytherin
c3abdaf3aa Add sequencer.getSteps() (#777)
* create getSteps() and test it

* Adds test with new sequencer instance and updated Readme
2019-02-17 18:14:59 -06:00
Varun Gupta
761142d9d3 createMetaModule generator (#779)
* initial createMetaModule

* fixes

* fix

* Update createMetaModule.js

* Update createMetaModule.js

* refactor and fix

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

* update ndvi-colormap

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

* fix test

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

* add docs

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2019-02-16 22:33:45 +05:30
Varun Gupta
81b06d1b42 Benchmarks (#781)
* benchmarks initial try

* added test to package.json

* Update benchmark.js

* Update benchmark.js

* Update benchmark.js

* Update benchmark.js

* bigger image

* change load-Image to browser only and fix benchmark

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

* run on same image

Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2019-02-16 21:25:23 +05:30
Varun Gupta
75dc23fb55 Improve Code Quality and Optimize (#763)
Signed-off-by: tech4GT <varun.gupta1798@gmail.com>
2019-02-16 11:05:32 +05:30
Mridul97
f42aa2fd67 changes (#704) 2019-02-15 13:36:33 -05:00
dependabot[bot]
e0ed73a24e Bump bootstrap from 3.4.0 to 3.4.1 (#769)
Bumps [bootstrap](https://github.com/twbs/bootstrap) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/twbs/bootstrap/releases)
- [Commits](https://github.com/twbs/bootstrap/compare/v3.4.0...v3.4.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-14 12:29:40 -05:00
dependabot[bot]
c2bc12a112 Bump tape from 4.10.0 to 4.10.1 (#770)
Bumps [tape](https://github.com/substack/tape) from 4.10.0 to 4.10.1.
- [Release notes](https://github.com/substack/tape/releases)
- [Commits](https://github.com/substack/tape/compare/v4.10.0...v4.10.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-14 07:47:12 -05:00
Varun Gupta
fa30c1b30f Opaque meta module + test - colorbar (#764)
* opaque colorbar test

* build

* rebase + build

* fixed opaque meta modules yay

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

* add test and prevent internal logs

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

* Update meta-modules.js
2019-02-13 10:36:11 -05:00
Sand
53db6bd2ce Assigned ref.images[image].steps[i] to a variable to stop duplication (#756) 2019-02-11 19:19:41 -05:00
aashna27
f4a80a0490 Default values added for few modules (#757) 2019-02-11 19:18:49 -05:00
dependabot[bot]
9836314f1f Bump ora from 3.0.0 to 3.1.0 (#758)
Bumps [ora](https://github.com/sindresorhus/ora) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/sindresorhus/ora/releases)
- [Commits](https://github.com/sindresorhus/ora/compare/v3.0.0...v3.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-11 19:18:09 -05:00
dependabot[bot]
6e193d48fc Bump tape from 4.9.2 to 4.10.0 (#759)
Bumps [tape](https://github.com/substack/tape) from 4.9.2 to 4.10.0.
- [Release notes](https://github.com/substack/tape/releases)
- [Commits](https://github.com/substack/tape/compare/v4.9.2...v4.10.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-11 19:17:47 -05:00
igorwilbert
1a0ae054c8 New layout for Save and Add areas (#745) 2019-02-09 08:54:50 -05:00
Jeffrey Warren
b638ceb1db Change default of Rotate module to 90 (#734) 2019-02-08 17:37:40 -05:00
aashna27
f09df0d042 Fixes Decode-qr (#680) 2019-02-08 17:35:32 -05:00
jywarren
044a120468 bumped to v2.5.0 2019-02-08 13:24:20 -05:00
Mridul97
2d8b93020a Add tint module (#701)
* Add tint module

* rebased, rebuilt
2019-02-08 13:20:47 -05:00
Mridul97
90c57af332 Change threshold range (#700)
* change tolerance values

* changes
2019-02-08 13:03:20 -05:00
Harsh Khandeparkar
78cbaa262b rebuild rebase (#674) 2019-02-08 12:10:24 -05:00
aashna27
ddde6e1e2e Added Threshold (#722) 2019-02-08 11:50:45 -05:00
Harsh Khandeparkar
2d8bb53402 Used pixels.set To Refactor DrawRectangle (#690)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* use pixel.set for drawRectangle

* chnage thickness
2019-02-08 11:21:24 -05:00
dependabot[bot]
b47de40292 Bump jsqr from 1.1.1 to 1.2.0 (#749)
Bumps [jsqr](https://github.com/cozmo/jsQR) from 1.1.1 to 1.2.0.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-07 18:01:44 -05:00
jywarren
0a091a611f version bump to 2.4.0 2019-02-06 19:52:05 -05:00
Aneal Sharma
2274ce6785 Update "Why" section in README.md (#742) 2019-02-06 19:30:04 -05:00
Jeffrey Warren
58c6cbff24 Update package-lock.json 2019-02-06 16:32:01 -05:00
Mridul97
6153dbfdaa Parse info.json to set module defaults (#650)
* changes

* changes

* changes

* changes

* changes

* changes
2019-02-06 11:10:54 -05:00
Mridul97
6b305a02f5 Fix choose file option and insert step functionality (#712)
* Fix choose file option

* changes
2019-02-05 11:32:16 -05:00
Vibhor Gupta
57e4849d35 Add: Module tests (#748)
* WIP

* module testing harness

* adjustments
2019-02-05 11:31:13 -05:00
Gabriel Ting
3081dccf19 Added line that selects the "More modules..." after adding a step. (#713)
* Added line making module selection correct after adding a step

* Added line with appropriate module selection
2019-02-01 12:28:49 -05:00
David Koch
fa047ce6a9 minor fix for better readability (#730)
* minor fix for better readability

* added one '#'
2019-01-31 13:01:24 -05:00
Vibhor Gupta
d0bf9de71a CLI code refactor (#665)
* CLI refactor

* es6 rollback

* Travis fix

* syntax fix

* clustered require statements

* travis debug

* travis debug
2019-01-31 13:01:03 -05:00
Vibhor Gupta
0cd3156c06 Setup UI testing (#720)
* default sequencer ui test

* default step ui test suite

* intermediate step ui test

* preview ui test suite

* url methods test suite

* add set url params method test suite

* argument call tests

* test directory refactor

* travis fix
2019-01-31 12:56:34 -05:00
dependabot[bot]
8880a580dd Bump looks-same from 5.0.2 to 6.0.0 (#729)
Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 5.0.2 to 6.0.0.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v5.0.2...v6.0.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-30 06:00:00 -06:00
dependabot[bot]
c445070937 Bump jsdom from 13.1.0 to 13.2.0 (#709)
Bumps [jsdom](https://github.com/jsdom/jsdom) from 13.1.0 to 13.2.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/13.1.0...13.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-24 09:04:41 -03:00
dependabot[bot]
d792f21147 Update jsdom requirement from ^11.12.0 to ^13.1.0 (#703)
Updates the requirements on [jsdom](https://github.com/jsdom/jsdom) to permit the latest version.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/commits/13.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-23 21:15:24 -03:00
Nicole Ortiz García
ad60c91d6b Updating demo.css to only remove padding on sliders (#702)
* Update demo.css

removed padding only on sliders -- added the attribute selector [type="range"] on the demo.css file.

* Update demo.css

removed space between .form-control and [type="range"] -- pull request review.
2019-01-23 19:09:53 -03:00
aashna27
3c2d6542dc Added second spinner (#691) 2019-01-23 19:08:53 -03:00
atsutton
b64e7cefc0 Switch to npm for gifshot (#689)
* Switch to npm for gifshot

* Added a refreshed package-lock.json

* Updated package.json and package-lock.json

* Cleared cache, resync with trunk
2019-01-23 19:08:11 -03:00
Harpinder
eb3b3df800 Changed New PR Welcome Comment issue: #684 (#688) 2019-01-22 18:16:13 -04:00
Mridul97
0e6e1837a6 Use browserify for ui js files and refactor ui js files (#668)
* changes

* changes

* changes
2019-01-22 18:15:23 -04:00
Maseu
dc361a3ab8 Fixed Import Image Text Overflow (#695)
* Replace dropzone id with dropzone class

* Added dropzone class to div container

* Updated dropZone listener

* Changed #dropzone input to .dropzone input
2019-01-22 16:26:04 -04:00
Slytherin
3d2d7844b6 Added thumbnails to insert preview. (#653)
* Adds thumbnail to insert step view

* removed redundant code

* Resolved merge conflicts

* Resolved merge conflicts

* made changes in intermediateHtmlStepUi

* Update intermediateHtmlStepUi.js
2019-01-22 16:24:59 -04:00
Slytherin
8fd4efc621 Add tests for notify() (#686)
* Added HTML-UI test

* Added test for notify()

* Updated Readme's
2019-01-22 10:43:39 -04:00
Harsh Prabhu
413a235594 updated PULL-REQUEST-TEMPLATE.md (#682)
* updated PULL-REQUEST-TEMPLATE.md

* Updated PULL-REQUEST_TEMPLATE.md
2019-01-22 09:14:19 -04:00
Himanshu
0e87c8b1df Remove console logs (#685)
* Removed logs

* Update demo.js
2019-01-22 09:13:50 -04:00
Mridul97
4fd814dd95 Add paint bucket module (#556)
* Add paint-bucket

* Add tolerance input
2019-01-22 09:03:42 -04:00
Slytherin
17d79c4b44 Fixed PR template test command (#677) 2019-01-17 18:52:20 -05:00
Slytherin
85a53325d2 Changes travis configuration to remove test for node v4 (#683) 2019-01-17 17:43:35 -05:00
Slytherin
81719a9e13 Stops loading of blend-module when offset unavailable (#654)
* Stops loading of blend-module when offset unavailable

* Added improvements

* Update demo.css

* Update notification.js

* Update defaultHtmlStepUi.js

* Update Module.js

* Update notification.js

* adds generic UI function to user-interface

* Added changes
2019-01-16 17:41:33 -05:00
VIRIK JAIN
fc02e7aea0 Add CODE_OF_CONDUCT.md (#681) 2019-01-16 22:55:54 +05:30
Mridul97
f68983d6d7 Fix toggle-able collapse (#651)
* fix toggle

* changes

* dist changes

* changes

* changes

* changes

* changes
2019-01-14 17:05:23 -05:00
Harsh Khandeparkar
b5fcba3c54 Refactor Draw Rectangle Module (#664)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* change template

* refactor drawRectangle

* changes

* changes
2019-01-14 15:57:49 -05:00
Jeffrey Warren
f6b1f7b201 Add code climate coverage reporting in Travis (#635)
* Add code climate coverage reporting in Travis

* Update .travis.yml
2019-01-14 15:53:58 -05:00
HarshKhandeparkar
2399e511a0 New pr template (#663)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* change template
2019-01-12 08:47:13 -05:00
dependabot[bot]
cfdb54359c Bump looks-same from 5.0.1 to 5.0.2 (#652)
Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v5.0.1...v5.0.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-11 12:37:10 -05:00
Mridul97
9e5f252fd4 Fix scroll up button (#638)
* fix scroll-up button

* dist changes

* changes

* changes
2019-01-10 21:22:51 -05:00
aashna27
2e9689b905 TakePhoto fixes (#641) 2019-01-10 18:11:03 -05:00
Mridul97
c72beb7359 test brightness module (#624) 2019-01-09 14:24:17 -05:00
aashna27
e5d26815d7 toggle-able collapse added for modules (#636) 2019-01-08 15:22:25 -05:00
aashna27
73b917f036 Header opens a new page (#619) 2019-01-08 15:18:32 -05:00
Mridul97
0248bc7124 Compilation of ui js files (#627)
* changes

* changes
2019-01-08 11:40:28 -05:00
Mridul97
89345f32b3 Add a 'Start a new sequence' button (#589)
* add start a new sequence button

* changes

* reset sequence

* changes

* changes

* changes

* changes

* changes
2019-01-07 18:22:51 -05:00
HarshKhandeparkar
4e5f8728fa Fixed Save Button (#562)
* update dist

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

dist update

Revert "dist update"

This reverts commit 9ee2a987e8f978961656ae8f71f6e6702bbbd30d.

* fix save button

* cleanup

* fix update initValue

* fix select type inputs

* fix integer type values

* fix integer type values

* cleanup

* changes and cleanup

* changes

* changes

* better solution

cleanup

* change detection finalized

* Fix <form> placement

* fix form ending

* changes

* cleanup

* fix merge conflicts

* better event listener

* cleanup

* cleanup

* Changes

* cleanup
2019-01-07 15:16:25 -05:00
Bhavay Anand
c2477bc3f7 fixed excessive space around thumbnail grid (#629) 2019-01-07 10:41:04 -05:00
Vibhor Gupta
ef4a4d3055 removed crop div (#623) 2019-01-07 09:40:53 -05:00
aashna27
20d49456fd Modified style for step remove notification (#620)
* modified style for step remove notification

* Update defaultHtmlStepUi.js
2019-01-06 14:44:25 -05:00
aashna27
2aa7e9b96c Dithering module added (#617) 2019-01-06 10:47:23 -05:00
jywarren
52c9c35398 version bump 2019-01-05 21:01:58 -05:00
Vibhor Gupta
a522e13613 media query for buttons (#583)
* media query for buttons

* easy fix

* quick icon scroll for small screens

* rebuild
2019-01-05 20:53:45 -05:00
Jeffrey Warren
f41f564bd9 npm audit (#614) 2019-01-05 20:51:24 -05:00
Vibhor Gupta
1a98247858 import/export bug fix (#608) 2019-01-05 20:37:42 -05:00
Mridul97
4369137336 update value below the slider (#565)
* update value below the slider

* changes
2019-01-05 20:17:39 -05:00
Mridul97
595d66ecd5 remove extra clear cache button and isolate cache code (#609)
* remove extra clear cache button

* isolate cache code

* changes
2019-01-05 17:28:11 -05:00
Mridul97
6d9f656a1e Show a message where to find saved sequence on saving a sequence (#586)
* show message on save sequence

* chaanges

* changes
2019-01-05 14:32:18 -05:00
Mridul97
69ed689ad7 refactor/speed-up brightness module (#558)
* refactor/speed-up brightness module

* changes
2019-01-05 11:23:29 -05:00
Jeffrey Warren
107b4c22e4 new defaults (#607) 2019-01-04 18:13:28 -05:00
jywarren
e5d36ca317 demo tweak 2019-01-04 15:08:41 -05:00
aashna27
eb595d6139 added sliders (#603) 2019-01-04 14:35:39 -05:00
MargaretAN9
2bdd532848 Fix fastie (#594)
* fixed fastie

* fixed greyscale (#593)

* add local to locally stored modules (#596)

* Add drawing rectangles module (#566)

* add drawing rectangle module

* changes

* White balance module (#585)

* white baalance module

* improved algorithm

* limit check

* Ability to set background color when "over cropping" to enlarge canvas (#563)

* ability to set background color when over cropping to enlarge canvas

* changes

* Clarify that saved sequence will only be available in this browser (#587)

* clarify that saved sequence will only be available in this browser

* changes

* changes

* changes

* Added a function to sequencer and then made the functionality available to crop and overlay module. (#592)

* Add manifest.json and cache static assets for offline use (#331)

* Add manifest.json

* cache static assets for offline use

* update cache

* add meta theme-color and change static files to be cache

* cache the files on network request

* caching on first run

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

* add a button to clear cache

* add styling to clear cache link

* Update Modules.js (#452)

I've arranged the modules in alphabetical order.

* Added a function to parse Input coordinates and added the functionality for crop and overlay modules

* Added changed dist files

* Revert "Added changed dist files"

This reverts commit dbda25a228.

* Changed function name and added the functionaity to src/utils folder

* Added unit test

* Added unit test to /test/util

* Add border thickness and color in DrawRectangle module (#602)

* added border thickness functionality

* add color functionality

* Bump tape from 4.9.1 to 4.9.2 (#579)

Bumps [tape](https://github.com/substack/tape) from 4.9.1 to 4.9.2.
- [Release notes](https://github.com/substack/tape/releases)
- [Commits](https://github.com/substack/tape/compare/v4.9.1...v4.9.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Bump looks-same from 4.1.0 to 5.0.1 (#559)

Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 4.1.0 to 5.0.1.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v4.1.0...v5.0.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Bump jsqr from 0.2.2 to 1.1.1 (#532)

Bumps [jsqr](https://github.com/cozmo/jsQR) from 0.2.2 to 1.1.1.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>

* Update color drop down (#595)

* updated drop down

* fixed greyscale (#593)

* add local to locally stored modules (#596)

* Add drawing rectangles module (#566)

* add drawing rectangle module

* changes

* White balance module (#585)

* white baalance module

* improved algorithm

* limit check

* Ability to set background color when "over cropping" to enlarge canvas (#563)

* ability to set background color when over cropping to enlarge canvas

* changes

* Clarify that saved sequence will only be available in this browser (#587)

* clarify that saved sequence will only be available in this browser

* changes

* changes

* changes

* Added a function to sequencer and then made the functionality available to crop and overlay module. (#592)

* Add manifest.json and cache static assets for offline use (#331)

* Add manifest.json

* cache static assets for offline use

* update cache

* add meta theme-color and change static files to be cache

* cache the files on network request

* caching on first run

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

* add a button to clear cache

* add styling to clear cache link

* Update Modules.js (#452)

I've arranged the modules in alphabetical order.

* Added a function to parse Input coordinates and added the functionality for crop and overlay modules

* Added changed dist files

* Revert "Added changed dist files"

This reverts commit dbda25a228.

* Changed function name and added the functionaity to src/utils folder

* Added unit test

* Added unit test to /test/util

* Add border thickness and color in DrawRectangle module (#602)

* added border thickness functionality

* add color functionality

* rebuild
2019-01-04 14:05:59 -05:00
MargaretAN9
d104ea744b Update color drop down (#595)
* updated drop down

* fixed greyscale (#593)

* add local to locally stored modules (#596)

* Add drawing rectangles module (#566)

* add drawing rectangle module

* changes

* White balance module (#585)

* white baalance module

* improved algorithm

* limit check

* Ability to set background color when "over cropping" to enlarge canvas (#563)

* ability to set background color when over cropping to enlarge canvas

* changes

* Clarify that saved sequence will only be available in this browser (#587)

* clarify that saved sequence will only be available in this browser

* changes

* changes

* changes

* Added a function to sequencer and then made the functionality available to crop and overlay module. (#592)

* Add manifest.json and cache static assets for offline use (#331)

* Add manifest.json

* cache static assets for offline use

* update cache

* add meta theme-color and change static files to be cache

* cache the files on network request

* caching on first run

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

* add a button to clear cache

* add styling to clear cache link

* Update Modules.js (#452)

I've arranged the modules in alphabetical order.

* Added a function to parse Input coordinates and added the functionality for crop and overlay modules

* Added changed dist files

* Revert "Added changed dist files"

This reverts commit dbda25a228.

* Changed function name and added the functionaity to src/utils folder

* Added unit test

* Added unit test to /test/util

* Add border thickness and color in DrawRectangle module (#602)

* added border thickness functionality

* add color functionality
2019-01-04 12:34:13 -05:00
dependabot[bot]
bc122af258 Bump jsqr from 0.2.2 to 1.1.1 (#532)
Bumps [jsqr](https://github.com/cozmo/jsQR) from 0.2.2 to 1.1.1.
- [Release notes](https://github.com/cozmo/jsQR/releases)
- [Commits](https://github.com/cozmo/jsQR/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-04 12:21:51 -05:00
dependabot[bot]
11476d5084 Bump looks-same from 4.1.0 to 5.0.1 (#559)
Bumps [looks-same](https://github.com/gemini-testing/looks-same) from 4.1.0 to 5.0.1.
- [Release notes](https://github.com/gemini-testing/looks-same/releases)
- [Changelog](https://github.com/gemini-testing/looks-same/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gemini-testing/looks-same/compare/v4.1.0...v5.0.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-04 12:20:49 -05:00
dependabot[bot]
a2157be2da Bump tape from 4.9.1 to 4.9.2 (#579)
Bumps [tape](https://github.com/substack/tape) from 4.9.1 to 4.9.2.
- [Release notes](https://github.com/substack/tape/releases)
- [Commits](https://github.com/substack/tape/compare/v4.9.1...v4.9.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-04 12:19:50 -05:00
Mridul97
b71e9dec12 Add border thickness and color in DrawRectangle module (#602)
* added border thickness functionality

* add color functionality
2019-01-04 11:27:29 -05:00
Slytherin
aad1f823d3 Added a function to sequencer and then made the functionality available to crop and overlay module. (#592)
* Add manifest.json and cache static assets for offline use (#331)

* Add manifest.json

* cache static assets for offline use

* update cache

* add meta theme-color and change static files to be cache

* cache the files on network request

* caching on first run

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

* add a button to clear cache

* add styling to clear cache link

* Update Modules.js (#452)

I've arranged the modules in alphabetical order.

* Added a function to parse Input coordinates and added the functionality for crop and overlay modules

* Added changed dist files

* Revert "Added changed dist files"

This reverts commit dbda25a228.

* Changed function name and added the functionaity to src/utils folder

* Added unit test

* Added unit test to /test/util
2019-01-04 11:27:00 -05:00
Mridul97
b520622bfd Clarify that saved sequence will only be available in this browser (#587)
* clarify that saved sequence will only be available in this browser

* changes

* changes

* changes
2019-01-03 16:22:21 -05:00
Mridul97
6028520197 Ability to set background color when "over cropping" to enlarge canvas (#563)
* ability to set background color when over cropping to enlarge canvas

* changes
2019-01-03 15:00:11 -05:00
Vibhor Gupta
a60acd5a72 White balance module (#585)
* white baalance module

* improved algorithm

* limit check
2019-01-03 14:26:06 -05:00
Mridul97
0fc8007e79 Add drawing rectangles module (#566)
* add drawing rectangle module

* changes
2019-01-03 13:41:34 -05:00
Mridul97
5bdaea3688 add local to locally stored modules (#596) 2019-01-03 12:23:16 -05:00
MargaretAN9
e353465dce fixed greyscale (#593) 2019-01-03 00:39:31 -05:00
Vibhor Gupta
39c70d1304 updating description text to default (#574) 2019-01-02 19:06:20 -05:00
187 changed files with 30596 additions and 6292 deletions

View File

@@ -10,7 +10,7 @@
### Please show us where to look
http://sequencer.publiclab.org...
https://beta.sequencer.publiclab.org
### What's your PublicLab.org username?

View File

@@ -2,10 +2,10 @@ Fixes #[Add issue number here.]
Make sure these boxes are checked before your pull request (PR) is ready to be reviewed and merged. Thanks!
* [ ] tests pass -- look for a green checkbox ✔️ a few minutes after opening your PR -- or run tests locally with `rake test`
* [ ] tests pass -- look for a green checkbox ✔️ a few minutes after opening your PR -- or run tests locally with `npm test`
* [ ] code is in uniquely-named feature branch and has no merge conflicts
* [ ] PR is descriptively titled
* [ ] ask `@publiclab/reviewers` for help, in a comment below
* [ ] ask `@publiclab/is-reviewers` for help, in a comment below
> We're happy to help you get this ready -- don't be afraid to ask for help, and **don't be discouraged** if your tests fail at first!

4
.github/config.yml vendored
View File

@@ -10,14 +10,14 @@ newIssueWelcomeComment: |
# Comment to be posted to on PRs from first time contributors in your repository
newPRWelcomeComment: |
Thanks for opening this pull request! `Dangerbot` will test out your code and reply in a bit with some pointers and requests.
Thanks for opening this pull request!
There may be some errors, **but don't worry!** We're here to help! 👍🎉😄
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
# Comment to be posted to on pull requests merged by a first time user
firstPRMergeComment: |
Congrats on merging your first pull request! 🙌🎉⚡️
Your code will likely be published to https://sequencer.publiclab.org in the next few days.
Your code will be published to https://beta.sequencer.publiclab.org in a day or two.
In the meantime, can you tell us your Twitter handle so we can thank you properly?
Now that you've completed this, you can help someone else take their first step!
See: [Public Lab's coding community!](https://code.publiclab.org)

4
.gitignore vendored
View File

@@ -40,5 +40,9 @@ todo.txt
test.js
output.txt
output/
test/core/modules/test_outputs
test/core/modules/test_outputs/*
node_modules/
node_modules/*
test_outputs
/test_outputs

View File

@@ -1,16 +1,22 @@
language: node_js
node_js:
- '4'
- '5'
- '6'
- '7' # Node.js 7 is most stable version
- '8'
- '10'
env:
- CXX=g++-4.8
before_script:
- npm install grunt-cli -g # for "grunt build"
- npm install jasmine -g
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script:
- npm test
- npm run test-ui
- grunt build
after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
addons:
apt:
sources:

114
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,114 @@
This document was copied from its home at https://publiclab.org/conduct on October 12, 2017. See that page if you would like to submit your concerns in a safe, completely anonymous way, and to learn more about this document.
****
# Public Lab Code of Conduct
_Public Lab, PO Box 426113, Cambridge, MA 02142_
We are coming together with an intent to care for ourselves and one another. We want to nurture a compassionate democratic culture where responsibility is shared. We -- visitors, community members, community moderators, staff, organizers, sponsors, and all others -- hold ourselves accountable to the same values regardless of position or experience. For this to work for everybody, individual decisions will not be allowed to run counter to the welfare of other people. This community aspires to be a respectful place both during online and in-person interactions so that all people are able to fully participate with their dignity intact. This document is a piece of the culture we're creating.
This code of conduct applies to all spaces managed by the Public Lab community and non-profit, both online and in person. It was written by the Conduct Committee (formed in 2015 during Public Labs annual conference “The Barnraising”) and facilitated by staff to provide a clear set of practical guidelines for multi-day events such as Barnraisings, events led by organizers and community members, and online venues such as the website, comment threads on software platforms, chatrooms, our mailing lists, the issue tracker, and any other forums created by Public Lab which the community uses for communication.
## We come from all kinds of backgrounds
Our community is best when we fully invite and include participants from a wide range of backgrounds. We specifically design spaces to be welcoming and accessible to newcomers and folks from underrepresented groups. Public Lab is dedicated to providing a harassment-free, safe, and inclusive experience for everyone, regardless of personal and professional background, gender, gender identity and expression, style of clothing, sexual orientation, dis-/ability, physical appearance, body size, race, class, age, or religion. Public Lab resists and rejects: racism, sexism, ableism, ageism, homophobia, transphobia, body shaming, religion shaming, “geekier-than-thou” shaming, education bias, the shaming of people nursing children, and the dismissal or bullying of children or adults.
## We do not tolerate harassment or shaming
While we operate under the assumption that all people involved with Public Lab subscribe to the basic understanding laid out above, we take these issues very seriously and think they should, in general, be taken seriously. Therefore, individuals who violate this Code both in and outside of Public Lab spaces may affect their ability to participate in Public Lab ranging from temporarily being placed into online moderation to, as a last resort, expulsion from the community. If you have any questions about our commitment to this framework and/or if you are unsure about aspects of it, email conduct@publiclab.org and we will do our best to provide clarification.
## How It Works
Sometimes things go wrong. When a situation is uncomfortable, hurtful, exclusionary, or upsetting, there is a problem that should be addressed. This code of conduct is an effort to maintain a safe space for everyone, and to talk about what might happen if that space is compromised. Please see additional guidelines below for community behavior on how we expect people to interact with one another.
### Two helpful groups
__Conduct Committee (ConductCom)__: If at any time you experience something that you are not comfortable with, you may contact the Conduct Committee. As established during the 2015 Annual Barnraising, the following individuals are on the Conduct Committee: Klie Kliebert, Carla Green, Nick Shapiro, and Shannon Dosemagen, the executive director of the Public Lab nonprofit.
If you would like to have a confidential conversation, connect with ConductCom in person or email via [conduct@publiclab.org](mailto:conduct@publiclab.org). A minimum of two committee members will confer and respond as swiftly as possible. If you would prefer to speak privately with a representative of the nonprofit, please contact the executive director directly either in person or by email: [shannon@publiclab.org.](mailto:conduct@publiclab.org)
To submit a report anonymously for review by ConductCom, go online via phone or computer to our anonymous “contact” app, located at [http://bit.ly/PLReport](http://bit.ly/PLReport). This contact app will be monitored daily at 8am CST during in-person events like Barnraisings and weekly at all other times. During multi-day in-person events hosted by the Public Lab non-profit, there will also be a physical suggestion box available. This box will be monitored throughout the event and can also be used to let us know if you need us to check on an anonymous online submission sooner.
__Moderators Group:__ The moderators group is responsible for addressing immediate moderation issues that arise during online violations of the code over email lists and Public Lab community websites, as well as approving first-time posts and generally handling spam. Instructions on how to become a moderator, and, if youve been placed in moderation how to begin the process of getting out of moderation can be found at: https://publiclab.org/wiki/moderation.
****
## A Culture of Empathy
We begin interactions by acknowledging that we are part of a community with complementary goals. Different views are allowed to respectfully coexist in the same space. When something's happened and someone is uncomfortable, our first choice is to work through it. Endeavor to listen and appropriately adjust your behavior if someone approaches you privately with a request that you apologize or publicly requests that you stop an ongoing presentation. If someone questions your words, actions or motives, or "calls you out", hear their feedback and respond respectfully. Its okay to not understand why something is hurtful or causes discomfort, as long as you approach it respectfully, with empathy. Repeating hurtful behavior after it has been addressed is disrespectful and is not allowed. Doing so will result in removal and subsequent banning from in-person events and being placed into moderation in online spaces.
### The first rule of engaging with others is consent
During in-person gatherings, consent is important to highlight because the negotiation of consent can be subtle, and its easy to miss each others non-verbal cues, resulting in miscommunication and/or offense. During online interactions, consent can be even harder to distinguish.
We make guesses or assessments of consent (willingness, welcome, invitation) all the time. Then we stay open to signs that the consent isn't there. Handshakes are a clear example of consent: someone offers a hand, and you take it if you want to shake it. A friendly smile might indicate consent to start a conversation. It might not. We learn that in the interaction. Sometimes we ask directly. We are open to making mistakes, and learning from them. The more we learn to be empathetic and see other people, the more we're able to talk about consent.
Before you engage with someone on any level, be sure you have their consent. If your indications aren't being heard, you can also ask for help from other folks, especially Conduct Committee members and staff of the non-profit: "They aren't taking the hint. Will you help?" Turning a blind eye to hurtful interactions can be as bad for our community as the exchange itself. If you witness something, it's your responsibility to say something. This is how we keep each other accountable, encourage empathy, and keep our community safe.
## Guidelines for in-person community behavior
Do | Dont
----------- | -----------
Respectfully share what method works best for you, while giving others space to think differently and contribute other ideas | Disparage entire groups/sets of people for their beliefs or methods
Ask permission to take pictures of and post about others on social media (see Media Consent, above)| Upload photos, tag or mention others online without their consent
Speak your own narrative, from your own unique culture | Caricature the cultural expressions of groups you are not a member of
Model inclusionary expertise - if others in the group appear to be “lost”, slow down; stop and ask for input | Present information in a way / at a level that no one else in the room can understand, with no attempt to include others in the discussion
Create events that are all-ages appropriate | Use language that excludes youth and their experiences as vital contributors
Give everyone a chance to talk, only interrupting if absolutely necessary - for example, Code of Conduct violations | Repeatedly disrupt a discussion
Stop, listen and ask for clarification if someone perceives your behavior or presentation as violating the Code of Conduct | Ignore others request to stop potentially harmful behavior, even if it was an accident
Cultivate a sense of humor based on other subjects, such as word play (especially puns!) | Joke using words related to actual or insulting descriptions of people
Use words that accurately describe the situation - For example, “The wind was ridiculously strong!” instead of “The wind was crazy!” | Use disability and mental/emotional health terminology to describe a situation metaphorically, especially if the phrasing is meant as an insult
Only discuss someone elses lifestyle practices if they invite you to a conversation on the topic | Make unwelcomed comments regarding a persons lifestyle practices, including those related to food, health, parenting, relationships, and employment
Ask someone before you hug them; keep your hands/body to yourself, even when joking, unless the other person has given verbal consent | Initiate physical contact or simulate physical contact without consent
Disengage and find another activity if someone did not invite you and is not engaging with you | Violate personal space by continuing your physical presence into private spaces without consent
Exercise the right to talk about your own identity if you want to, or not if you dont want to | Deliberately “out” any aspect of a persons identity without their consent
Use the pronouns people have specified for themselves | Purposely misgender someone (ie, refusing to use their correct gender pronouns) after they have told you their correct pronouns
## Additional guidelines for online community behavior
Online modes of interaction involve large numbers of people without the helpful presence of gestural, expression, and tonal cues regarding consent. Because of this, respectful and self-aware online conduct is both especially important and difficult. Our community has evolved specific guidelines for online interactions.
_If someone violates these guidelines, someone from the Moderators group will place them into moderation by changing that persons posting permission on the relevant list, on the website, or both._
Our triple notification standard for moderation means a point person from the Moderators group will:
1. e-mail the person directly with a brief explanation of what was violated,
2. send a summary email to the rest of the moderators group,
3. if it happened on a public list (vs a website), notify the list that one of our members has been placed into moderation with a brief explanation of what is not tolerated.
If you wish to begin the process of getting out of moderation, respond to the email sent to you from [moderators@publiclab.org](mailto:moderators@publiclab.org). The Moderators group has the option to involve ConductCom.
Do | Dont
-------|--------
Stay on topic to make long threads easier to follow |Send spurious one-line responses that effectively "spam" hundreds of people and lower the overall content quality of a conversation. (Exception: expressions of appreciation and encouragement!)
Start a new thread to help others follow along. Important if your response starts to significantly diverge from the original topic | Respond with off-topic information making it hard for the large group of readers to follow along
Write short and literal subject lines to help the readers of the list manage the volume of communication | Humor and euphemisms in subject lines are easily misunderstood, although enthusiasm is welcome!
Mind your tone. We are not having this conversation in person, so it is all the more important to maintain a tone of respect | Write in aggressive tone, disrespectful tone, mocking tone, off-color tone. Note: writing in all caps is regarded as shouting
## Media Consent
* ALWAYS check with parents about posting anything with minors.
* Never post the names of minors in conjunction with their photo.
* During multi-day events like Barnraisings most people will have signed media releases. Those who havent will be responsible for placing stickers on their nametags, and/or raising their hands in the moment to alert photographers to move them out of frame.
* For events where people have not signed blanket media release forms, the photographer is responsible for letting the room know that you are taking photos that will be posted online. Pay special attention to the presence of minors and their parent's wishes.
## Addendum for all staff
Staff are bound by their Employment Handbook, you must reference it. Additionally:
* Direct problems that come up among community members to the Conduct Committee.
* When organizing events, circulate access information regarding wheelchair-accessible ADA bathrooms, non-gendered bathrooms, the presence of stairs or curb ramps in the parking lot, et cetera.
* During events that you are attending in person, solve accessibility issues by making sure attendees know where bathrooms are located and can access them by wheelchair without being obstructed by things like chairs, kites, contraptions, or cords.
* Watch for people feeling left out and include them.

View File

@@ -11,6 +11,7 @@ Most contribution (we imagine) would be in the form of API-compatible modules, w
* [Contributing Modules](#contributing-modules)
* [Info File](#info-file)
* [Ideas](#Contribution-ideas)
* [Grunt Tasks](#grunt-tasks)
****
@@ -62,6 +63,8 @@ Image Sequencer modules are designed to be run either in the browser or in a Nod
https://github.com/tech4gt/image-sequencer
### Browser/node compatibility
If you wish to offer a module without browser-compatibility, please indicate this in the returned `info` object as:
module.exports = [
@@ -175,6 +178,7 @@ There are four events in all:
* `UI.onComplete(options.step)` 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(options.step)` is emitted automatically and the module should not emit it.
* `UI.notify(msg,id)` must be emmited when a notification has to be produced.
### Name and description
@@ -271,16 +275,20 @@ For help integrating, please open an issue.
IMAGE SEQUENCER supports "meta modules" -- modules made of other modules. The syntax and structure of these meta modules is very similar to standard modules. Sequencer can also genarate meta modules dynamically with the function `createMetaModule` which can be called in the following ways
```js
// stepsString is a stringified sequence
sequencer.createMetaModule(stepsString,info)
/* stepsArray is the array of objects in this format
* [
* {name: "moduleName",options: {}},
* {name: "moduleName",options: {}}
* ]
/* Mapping function is a function which gets the inputs of the module as argument
* and returns an array with steps mapped to their options
* See https://github.com/publiclab/image-sequencer/blob/main/src/modules/Colorbar/Module.js for example
*/
/* Module options is an object with the following keys
* infoJson: the info.json object for the module
*/
sequencer.createMetaModule(mappingFunction,moduleOptions)
/* createMetaModule returns an array of module function and info just like normal
* modules. These can also be loaded into sequencer dynamically like other modules
*/
sequencer.createMetaModule(stepsArray,info)
```
A Meta module can also be contributed like a normal module with an info and a Module.js. A basic Meta module shall follow the following format
@@ -288,12 +296,17 @@ A Meta module can also be contributed like a normal module with an info and a Mo
```js
// Module.js
module.exports = function metaModuleFun(){
this.expandSteps([
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', options: {} }
]);
}
module.exports = require('../../util/createMetaModule.js')(
function mapFunction(options) {
return [
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', 'options': {} },
];
}, {
infoJson: require('./info.json')
}
)[0];
```
```json
@@ -301,7 +314,8 @@ module.exports = function metaModuleFun(){
{
"name": "meta-moduleName",
"description": "",
"length": //Integer representing number of steps in the metaModule
"inputs": {
}
}
```
@@ -316,19 +330,35 @@ module.exports = [
All of the above can also be combined together to form a single file module.
```js
// MetaModule.js
module.export = [
function (){
this.expandSteps([
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', options: {} }
]);
},
{
"name": "meta-moduleName",
"description": "",
"length": //Integer representing number of steps in the metaModule
}
]
module.exports =
sequencer.createMetaModule(
function mapFunction(options) {
return [
{ 'name': 'module-name', 'options': {} },
{ 'name': 'module-name', 'options': {} },
];
}, {
infoJson: {
"name": "meta-moduleName",
"description": "",
"inputs": {
}
}
});
```
The length is absolutely required for a meta-module, since sequencer is optimized to re-run minimum number of steps when a step is added in the UI which is 1 in the case of normal modules, if the added step is a meta-module the length of the sequence governs the number of steps to re-run.
## Grunt Tasks
This repository has different grunt tasks for different uses. The source code is in the [Gruntfile](https://github.com/publiclab/image-sequencer/blob/main/Gruntfile.js).
The following command is used for running the tasks: `grunt [task-name]`. Here `[task-name]` should be replaced by the name of the task to be run. To run the default task run `grunt` without any options.
#### Tasks
1. **compile**: Compiles/Browserifies the dist files in `/dist/image-sequencer.js` and `/dist/image-sequencer-ui.js`.
2. **build**: Compiles the files as in the **compile** task and minifies/uglifies dist files in `/dist/image-sequencer.min.js` and `/dist/image-sequencer-ui.min.js`.
3. **watch**: Checks for any changes in the source code and runs the **compile** task if any changes are found.
4. **serve**: Compiles the dist files as in the **compile** task and starts a local server on `localhost:3000` to host the demo site in `/examples/` directory. Also runs the **watch** task.
5. **production**: Compiles and minifies dist files in `/dist/image-sequencer.js` and `/dist/image-sequencer-ui.js` without the `.min.js` extension to include minified files in the demo site. This script should only be used in production mode while deploying.
6. **default**: Runs the **watch** task as default.

View File

@@ -15,22 +15,46 @@ module.exports = function(grunt) {
livereload: true
},
source: {
files: ["src/**/*", "Gruntfile.js"],
tasks: ["build:js"]
files: ["src/**/*", "Gruntfile.js", "examples/lib/*","examples/demo.js"],
tasks: ["compile"]
}
},
browserify: {
dist: {
core: {
src: ["src/ImageSequencer.js"],
dest: "dist/image-sequencer.js"
},
ui: {
src: ["examples/demo.js"],
dest: "dist/image-sequencer-ui.js"
},
prodcore: {
src: ["src/ImageSequencer.js"],
dest: "dist/image-sequencer.brow.js"
},
produi: {
src: ["examples/demo.js"],
dest: "dist/image-sequencer-ui.brow.js"
}
},
uglify: {
dist: {
core: {
src: ["./dist/image-sequencer.js"],
dest: "./dist/image-sequencer.min.js"
},
ui: {
src: ['dist/image-sequencer-ui.js'],
dest: 'dist/image-sequencer-ui.min.js'
},
prodcore: {
src: ["dist/image-sequencer.brow.js"],
dest: "dist/image-sequencer.js"
},
produi: {
src: ["dist/image-sequencer-ui.brow.js"],
dest: "dist/image-sequencer-ui.js"
}
},
browserSync: {
@@ -45,6 +69,8 @@ module.exports = function(grunt) {
/* Default (development): Watch files and build on change. */
grunt.registerTask("default", ["watch"]);
grunt.registerTask("build", ["browserify:dist", "uglify:dist"]);
grunt.registerTask("serve", ["browserify:dist", "uglify:dist", "browserSync", "watch"]);
grunt.registerTask("build", ["browserify:core", "browserify:ui", "uglify:core", "uglify:ui"]);
grunt.registerTask("serve", ["browserify:core", "browserify:ui", "browserSync", "watch"]);
grunt.registerTask("compile", ["browserify:core", "browserify:ui"]);
grunt.registerTask("production", ["browserify:prodcore", "browserify:produi", "uglify:prodcore", "uglify:produi"]);
};

270
README.md
View File

@@ -1,26 +1,34 @@
Image Sequencer
====
[![Build Status](https://travis-ci.org/publiclab/image-sequencer.svg?branch=master)](https://travis-ci.org/publiclab/image-sequencer)
**Latest Stable Demo**: https://sequencer.publiclab.org
**Latest Beta Demo**: https://beta.sequencer.publiclab.org
## Why
Image Sequencer is different from other image processing systems in that it's _non-destructive_: instead of modifying the original image, it **creates a new image at each step in a sequence**. This is because it:
Image Sequencer is different from other image processing systems because it's _non-destructive_: instead of modifying the original image, it **creates a new image at each step in a sequence**. This is because it:
* produces a legible trail of operations, to "show your work" for evidential, educational, or reproducibility reasons
* makes the creation of new tools or "modules" simpler -- each must accept an input image, and produce an output image
* allows many images to be run through the same sequence of steps
* allows many images to run through the same sequence of steps
* works identically in the browser, on Node.js, and on the command line
The following diagrams attempt to explain how the applications various components interconnect:
![general diagram](https://publiclab.org/i/30547.png?s=o)
![workflow diagram](https://raw.githubusercontent.com/publiclab/image-sequencer/master/examples/images/diagram-workflows.png)
It is also for prototyping some other related ideas:
It also for prototypes other related ideas:
* filter-like image processing -- applying a transform to an image from a given source, like a proxy. I.e. [every image tile of a satellite imagery web map](https://publiclab.org/notes/warren/05-10-2018/prototype-filter-map-tiles-in-real-time-in-a-browser-with-imagesequencer-ndvi-landsat)
* test-based image processing -- the ability to create a sequence of steps that do the same task as some other image processing tool, provable with example before/after images to compare with
* logging of each step to produce an evidentiary record of modifications to an original image
* filter-like image processing -- apply a transform to an image from a given source, like a proxy. I.e. [every image tile of a satellite imagery web map](https://publiclab.org/notes/warren/05-10-2018/prototype-filter-map-tiles-in-real-time-in-a-browser-with-imagesequencer-ndvi-landsat)
* test-based image processing -- the ability to create a sequence of steps that do the same task as other image processing tools, provable with example before/after images to compare with
* logging each step -- to produce an evidentiary record of modifications to an original image
* cascading changes -- change an earlier step's settings, and see those changes affect later steps
* "small modules"-based extensibility: see [Contributing](https://github.com/publiclab/image-sequencer/blob/master/CONTRIBUTING.md)
* "small modules" -- based extensibility: see [Contributing](https://github.com/publiclab/image-sequencer/blob/master/CONTRIBUTING.md)
## Examples
@@ -45,10 +53,10 @@ A diagram of this running 5 steps on a single sample image may help explain how
## Installation
This library works in the browser, in Node, and on the command line (CLI), which we think is great.
This library conveniently works in the browser, in Node, and on the command line (CLI).
### Unix based platforms
You can set up a local environment to test the UI with `sudo npm run setup` followed by `npm start`
You can set up a local environment to test the UI with `sudo npm run setup` followed by `npm start`
### Windows
Our npm scripts do not support windows shells, please run the following snippet in PowerShell.
@@ -84,11 +92,19 @@ $ npm install image-sequencer -g
```
$ npm run debug invert
```
## Quick Usage
### Initializing the Sequencer
The Image Sequencer Library exports a function ImageSequencer which initializes a sequencer.
```js
var sequencer = ImageSequencer();
```
Image Sequencer can be used to run modules on an HTML Image Element using the
`replaceImage` method. The method accepts two parameters - `selector` and `steps`.
`replaceImage` method, which accepts two parameters - `selector` and `steps`.
`selector` is a CSS selector. If it matches multiple images, all images will be
modified. `steps` may be the name of a module or array of names of modules.
@@ -149,7 +165,7 @@ Options for the steps can be passed in one line as JSON in the details option li
$ ./index.js -i [PATH] -s "brightness" -c '{"brightness":50}'
```
Or the values can be given through terminal prompt like
Or the values can be given through the terminal prompt like
<img width="1436" alt="screen shot 2018-02-14 at 5 18 50 pm" src="https://user-images.githubusercontent.com/25617855/36202790-3c6e8204-11ab-11e8-9e17-7f3387ab0158.png">
@@ -218,11 +234,11 @@ sequencer.addSteps(modules, optional_options);
```
If only one module is to be added, `modules` is simply the name of the module.
If multiple images are to be added, `modules` is an array of the names of modules
which are to be added, in that particular order.
If multiple images are to be added, `modules` is an array, which holds the names of modules
to be added, in that particular order.
optional_otions is just additional parameters, in object form, which you might
want to provide to the modules. It's an optional parameter.
optional_otions is just an optional parameter, in object form, which you might
want to provide to the modules.
return value: **`sequencer`** (To allow method chaining)
@@ -273,7 +289,7 @@ return value: **`sequencer`** (To allow method chaining)
### Removing a step from the sequencer
The `removeSteps` method is used to remove unwanted steps from the sequencer.
It accepts the index of the step as an input or an array of the unwanted indices
It accepts the index of the step as an input or an array of the unwanted indices,
if there are more than one.
For example, if the modules ['ndvi-red','crop','invert'] were added in this order,
@@ -300,7 +316,7 @@ role it played in `addSteps`.
Indexes can be negative. Negative sign with an index means that counting will be
done in reverse order. If the index is out of bounds, the counting will wrap in
the original direction of counting. So, an `index` of -1 means that the module is
the original direction of counting. So, an `index` of -1 means the module is
inserted at the end.
```js
@@ -326,12 +342,11 @@ Methods can be chained on the Image Sequencer:
* run() can not be in the middle of the chain.
* If the chain starts with loadImage() or loadImages(), the following methods are
applied only to the newly loaded images.
* If no name is provided to the image, a name will be generated for it. The name will
be of the form "image<number>". For Ex: "image1", "image2", "image3", etc.
Valid Chains:
```js
sequencer.loadImage('red',function(){
sequencer.loadImage(function(){
this.addSteps('invert').run(function(out){
//do something with ouptut.
});
@@ -346,156 +361,20 @@ sequencer.addSteps('invert').run().addSteps('ndvi-red');
```
## Multiple Images
Image Sequencer is capable of handling multiple images at once.
### Initializing a sequencer with multiple images.
This is just like before.
```js
var sequencer = ImageSequencer();
```
## Fetching current steps
### Loading Multiple Images into the Sequencer
Multiple images can be loaded by the method `loadImages`. Everything is the same,
except that now, a unique identification called `image_name` has to be provided
with each image. This is a string literal.
* 3/2 parameters :
```js
sequencer.loadImages(image_name,
image_src,optional_callback);
```
* 1/2 parameters (JSON) :
```js
sequencer.loadImages({
images: {
image1_name: image_src,
image2_name: image_src,
...
},
callback: optional_callback
});
```
return value: **none**
### Adding Steps on Multiple Images
The same method `addSteps` is used for this. There's just a slight obvious change
in the syntax that the image name has to be supplied too. `image_name` as well as,
`module_name` in the following examples can be either strings or arrays of strings.
The `getSteps` method can be used to get the array of current steps in `this` instance of sequencer.For example
```js
sequencer.addSteps(image_name,module_name,optional_options);
sequencer.getSteps()
```
If no Image Name is specified, the module is added to **all** images.
```js
sequencer.addSteps(module_name,optional_options);
```
All this can be passed in as JSON:
```js
sequencer.addSteps({
image1_name: {name: module_name, o: optional_options},
image2_name: {name: module_name, o: optional_options},
...
});
```
return value: **`sequencer`** (To allow method chaining)
### Running a Sequencer with multiple images
The same `run` method can be used with a slight change in syntax.
The `run` method accepts parameters `image` and `from`. `from` is the index from
where the function starts generating output. By default, it will run across all
the steps. (from = 1) If no image is specified, the sequencer will be run over **all
the images**. `image_name` may be an array of image names.
```js
sequencer.run(); //All images from first step
```
```js
sequencer.run(image_name,from); //Image 'image' from 'from'
```
The `run` method also accepts an optional callback just like before:
```js
sequencer.run(image_name,from,function(out){
// This gets called back.
// "out" is the DataURL of final image.
});
```
JSON input is also acceptable.
```js
sequencer.run({
image1_name: from,
image2_name: from,
...
});
```
return value: **`sequencer`** (To allow method chaining)
### Removing Steps from an Image
Similarly, `removeSteps` can also accept an `image_name` parameter. Either, both,
or none of `image_name` and `steps` them may be an array. JSON input is also acceptable.
```js
sequencer.removeSteps("image_name",[steps]);
```
```js
sequencer.removeSteps("image_name",step);
```
```js
sequencer.removeSteps({
image1_name: [steps],
image2_name: [steps],
...
});
```
return value: **`sequencer`** (To allow method chaining)
### Inserting steps on an image
The `insertSteps` method can also accept an `image_name` parameter. `image_name`
may be an array. Everything else remains the same. JSON Input is acceptable too.
```js
sequencer.insertSteps("image",index,"module_name",o);
```
```js
sequencer.insertSteps([image],index,"module_name",o);
```
```js
sequencer.insertSteps({
image1: [
{index:index1, name: module_name1, o:optional_options1},
{index:index2, name: module_name2, o:optional_options2},
...
]
});
```
return value: **`sequencer`** (To allow method chaining)
returns an array of steps associated with the current sequencer.
## Saving Sequences
IMAGE SEQUENCER supports saving a sequence of modules and their associated settings in a simple string syntax. These sequences can be saved in the local storage inside the browser and inside a JSON file in node.js. sequences can be saved in node context using the CLI option
IMAGE SEQUENCER supports saving a sequence of modules and their associated settings in a simple string syntax. These sequences can be saved in the local storage of the browser and inside a JSON file in node.js. sequences can be saved in node context using the CLI option
```shell
--save-sequence "name stringified-sequence"
@@ -535,6 +414,29 @@ Image Sequencer can also generate a string for usage in the CLI for the current
sequencer.toCliString()
```
## Importing steps using JSON array
Image sequencer provides the following core API function to import the given sequence of JSON steps into sequencer.
```js
sequencer.importJSON(obj)
```
It can be implemented the following way for example:
```js
sequencer.importJSON([
{ name: 'blur', options: {} }
]);
```
where name is the name of step to be added, options object can be the one used to provide various params to the sequencer which can customise the default ones.
To see this in action, please refer to line # 51 of the following:
[test/core/modules/import-export.js](https://github.com/publiclab/image-sequencer/blob/main/test/core/modules/import-export.js)
## Creating a User Interface
Image Sequencer provides the following events which can be used to generate a UI:
@@ -549,6 +451,10 @@ for a module. This can be used, for instance, to update the DIV with the new ima
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.
* `notify` : This event is triggered whenever we need to shoot a notification to the
user-interface.For example when the step is not available, we can shoot a notification,
by sending appropriate message.For HTML UI it adds a DOM node to the browser, for CLI
and node , it logs the notification output to the respective console.
How to define these functions:
@@ -557,7 +463,8 @@ sequencer.setUI({
onSetup: function(step) {},
onDraw: function(step) {},
onComplete: function(step) {},
onRemove: function(step) {}
onRemove: function(step) {},
notify: function(msg,id) {}
});
```
@@ -600,10 +507,37 @@ sequencer.setUI({
});
```
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 name of a loaded image defaults to a name like "image1",
"image2", et cetra.
## Using multiple images on same sequencer:
Details of all modules can be sought using `sequencer.modulesInfo()`.
Image Sequencer object supports one imageURL at a time.
Adding a seccond image to same sequencer will result to adding same set of steps added to prior image and flushing out the previous one.
```js
s1 = new ImageSequencer(...);
s1.loadImage(url1);
s1.addSteps('blur');
s1.run();
s1.addImage(url2);
s1.run();
```
However if we want to use more than one image, we can either initialize a sequencer for each image like:
```js
sequencer1 = new ImageSequencer(...);
sequencer1.loadImage(...);
sequencer1.addSteps(steps);
sequencer1.run();
sequencer2 = new ImageSequencer(...);
sequencer2.loadImage(...);
sequencer2.addSteps(steps);
sequencer2.run();
```
**Note**: Details of all modules can be sought using `sequencer.modulesInfo()`.
This method returns an object which defines the name and inputs of the modules. If a module name (hyphenated) is passed in the method, then only the details of that module are returned.
The `notify` function takes two parameters `msg` and `id`, former being the message to be displayed on console (in case of CLI and node ) and a HTML component(in browser). The id is optional and is useful for HTML interface to give appropriate IDs.

18088
dist/image-sequencer-ui.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/image-sequencer-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6795
dist/image-sequencer.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,30 +6,38 @@ List of Module Documentations
1. [Crop](#crop-module)
2. [Segmented-Colormap](#segmented-colormap-module)
3. [FisheyeGl](#fisheyeGl-module)
4. [Average](#average-module)
5. [Blend](#blend-module)
6. [Blur](#blur-module)
7. [Brightness](#brightness-module)
8. [Channel](#channel-module)
9. [Colorbar](#colorbar-module)
10. [Colormap](#colormap-module)
11. [Contrast](#contrast-module)
12. [Convolution](#convolutioon-module)
13. [DecodeQr](#decodeQr-module)
14. [Dynamic](#dynamic-module)
15. [Edge-Detect](#edge-detect-module)
16. [Gamma-Correction](#gamma-correction-module)
17. [Gradient](#gradient-module)
18. [Histogram](#histogram-module)
19. [Import-image](#import-image-module)
20. [Invert](#invert-module)
21. [Ndvi](#ndvi-module)
22. [Ndvi-Colormap](#ndvi-colormap-module)
23. [Overlay](#overlay-module)
24. [Resize](#resize-module)
25. [Rotate](#rotate-module)
26. [Saturation](#saturation-module)
4. [Add QR](#Add-QR-module)
5. [Average](#average-module)
6. [Blend](#blend-module)
7. [Blur](#blur-module)
8. [Brightness](#brightness-module)
9. [Channel](#channel-module)
10. [Colorbar](#colorbar-module)
11. [Colormap](#colormap-module)
12. [Contrast](#contrast-module)
13. [Convolution](#convolution-module)
14. [DecodeQr](#decodeQr-module)
15. [Dither](#dither-module)
16. [DrawRectangle](#draw-rectangle-module)
17. [Dynamic](#dynamic-module)
18. [Edge-Detect](#edge-detect-module)
19. [FlipImage](#flipimage-module)
20. [Gamma-Correction](#gamma-correction-module)
21. [Gradient](#gradient-module)
22. [Histogram](#histogram-module)
23. [Import-image](#import-image-module)
24. [Invert](#invert-module)
25. [Ndvi](#ndvi-module)
26. [Ndvi-Colormap](#ndvi-colormap-module)
27. [Overlay](#overlay-module)
28. [PaintBucket](#paint-bucket-module)
29. [Resize](#resize-module)
30. [ReplaceColor](#replacecolor-module)
31. [Rotate](#rotate-module)
32. [Saturation](#saturation-module)
33. [Threshold](#threshold)
34. [Tint](#tint)
35. [WhiteBalance](#white-balance)
## crop-module
@@ -100,6 +108,23 @@ where `options` is an object with the following properties:
* x : Field of View x (0 to 2; default 1)
* y : Field of View y (0 to 2; default 1)
## add-qr-module
This module Adds QR corresponding to the given string.
#### Usage
```js
sequencer.loadImage('PATH')
.addSteps('add-qr',options)
.run()
```
where `options` is an object with the following properties:
* size : size of QR code in pixels (default 200)
* qrCodeString : input string to generate QR code
## average-module
This module is used for averaging all the pixels of the image.
@@ -247,6 +272,42 @@ This module is used for decoding a QR in image (if present).
.addSteps('decode-qr',options)
.run()
```
## dither-module
This module approximates a color from a mixture of other colors when the required color is not available, creating illusions of the color that is not present actually.
[more info on wikipedia](https://en.wikipedia.org/wiki/Dither)
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('dither',options)
.run()
```
where `options` is an object with the following property:
* dither : Can select the name of the Dithering Algorithm(default none)
## draw-rectangle-module
This module helps to draw a rectangle on the image with a starting and ending corner with the specified thickness and color of the border.
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('draw-rectangle',options)
.run()
```
where `options` is an object with the following properties:
* startingX : starting x position of the rectangle (default 0)
* startingY : starting y position of the rectangle (default 0)
* endX : last x position of the rectangle (default "width")
* endY : last y position of the rectangle (default "height")
* thickness : thickness of the border (default 1)
* color : RGBA values separated by a space (default "0 0 0 255")
## dynamic-module
This module is used for producing each color channel based on the original image's color.
@@ -281,6 +342,22 @@ where `options` is an object with the following properties:
* highThresholdRatio : Upper Threshold Ratio ( default : 0.2)
* lowThresholdratio : Lower Threshold Ratio ( default : 0.2)
## flipimage-module
This module is used for flipping the image on the selected axis.
#### Usage
```js
sequencer.loadImage('PATH')
.addSteps('flip-image',options)
.run()
```
where `options` is an object with the following properties:
* Axis : select the required axis (default : vertical)
## gamma-correction-module
This module is used for applying gamma correction.
@@ -386,6 +463,24 @@ where `options` is an object with the following properties:
* y : Y-position of the image on which the new image is overlayed (default 0)
* offset : offset to the step on which the output of the last step is overlayed (default -2)
## paint-bucket-module
This module fills any polygonal shape with the specified color in pixels.
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('paint-bucket',options)
.run()
```
where `options` is an object with the following property:
* startingX : it is the value of the starting x-cordinate (default 10)
* startingY : it is the value of the starting y-cordinate (default 10)
* fillColor : they are four spaced seperated numbers representing the RGBA values of fill-color (default "100 100 100 255")
* tolerance : it is the % tolerance (default 10)
## resize-module
This module is used for resizing an image.
@@ -400,6 +495,22 @@ This module is used for resizing an image.
where `options` is an object with the following property:
* resize : Percentage value of resize (default 125%)
## replacecolor-module
This module is used in replacing the color with grey or any desired color.
#### Usage
```js
sequencer.loadImage('PATH')
.addSteps('replace-color',options)
.run()
```
where `options` is an object with the following properties:
* replaceMethod : replaces with the desired color (default greyscale)
* replaceColor : three space separated numbers representing the RGB values of color to be filled (default "0 0 255")
* color : three space separated numbers representing the RGB values of color to be replaced (default "228 86 81")
* tolerance : it is the % tolerance (default 50)
## rotate-module
This module is used for rotating an image.
@@ -428,4 +539,49 @@ This module is used for changing the saturation of the image.
where `options` is an object with the following property:
* saturation : saturation for the new image (between 0 and 2; default 0)
## Threshold
Thresholding is used to create binary images.
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('threshold',options)
.run()
```
where `options` is an object with the following property:
* threshold : it allows to select the types of thresholding (default "manual thresholding")
* input : it allows to select the range of thresholding value (default 120)
## Tint
It adds color tint to an image
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('tint',options)
.run()
```
where `options` is an object with the following property:
* color : RGB values seperated by a space (default "0 0 255")
* factor : amount of tint (default 0.5)
## White Balance
This Change the colour balance of the image by adjusting the colour temperature.
## Usage
```js
sequencer.loadImage('PATH')
.addSteps('white-balance',options)
.run()
```
where `options` is an object with the following property:
* temperature : temperature between 0 - 40,000 kelvin (default 6000)

View File

@@ -42,11 +42,11 @@ h1 {
margin: 0 !important;
}
.form-control {
.form-control[type="range"] {
padding: 0px 0px;
}
#dropzone {
.dropzone {
padding: 30px;
margin: 0 20% 30px;
border: 4px dashed #ccc;
@@ -59,7 +59,7 @@ h1 {
background: #eee;
}
#dropzone input {
.dropzone input {
max-width: 100%;
}
@@ -100,6 +100,11 @@ h1 {
color: #444;
}
.load-spin{
position: absolute;
right: 0px;
}
#addStep {
max-width: 500px;
margin: 20px auto;
@@ -128,8 +133,8 @@ h1 {
}
#save-seq {
display: block;
margin: 0 auto;
min-width: 250px;
margin: 0px 10px 0px 0px;
width: 250px;
}
.info {
padding: 8px;
@@ -137,8 +142,8 @@ h1 {
}
#gif {
display: block;
margin: 0 auto;
min-width: 250px;
margin: 0px 0px 0px 10px;
width: 250px;
}
#dwnld {
max-width: 500px;
@@ -153,25 +158,19 @@ h1 {
height: auto;
}
.move-up {
#move-up {
position: fixed;
bottom: 50px;
right: 40px;
z-index: 3;
z-index: 550;
display: none;
z-index:1000;
}
.move-up button {
background:transparent;
border:none;
}
.move-up button:active:hover {
padding-right:4px !important;
margin-right:2px;
}
.move-up i {
#move-up i {
font-size:60px;
opacity:0.5;
opacity:0.7;
color:#BABABA;
}
@@ -199,24 +198,35 @@ h1 {
.radio-group {
margin-bottom: 20px;
overflow: auto;
}
#stepRemovedNotification {
.no-border {
border: 0px;
}
.savesequencemsg{
display: none;
text-align: center;
}
.notification {
background-color: #808b96;
padding:4px;
color:white;
border-radius:3px;
font-size:2rem;
position:fixed;
bottom:8px;
left:45%;
bottom:2px;
left:50%;
min-width:14rem;
text-align:center;
display:none;
transform: translate(-50%, -50%);
}
.no-border {
border: 0px;
#resetButton {
margin-top: 20px;
}
.i-over {
@@ -230,3 +240,19 @@ h1 {
.i-small {
left: 25px;
}
@media all and (max-width: 767px) {
.center-align {
justify-content: space-between;
}
}
a.name-header{
text-decoration: none;
color: #445;
}
.step-column{
display:flex;
align-content: center;
justify-content: center;
}

View File

@@ -1,41 +1,16 @@
var defaultHtmlSequencerUi = require('./lib/defaultHtmlSequencerUi.js'),
setupCache = require('./lib/cache.js'),
intermediateHtmlStepUi = require('./lib/intermediateHtmlStepUi.js'),
DefaultHtmlStepUi = require('./lib/defaultHtmlStepUi.js'),
urlHash = require('./lib/urlHash.js'),
insertPreview = require('./lib/insertPreview.js');
window.onload = function() {
function generatePreview(previewStepName, customValues, path) {
var previewSequencer = ImageSequencer();
function insertPreview(src) {
var img = document.createElement('img');
img.classList.add('img-thumbnail')
img.classList.add('no-border');
img.src = src;
$(img).css("max-width", "200%");
$(img).css("transform", "translateX(-20%)");
var stepDiv = $('#addStep .row').find('div').each(function() {
if ($(this).find('div').attr('data-value') === previewStepName) {
$(this).find('div').append(img);
}
});
}
function loadPreview() {
previewSequencer = previewSequencer.addSteps('resize', { resize: "40%" });
if (previewStepName === "crop") {
console.log(customValues);
previewSequencer.addSteps(previewStepName, customValues).run(insertPreview);
}
else {
previewSequencer.addSteps(previewStepName, { [previewStepName]: customValues }).run(insertPreview);
}
}
previewSequencer.loadImage(path, loadPreview);
}
sequencer = ImageSequencer();
function refreshOptions() {
// Load information of all modules (Name, Inputs, Outputs)
var modulesInfo = sequencer.modulesInfo();
console.log(modulesInfo)
var addStepSelect = $("#addStep select");
addStepSelect.html("");
@@ -49,35 +24,63 @@ window.onload = function() {
}
// Null option
addStepSelect.append('<option value="none" disabled selected>More modules...</option>');
addStepSelect.selectize({
sortField: 'text'
});
}
refreshOptions();
$(window).on('scroll', scrollFunction);
function scrollFunction() {
var shouldDisplay = $('body').scrollTop() > 20 || $(':root').scrollTop() > 20;
$('#move-up').css({
display: shouldDisplay ? 'block' : 'none'
});
}
function topFunction() {
$('body').animate({scrollTop: 0});
$(':root').animate({scrollTop: 0});
}
$('#move-up').on("click",topFunction);
// UI for each step:
sequencer.setUI(DefaultHtmlStepUi(sequencer));
// UI for the overall demo:
var ui = DefaultHtmlSequencerUi(sequencer);
var ui = defaultHtmlSequencerUi(sequencer);
// find any `src` parameters in URL hash and attempt to source image from them and run the sequencer
if (getUrlHashParameter('src')) {
sequencer.loadImage(getUrlHashParameter('src'), ui.onLoad);
if (urlHash.getUrlHashParameter('src')) {
sequencer.loadImage(urlHash.getUrlHashParameter('src'), ui.onLoad);
} else {
sequencer.loadImage("images/tulips.png", ui.onLoad);
}
var resetSequence = function(){
var r=confirm("Do you want to reset the sequence?");
if (r)
window.location = "/";
}
$("#addStep select").on("change", ui.selectNewStepUi);
$("#addStep #add-step-btn").on("click", ui.addStepUi);
$("#resetButton").on("click",resetSequence);
//Module button radio selection
$('.radio-group .radio').on("click", function() {
$(this).parent().find('.radio').removeClass('selected');
$(this).addClass('selected');
newStep = $(this).attr('data-value');
console.log(newStep);
//$("#addStep option[value=" + newStep + "]").attr('selected', 'selected');
$("#addStep select").val(newStep);
ui.selectNewStepUi();
ui.addStepUi();
ui.selectNewStepUi(newStep);
ui.addStepUi(newStep);
$(this).removeClass('selected');
});
@@ -86,11 +89,23 @@ window.onload = function() {
return false;
});
function displayMessageOnSaveSequence(){
$(".savesequencemsg").fadeIn();
setTimeout(function() {
$(".savesequencemsg").fadeOut();
}, 1000);
}
$('body').on('click', 'button.remove', ui.removeStepUi);
$('#save-seq').click(() => {
sequencer.saveSequence(window.prompt("Please give a name to your sequence..."), sequencer.toString());
sequencer.loadModules();
refreshOptions();
var result = window.prompt("Please give a name to your sequence... (Saved sequence will only be available in this browser).");
if(result){
result = result + " (local)";
sequencer.saveSequence(result, sequencer.toString());
sequencer.loadModules();
displayMessageOnSaveSequence();
refreshOptions();
}
});
var isWorkingOnGifGeneration = false;
@@ -174,81 +189,35 @@ window.onload = function() {
takePhotoSelector: "#take-photo",
onLoad: function onFileReaderLoad(progress) {
var reader = progress.target;
var step = sequencer.images.image1.steps[0];
var step = sequencer.steps[0];
var util= intermediateHtmlStepUi(sequencer);
step.output.src = reader.result;
sequencer.run({ index: 0 });
step.options.step.imgElement.src = reader.result;
updatePreviews(reader.result);
if(typeof step.options !=="undefined")
step.options.step.imgElement.src = reader.result;
else
step.imgElement.src = reader.result;
insertPreview.updatePreviews(reader.result,'addStep');
insertPreview.updatePreviews(sequencer.steps[0].imgElement.src,'insertStep');
},
onTakePhoto: function (url) {
var step = sequencer.images.image1.steps[0];
var step = sequencer.steps[0];
step.output.src = url;
sequencer.run({ index: 0 });
step.options.step.imgElement.src = url;
if(typeof step.options !=="undefined")
step.options.step.imgElement.src = url;
else
step.imgElement.src = url;
insertPreview.updatePreviews(url,'addStep');
insertPreview.updatePreviews(sequencer.steps[0].imgElement.src,'insertStep');
}
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
.then(function(registration) {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
console.log(installingWorker)
if (installingWorker.state === 'installed') {
location.reload();
}
}
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function(error) {
console.log('Service worker registration failed, error:', error);
});
}
setupCache();
if ('serviceWorker' in navigator) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
$("#clear-cache").append(" " + cacheName);
});
});
}
$("#clear-cache").click(function() {
if ('serviceWorker' in navigator) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
location.reload();
});
function updatePreviews(src) {
$('#addStep img').remove();
var previewSequencerSteps = {
"brightness": "20",
"saturation": "5",
"rotate": 90,
"contrast": 90,
"crop": {
"x": 0,
"y": 0,
"w": "(50%)",
"h": "(50%)",
"noUI": true
}
}
Object.keys(previewSequencerSteps).forEach(function(step, index) {
generatePreview(step, Object.values(previewSequencerSteps)[index], src);
});
}
if (getUrlHashParameter('src')) {
updatePreviews(getUrlHashParameter('src'));
if (urlHash.getUrlHashParameter('src')) {
insertPreview.updatePreviews(urlHash.getUrlHashParameter('src'),'addStep');
} else {
updatePreviews("images/tulips.png");
insertPreview.updatePreviews("images/tulips.png",'addStep');
}
};
};

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,12 @@
<meta name="theme-color" content="#428bca">
<link rel="icon" sizes="192x192" href="../icons/ic_192.png">
<link rel="manifest" href="manifest.json">
<!--Adding meta Tag for search engine optimisation-->
<meta property="og:description" content="A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it creates a new image at each step in a sequence.">
<meta property="og:site_name" content="sequencer.publiclab.org">
<meta property="og:url" content="https://sequencer.publiclab.org">
<meta property="og:title" content="Image Sequencer">
<title>Image Sequencer</title>
@@ -17,36 +23,33 @@
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../src/ui/prepareDynamic.js"></script>
<script src="../dist/image-sequencer.js" charset="utf-8"></script>
<script src="lib/urlHash.js" charset="utf-8"></script>
<script src="lib/defaultHtmlStepUi.js" charset="utf-8"></script>
<script src="lib/defaultHtmlSequencerUi.js" charset="utf-8"></script>
<script src="lib/intermediateHtmlStepUi.js" charset="utf-8"></script>
<script src="demo.js" charset="utf-8"></script>
<script src="../dist/image-sequencer-ui.js" charset="utf-8"></script>
<!-- for crop module: -->
<script src="../node_modules/imgareaselect/jquery.imgareaselect.dev.js"></script>
<script src="gifshot.js" type="text/javascript"></script>
<script src="../node_modules/gifshot/dist/gifshot.min.js" type="text/javascript"></script>
<!-- Download.js for large files -->
<script src="../node_modules/downloadjs/download.min.js" type="text/javascript"/>
<script src="lib/scrollToTop.js"></script>
<script src="../node_modules/selectize/dist/js/standalone/selectize.min.js"></script>
</head>
<body>
<link href="../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="demo.css">
<link href="../node_modules/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="../node_modules/selectize/dist/css/selectize.default.css" rel="stylesheet">
<!-- for crop module: -->
<link href="../node_modules/imgareaselect/distfiles/css/imgareaselect-default.css" rel="stylesheet">
<link href="./selectize.default.css" rel="stylesheet">
<link rel="stylesheet" href="demo.css">
<div class="container-fluid">
<header class="text-center">
<h1>Image Sequencer</h1>
<h1><a href="/" target='_blank' class="name-header">Image Sequencer</a></h1>
<p>
A pure JavaScript sequential image processing system, inspired by storyboards. Instead of modifying the original image, it
creates a new image at each step in a sequence.
@@ -61,13 +64,13 @@
</p>
</header>
<div id="dropzone">
<div id="dropzone" class="dropzone">
<p>
<i>Select or drag in an image to start!</i>
</p>
<center>
<input type="file" id="fileInput" value="" accept="image/*"><br />
<button type="button" id="take-photo">Take a Photo</button>
<button type="button" id="take-photo" class="btn btn-primary btn-block btn-lg ">Take a Photo</button>
<video id="video" width="400" height="300" style="display:none"></video>
<a href="#" id="capture" style="display:none" class="btn btn-primary btn-md">Click Picture</a>
<a href="#" id="close" style="display:none" class="btn btn-default btn-md">Close</a>
@@ -80,13 +83,22 @@
</section>
<hr />
<p class = "alert alert-success savesequencemsg">Saved Sequence Success. Sequence can be found among other modules.</p>
<div class="row">
<div class="col-sm-8">
<section id="addStep" class="panel panel-primary">
<div class="form-inline">
<div class="panel-body">
<div style="display:flex; justify-content:center;">
</div>
<p class="info">Select a new module to add to your sequence.</p>
<div class="row center-align radio-group">
<div>
<div class="radio" data-value="resize">
<i class="fa fa-arrows-alt fa-4x i-over"></i>
</div>
<p>Resize</p>
</div>
<div>
<div class="radio" data-value="brightness">
<i class="fa fa-sun-o fa-4x i-over"></i>
@@ -119,12 +131,15 @@
</div>
</div>
<div class="center-align">
<select class="form-control input-lg" id="selectStep">
<select id="selectStep" >
<!-- The default null selection has been appended manually in demo.js
This is because the options in select are overritten when options are appended.-->
</select>
<button class="btn btn-success btn-lg" name="add" id="add-step-btn">Add Step</button>
</div>
<div class="row center-align">
<button id="resetButton" class="btn btn-default btn-lg" style=" margin: 20px 35px 0px 35px; width:100%;">Clear All Steps</button>
</div>
</div>
</div>
</section>
@@ -133,8 +148,9 @@
<div class="panel-body">
<div class="row center-align">
<button class="btn btn-primary btn-lg" name="save-sequence" id="save-seq">Save Sequence</button>
<button class="btn btn-primary btn-lg js-view-as-gif" id="gif">View GIF</button>
<!--<button class="btn btn-primary btn-block btn-lg" name="save-sequence" id="save-seq">Save Sequence</button> -->
<!--<button class="btn btn-primary btn-block btn-lg" id="download-btn" name="download" style="display: block; margin: 0px 10px 0px 0px; width: 250px;">Download PNG</button> -->
<button class="btn btn-primary btn-block btn-lg js-view-as-gif" id="gif" style="margin: 0px 35px 0px 35px;width:100%;">View GIF</button>
</div>
@@ -168,7 +184,15 @@
<div class="form-inline">
<div class="panel-body">
<div style="text-align:center;">
<button class="btn btn-success btn-lg" id="download-btn" name="download">Download PNG</button>
<h2 style="margin-top:20px">Save</h2>
<p>Or click ARROW to save the entire sequence as a workflow for future use.</p>
<select class="form-control input-md" id="selectSaveOption" style="margin-top:20px">
<option>Save as PNG</option>
<option>Save as GIF (all steps)</option>
<option>Save sequence</option>
<option>Save sequence string</option>
</select>
<button id="saveButton" class="btn btn-primary btn-lg" style="margin-top:20px; margin-bottom:0px;align:center; width:100%;">Save</button>
</div>
</div>
</div>
@@ -176,14 +200,32 @@
</div>
</div>
<form class="move-up" action="#up">
<button><i class="fa fa-arrow-circle-o-up"></i></button>
</form>
<footer>
<hr style="margin:20px;"><center><a class="color:grey;" id="clear-cache">Clear offline cache</a></center>
<hr style="margin:20px;"><center><button class="btn btn-default btn-sm" id="clear-cache">Clear offline cache</button></center>
<div class="row">
<div class="col-md-6">
<h2>Need Help?</h2>
<p>
Post a link to this and ask help from other community members on <a href="https://github.com/publiclab/image-sequencer/issues/new">Github Issues</a>
</p>
<p>
<a class="btn btn-primary" href="https://gitter.im/publiclab/publiclab">Ask a question on Gitter</a>
</p>
</div>
<div class="col-md-6">
<h2>Improve this tool</h2>
<p>
This is an open source toolkit which you can help add to and improve on <a href="https://github.com/publiclab/image-sequencer/">Github</a>
</p>
<p>
<a class="btn btn-primary" href="https://github.com/publiclab/image-sequencer">View the code &raquo;</a>
</p>
</div>
</div>
</footer>
<button id="move-up"><i class="fa fa-arrow-circle-o-up"></i></button>
<script type="text/javascript">
$(function() {
var sequencer;

39
examples/lib/cache.js Normal file
View File

@@ -0,0 +1,39 @@
var setupCache = function() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
.then(function(registration) {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
console.log(installingWorker)
if (installingWorker.state === 'installed') {
location.reload();
}
}
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function(error) {
console.log('Service worker registration failed, error:', error);
});
}
if ('serviceWorker' in navigator) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
$("#clear-cache").append(" " + cacheName);
});
});
}
$("#clear-cache").click(function() {
if ('serviceWorker' in navigator) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
location.reload();
});
}
module.exports = setupCache;

View File

@@ -1,3 +1,4 @@
var urlHash = require('./urlHash.js');
function DefaultHtmlSequencerUi(_sequencer, options) {
options = options || {};
@@ -7,24 +8,25 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
function onLoad() {
importStepsFromUrlHash();
if (!$('#selectStep').val())
$(addStepSel + " #add-step-btn").prop("disabled", true);
if ($('#selectStep').val()==='none')
$(addStepSel + " #add-step-btn").prop("disabled", true);
handleSaveSequence();
}
// look up needed steps from Url Hash:
function importStepsFromUrlHash() {
var hash = getUrlHashParameter("steps");
var hash = urlHash.getUrlHashParameter("steps");
if (hash) {
_sequencer.importString(hash);
_sequencer.run({ index: 0 });
}
setUrlHashParameter("steps", sequencer.toString());
urlHash.setUrlHashParameter("steps", sequencer.toString());
}
function selectNewStepUi() {
var m = $(addStepSel + " select").val();
if(!m) m = arguments[0];
$(addStepSel + " .info").html(_sequencer.modulesInfo(m).description);
$(addStepSel + " #add-step-btn").prop("disabled", false);
}
@@ -33,7 +35,7 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
var index = $(removeStepSel).index(this) + 1;
sequencer.removeSteps(index).run({ index: index - 1 });
// remove from URL hash too
setUrlHashParameter("steps", sequencer.toString());
urlHash.setUrlHashParameter("steps", sequencer.toString());
//disable save-sequence button if all steps are removed
handleSaveSequence();
}
@@ -42,6 +44,8 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
if ($(addStepSel + " select").val() == "none") return;
var newStepName = $(addStepSel + " select").val();
if(!newStepName) newStepName = arguments[0]
/*
* after adding the step we run the sequencer from defined step
@@ -56,17 +60,19 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
}
_sequencer
.addSteps(newStepName, options)
.run({ index: _sequencer.images.image1.steps.length - sequenceLength - 1 });
.run({ index: _sequencer.steps.length - sequenceLength - 1 });
$(addStepSel + " .info").html("Select a new module to add to your sequence.");
$(addStepSel + " select").val("none");
//enable save-sequence button if disabled initially
handleSaveSequence();
// add to URL hash too
setUrlHashParameter("steps", _sequencer.toString());
urlHash.setUrlHashParameter("steps", _sequencer.toString())
}
function handleSaveSequence(){
var stepCount=sequencer.images.image1.steps.length;
var stepCount=sequencer.steps.length;
if(stepCount<2)
$(" #save-seq").prop("disabled", true);
else
@@ -81,3 +87,6 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
addStepUi: addStepUi
}
}
module.exports = DefaultHtmlSequencerUi;

View File

@@ -7,22 +7,18 @@
// The variable 'step' stores useful data like input and
// output values, step information.
// See documetation for more details.
function stepRemovedNotify() {
if ($('#stepRemovedNotification').length == 0) {
var notification = document.createElement('span');
notification.innerHTML = ' <i class="fa fa-info-circle" aria-hidden="true"></i> Step Removed ';
notification.id = 'stepRemovedNotification';
$('body').append(notification);
}
var intermediateHtmlStepUi = require('./intermediateHtmlStepUi.js');
var urlHash = require('./urlHash.js');
var _ = require('lodash');
var mapHtmlTypes = require('./mapHtmltypes');
$('#stepRemovedNotification').fadeIn(500).delay(200).fadeOut(500);
}
function DefaultHtmlStepUi(_sequencer, options) {
options = options || {};
var stepsEl = options.stepsEl || document.querySelector("#steps");
var selectStepSel = options.selectStepSel = options.selectStepSel || "#selectStep";
function onSetup(step, stepOptions) {
if (step.options && step.options.description)
step.description = step.options.description;
@@ -31,33 +27,38 @@ function DefaultHtmlStepUi(_sequencer, options) {
'\
<div class="container">\
<div class="row step">\
<form class="input-form">\
<div class="col-md-4 details">\
<h3>' +
step.name +
"</h3>\
<p><i>" +
<h3>\
<span class = "toggle">' +step.name + ' <i class="fa fa-caret-up toggleIcon" aria-hidden="true"></i></span>' +
'<span class="load-spin" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></span>' +
'</h3><div class="cal collapse in"><p><i>"'+
(step.description || "") +
'</i></p>\
'</i></p></div>\
</div>\
<div class="col-md-8">\
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
<a><img alt="" style="max-width=100%" class="img-thumbnail step-thumbnail"/></a>\
</form>\
<div class="col-md-8 cal collapse in step-column">\
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
<div class="step-image">\
<a class="cal collapse in"><img alt="" class="img-thumbnail step-thumbnail"/></a>\
</div>\
</div>\
</div>\
</div>\
</div>';
var tools =
'<div class="tools btn-group">\
<button confirm="Are you sure?" onclick="stepRemovedNotify()" class="remove btn btn btn-default">\
<i class="fa fa-trash"></i>\
</button>\
<button class="btn insert-step" style="margin-left:10px;border-radius:6px;background-color:#fff;border:solid #bababa 1.1px;" >\
<i class="fa fa-plus"></i> Add\
</button>\
</div>';
'<div class="cal collapse in"><div class="tools btn-group">\
<button confirm="Are you sure?" class="remove btn btn btn-default">\
<i class="fa fa-trash"></i>\
</button>\
<button class="btn insert-step" style="margin-left:10px;border-radius:6px;background-color:#fff;border:solid #bababa 1.1px;" >\
<i class="fa fa-plus"></i> Add\
</button>\
</div>\
</div>';
var util = IntermediateHtmlStepUi(_sequencer, step);
var util = intermediateHtmlStepUi(_sequencer, step);
var parser = new DOMParser();
step.ui = parser.parseFromString(step.ui, "text/html");
@@ -73,7 +74,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
for (var paramName in merged) {
var isInput = inputs.hasOwnProperty(paramName);
var html = "";
var inputDesc = isInput ? inputs[paramName] : {};
var inputDesc = isInput ? mapHtmlTypes(inputs[paramName]) : {};
if (!isInput) {
html += '<span class="output"></span>';
} else if (inputDesc.type.toLowerCase() == "select") {
@@ -101,7 +102,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
'"max="' +
inputDesc.max +
'"step="' +
inputDesc.step + '">' + '<span>' + paramVal + '</span>';
(inputDesc.step ? inputDesc.step : 1)+ '">' + '<span>' + paramVal + '</span>';
}
else html += '">';
@@ -112,8 +113,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
div.setAttribute("name", paramName);
var description = inputs[paramName].desc || paramName;
div.innerHTML =
"<div class='det'>\
<form class='input-form'>\
"<div class='det cal collapse in'>\
<label for='" +
paramName +
"'>" +
@@ -122,44 +122,15 @@ function DefaultHtmlStepUi(_sequencer, options) {
" +
html +
"\
</form>\
</div>";
step.ui.querySelector("div.details").appendChild(div);
}
function toggleSaveButton() {
$(step.ui.querySelector("div.details .btn-save")).prop("disabled", false);
focusInput();
}
$(step.ui.querySelectorAll(".target")).on('change', toggleSaveButton);
$(step.ui.querySelector("div.details")).append(
"<p><button class='btn btn-default btn-save' disabled = 'true' >Apply</button><span> Press apply to see changes</span></p>"
'<div class="cal collapse in"><p><button type="submit" class="btn btn-default btn-save" disabled = "true" >Apply</button><span> Press apply to see changes</span></p></div>'
);
function focusInput() {
$(step.ui.querySelector("div.details .target")).focus();
}
function saveOptions(e) {
e.preventDefault();
$(step.ui.querySelector("div.details"))
.find("input,select")
.each(function(i, input) {
step.options[$(input).attr("name")] = input.value;
});
_sequencer.run({ index: step.index - 1 });
// modify the url hash
setUrlHashParameter("steps", _sequencer.toString());
// disable the save button
$(step.ui.querySelector("div.details .btn-save")).prop("disabled", true);
}
// on clicking Save in the details pane of the step
$(step.ui.querySelector("div.details .btn-save")).click(saveOptions);
$(step.ui.querySelector("div.details .input-form")).on('submit', saveOptions);
}
if (step.name != "load-image") {
@@ -168,13 +139,14 @@ function DefaultHtmlStepUi(_sequencer, options) {
.appendChild(
parser.parseFromString(tools, "text/html").querySelector("div")
);
$(step.ui.querySelectorAll(".remove")).on('click', function() {notify('Step Removed','remove-notification')});
$(step.ui.querySelectorAll(".insert-step")).on('click', function() { util.insertStep(step.ID) });
// Insert the step's UI in the right place
if (stepOptions.index == _sequencer.images.image1.steps.length) {
if (stepOptions.index == _sequencer.steps.length) {
stepsEl.appendChild(step.ui);
$("#steps .container:nth-last-child(1) .insert-step").prop('disabled',true);
if($("#steps .container:nth-last-child(2)"))
if($("#steps .container:nth-last-child(2)"))
$("#steps .container:nth-last-child(2) .insert-step").prop('disabled',false);
} else {
stepsEl.insertBefore(step.ui, $(stepsEl).children()[stepOptions.index]);
@@ -183,28 +155,92 @@ function DefaultHtmlStepUi(_sequencer, options) {
else {
$("#load-image").append(step.ui);
}
$(step.ui.querySelector(".toggle")).on("click", () => {
$(step.ui.querySelector('.toggleIcon')).toggleClass('fa-caret-up').toggleClass('fa-caret-down');
$(step.ui.querySelectorAll(".cal")).collapse('toggle');
});
$(step.imgElement).on("mousemove", _.debounce(() => imageHover(step), 150));
function saveOptions(e) {
e.preventDefault();
if (optionsChanged){
$(step.ui.querySelector("div.details"))
.find("input,select")
.each(function(i, input) {
$(input)
.data('initValue', $(input).val())
.data('hasChangedBefore', false);
step.options[$(input).attr("name")] = $(input).val();
});
_sequencer.run({ index: step.index - 1 });
// modify the url hash
urlHash.setUrlHashParameter("steps", _sequencer.toString())
// disable the save button
$(step.ui.querySelector('.btn-save')).prop('disabled', true);
optionsChanged = false;
changedInputs = 0;
}
}
function handleInputValueChange(currentValue, initValue, hasChangedBefore) {
var inputChanged = !(isNaN(initValue) || isNaN(currentValue) ? currentValue === initValue : currentValue - initValue === 0);
changedInputs += hasChangedBefore ? inputChanged ? 0 : -1 : inputChanged ? 1 : 0;
optionsChanged = changedInputs > 0;
$(step.ui.querySelector('.btn-save')).prop('disabled', !optionsChanged);
return inputChanged;
}
var
changedInputs = 0,
optionsChanged = false;
$(step.ui.querySelector('.input-form')).on('submit', saveOptions);
$(step.ui.querySelectorAll('.target')).each(function(i, input) {
$(input)
.data('initValue', $(input).val())
.data('hasChangedBefore', false)
.on('input', function() {
$(this)
.focus()
.data('hasChangedBefore',
handleInputValueChange(
$(this).val(),
$(this).data('initValue'),
$(this).data('hasChangedBefore')
)
)
})
})
$('input[type="range"]').on('input', function() {
$(this).next().html($(this).val());
})
}
var inputs = document.querySelectorAll('input[type="range"]')
for (i in inputs)
inputs[i].oninput = function(e) {
e.target.nextSibling.innerHTML = e.target.value;
}
function onDraw(step) {
$(step.ui.querySelector(".load")).show();
$(step.ui.querySelector("img")).hide();
if( $(step.ui.querySelector(".toggleIcon")).hasClass("fa-caret-down") )
{
$(step.ui.querySelector(".load-spin")).show();
}
}
function onComplete(step) {
$(step.ui.querySelector(".load")).hide();
$(step.ui.querySelector("img")).show();
$(step.ui.querySelector(".load-spin")).hide();
step.imgElement.src = step.output;
step.imgElement.src = (step.name == "load-image") ? step.output.src : step.output;
var imgthumbnail = step.ui.querySelector(".img-thumbnail");
for (let index = 0; index < step.linkElements.length; index++) {
if (step.linkElements[index].contains(imgthumbnail))
step.linkElements[index].href = step.output;
step.linkElements[index].href = step.imgElement.src;
}
// TODO: use a generalized version of this
@@ -213,7 +249,8 @@ function DefaultHtmlStepUi(_sequencer, options) {
}
for (let index = 0; index < step.linkElements.length; index++) {
step.linkElements[index].download = step.name + "." + fileExtension(step.output);
step.linkElements[index].download = step.name + "." + fileExtension(step.imgElement.src);
step.linkElements[index].target = "_blank";
}
@@ -224,35 +261,81 @@ function DefaultHtmlStepUi(_sequencer, options) {
for (var i in inputs) {
if (step.options[i] !== undefined) {
if (inputs[i].type.toLowerCase() === "input")
step.ui.querySelector('div[name="' + i + '"] input').value =
step.options[i];
$(step.ui.querySelector('div[name="' + i + '"] input'))
.val(step.options[i])
.data('initValue', step.options[i]);
if (inputs[i].type.toLowerCase() === "select")
step.ui.querySelector('div[name="' + i + '"] select').value =
step.options[i];
$(step.ui.querySelector('div[name="' + i + '"] select'))
.val(step.options[i])
.data('initValue', step.options[i]);
}
}
for (var i in outputs) {
if (step[i] !== undefined)
step.ui.querySelector('div[name="' + i + '"] input').value =
step[i];
$(step.ui.querySelector('div[name="' + i + '"] input'))
.val(step[i]);
}
}
}
function imageHover(step){
var img = $(step.imgElement);
img.mousemove(function(e) {
var canvas = document.createElement('canvas');
canvas.width = img.width();
canvas.height = img.height();
var context = canvas.getContext('2d');
context.drawImage(this,0,0);
var offset = $(this).offset();
var xPos = e.pageX - offset.left;
var yPos = e.pageY - offset.top;
var myData = context.getImageData(xPos, yPos, 1, 1);
img[0].title = "rgb: " +myData.data[0]+","+ myData.data[1]+","+myData.data[2];//+ rgbdata;
});
}
function onRemove(step) {
step.ui.remove();
$("#steps .container:nth-last-child(1) .insert-step").prop('disabled',true);
$('div[class*=imgareaselect-]').remove();
}
function getPreview() {
return step.imgElement;
}
function notify(msg,id){
if ($('#'+id).length == 0) {
var notification = document.createElement('span');
notification.innerHTML = ' <i class="fa fa-info-circle" aria-hidden="true"></i> ' + msg ;
notification.id = id;
notification.classList.add("notification");
$('body').append(notification);
}
$('#'+id).fadeIn(500).delay(200).fadeOut(500);
}
return {
getPreview: getPreview,
onSetup: onSetup,
onComplete: onComplete,
onRemove: onRemove,
onDraw: onDraw
onDraw: onDraw,
notify: notify,
imageHover: imageHover
}
}
}
if(typeof window === "undefined"){
module.exports={
DefaultHtmlStepUi: DefaultHtmlStepUi
}
}
module.exports = DefaultHtmlStepUi;

View File

@@ -0,0 +1,55 @@
function generatePreview(previewStepName, customValues, path, selector) {
var previewSequencer = ImageSequencer();
function insertPreview(src) {
var img = document.createElement('img');
img.classList.add('img-thumbnail')
img.classList.add('no-border');
img.src = src;
$(img).css("max-width", "200%");
$(img).css("transform", "translateX(-20%)");
var stepDiv = $('#'+selector+' .row').find('div').each(function() {
if ($(this).find('div').attr('data-value') === previewStepName) {
$(this).find('div').append(img);
}
});
}
function loadPreview() {
if (previewStepName === "crop") {
previewSequencer.addSteps(previewStepName, customValues).run(insertPreview);
}
else {
previewSequencer.addSteps(previewStepName, { [previewStepName]: customValues }).run(insertPreview);
}
}
previewSequencer.loadImage(path, loadPreview);
}
function updatePreviews(src, selector) {
$('#'+selector+' img').remove();
var previewSequencerSteps = {
"resize": "125%",
"brightness": "175",
"saturation": "0.5",
"rotate": 90,
"contrast": 90,
"crop": {
"x": 0,
"y": 0,
"w": "(50%)",
"h": "(50%)",
"noUI": true
}
}
Object.keys(previewSequencerSteps).forEach(function (step, index) {
generatePreview(step, Object.values(previewSequencerSteps)[index], src, selector);
});
}
module.exports = {
generatePreview : generatePreview,
updatePreviews : updatePreviews
}

View File

@@ -1,40 +1,49 @@
var urlHash = require('./urlHash.js'),
insertPreview = require('./insertPreview.js');
function IntermediateHtmlStepUi(_sequencer, step, options) {
function stepUI() {
return '<div class="row insertDiv">\
<div class="col-md-6" style="margin-top:5%">\
<div class="col-md-6 col-md-offset-2" style="margin-top:5%">\
<section id="insertStep" class="panel panel-primary">\
<div class="form-inline">\
<div class="panel-body">\
<p class="info">Select a new module to add to your sequence.</p>\
<div class="row center-align radio-group">\
<div>\
<div class="radio" data-value="resize">\
<i class="fa fa-arrows-alt fa-4x i-over"></i>\
</div>\
<p>Resize</p>\
</div>\
<div>\
<button type="button" class="btn btn-default btn-circle btn-xl radio" data-value="brightness">\
<i class="fa fa-sun-o fa-4x"></i>\
</button>\
<div class="radio" data-value="brightness">\
<i class="fa fa-sun-o fa-4x i-over"></i>\
</div>\
<p>Brightness</p>\
</div>\
<div>\
<button type="button" class="btn btn-default btn-circle btn-xl radio" data-value="contrast">\
<i class="fa fa-adjust fa-4x"></i>\
</button>\
<div class="radio" data-value="contrast">\
<i class="fa fa-adjust fa-4x i-over"></i>\
</div>\
<p>Contrast</p>\
</div>\
<div>\
<button type="button" class="btn btn-default btn-circle btn-xl radio" data-value="saturation">\
<i class="fa fa-tint fa-4x"></i>\
</button>\
<div class="radio" data-value="saturation">\
<i class="fa fa-tint fa-4x i-over i-small"></i>\
</div>\
<p>Saturation</p>\
</div>\
<div>\
<button type="button" class="btn btn-default btn-circle btn-xl radio" data-value="rotate">\
<i class="fa fa-rotate-right fa-4x"></i>\
</button>\
<div class="radio" data-value="rotate">\
<i class="fa fa-rotate-right fa-4x i-over"></i>\
</div>\
<p>Rotate</p>\
</div>\
<div>\
<button type="button" class="btn btn-default btn-circle btn-xl radio" data-value="crop">\
<i class="fa fa-crop fa-4x"></i>\
</button>\
<div class="radio" data-value="crop">\
<i class="fa fa-crop fa-4x i-over"></i>\
</div>\
<p>Crop</p>\
</div>\
</div>\
@@ -50,12 +59,14 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
</section>\
</div>';
}
function selectNewStepUi() {
var m = $("#insertStep select").val();
$("#insertStep .info").html(_sequencer.modulesInfo(m).description);
$("#insertStep #add-step-btn").prop("disabled", false);
}
insertStep = function(id) {
insertStep = function (id) {
var modulesInfo = _sequencer.modulesInfo();
var parser = new DOMParser();
var addStepUI = stepUI();
@@ -65,6 +76,7 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
.insertAdjacentElement('afterend',
addStepUI
);
insertPreview.updatePreviews(step.output,'insertStep');
var insertStepSelect = $("#insertStep select");
insertStepSelect.html("");
// Add modules to the insertStep dropdown
@@ -74,11 +86,13 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
'<option value="' + m + '">' + modulesInfo[m].name + "</option>"
);
}
$('#insertStep #add-step-btn').selectize({
sortField: 'text'
});
$('#insertStep #add-step-btn').prop('disabled', true);
insertStepSelect.append('<option value="none" disabled selected>More modules...</option>');
$('#insertStep .radio-group .radio').on("click", function() {
$('#insertStep .radio-group .radio').on("click", function () {
$(this).parent().find('.radio').removeClass('selected');
$(this).addClass('selected');
newStep = $(this).attr('data-value');
@@ -88,7 +102,7 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
$(this).removeClass('selected');
});
$(step.ui.querySelector("#insertStep select")).on('change', selectNewStepUi);
$(step.ui.querySelector("#insertStep #add-step-btn")).on('click', function() { insert(id) });
$(step.ui.querySelector("#insertStep #add-step-btn")).on('click', function () { insert(id) });
}
function insert(id) {
@@ -109,10 +123,13 @@ function IntermediateHtmlStepUi(_sequencer, step, options) {
.insertSteps(id + 1, newStepName).run({ index: id });
// add to URL hash too
setUrlHashParameter("steps", _sequencer.toString());
urlHash.setUrlHashParameter("steps", _sequencer.toString());
}
return {
insertStep
}
}
}
module.exports = IntermediateHtmlStepUi;

View File

@@ -0,0 +1,28 @@
function mapHtmlTypes(inputInfo){
var htmlType;
switch(inputInfo.type.toLowerCase()){
case 'integer':
htmlType = inputInfo.min != undefined ? 'range' : 'number';
break;
case 'string':
htmlType = 'text';
break;
case 'select':
htmlType = 'select';
break;
case 'percentage':
htmlType = 'number';
break;
case 'float':
htmlType = inputInfo.min != undefined ? 'range' : 'text';
break;
default:
htmlType = 'text';
break;
}
var response = inputInfo;
response.type = htmlType;
return response;
}
module.exports = mapHtmlTypes;

View File

@@ -1,17 +0,0 @@
$(document).ready(function($){
$(function(){
$(window).scroll(function(){
if ($(this).scrollTop() > 100){
$('.move-up').fadeIn();
} else {
$('.move-up').fadeOut();
}
});
$('.move-up button').click(function(){
$('body,html').animate({
scrollTop: 0
}, 800);
return false;
});
});
});

View File

@@ -40,3 +40,10 @@ function setUrlHashParameter(param, value) {
setUrlHashParameters(params);
}
module.exports = {
getUrlHashParameter: getUrlHashParameter,
setUrlHashParameter: setUrlHashParameter,
getUrlHashParameters: getUrlHashParameters,
setUrlHashParameters: setUrlHashParameters
}

View File

@@ -0,0 +1,403 @@
/**
* selectize.default.css (v0.12.6) - Default Theme
* Copyright (c) 20132015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
visibility: visible !important;
background: #f2f2f2 !important;
background: rgba(0, 0, 0, 0.06) !important;
border: 0 none !important;
-webkit-box-shadow: inset 0 0 12px 4px #fff;
box-shadow: inset 0 0 12px 4px #fff;
}
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
content: '!';
visibility: hidden;
}
.selectize-control.plugin-drag_drop .ui-sortable-helper {
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown-header {
position: relative;
padding: 5px 8px;
border-bottom: 1px solid #d0d0d0;
background: #f8f8f8;
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-dropdown-header-close {
position: absolute;
right: 8px;
top: 50%;
color: #303030;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.selectize-dropdown-header-close:hover {
color: #000000;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
border-right: 0 none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
display: none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
border-top: 0 none;
}
.selectize-control.plugin-remove_button [data-value] {
position: relative;
padding-right: 24px !important;
}
.selectize-control.plugin-remove_button [data-value] .remove {
z-index: 1;
/* fixes ie bug (see #392) */
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 17px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: inherit;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 2px 0 0 0;
border-left: 1px solid #0073bb;
-webkit-border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.plugin-remove_button [data-value] .remove:hover {
background: rgba(0, 0, 0, 0.05);
}
.selectize-control.plugin-remove_button [data-value].active .remove {
border-left-color: #00578d;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
background: none;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
border-left-color: #aaaaaa;
}
.selectize-control.plugin-remove_button .remove-single {
position: absolute;
right: 0;
top: 0;
font-size: 23px;
}
.selectize-control {
position: relative;
}
.selectize-dropdown,
.selectize-input,
.selectize-input input {
color: #303030;
font-family: inherit;
font-size: 18px;
line-height: 18px;
-webkit-font-smoothing: inherit;
}
.selectize-input,
.selectize-control.single .selectize-input.input-active {
background: #fff;
cursor: text;
display: inline-block;
}
.selectize-input {
border: 1px solid #d0d0d0;
padding: 10px 0px;
display: inline-block;
width: 24vw;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.selectize-control.multi .selectize-input.has-items {
padding: 5px 8px 2px;
}
.selectize-input.full {
background-color: #fff;
}
.selectize-input.disabled,
.selectize-input.disabled * {
cursor: default !important;
}
.selectize-input.focus {
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.selectize-input.dropdown-active {
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-input > * {
vertical-align: baseline;
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
}
.selectize-control.multi .selectize-input > div {
cursor: pointer;
margin: 0 3px 3px 0;
padding: 2px 6px;
background: #1da7ee;
color: #fff;
border: 1px solid #0073bb;
}
.selectize-control.multi .selectize-input > div.active {
background: #92c836;
color: #fff;
border: 1px solid #00578d;
}
.selectize-control.multi .selectize-input.disabled > div,
.selectize-control.multi .selectize-input.disabled > div.active {
color: #ffffff;
background: #d2d2d2;
border: 1px solid #aaaaaa;
}
.selectize-input > input {
display: inline-block !important;
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
margin: 0 1px !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
-webkit-user-select: auto !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.selectize-input > input::-ms-clear {
display: none;
}
.selectize-input > input:focus {
outline: none !important;
}
.selectize-input::after {
content: ' ';
display: block;
clear: left;
}
.selectize-input.dropdown-active::before {
content: ' ';
display: block;
position: absolute;
background: #f0f0f0;
height: 1px;
bottom: 0;
left: 0;
right: 0;
}
.selectize-dropdown {
position: absolute;
z-index: 10;
border: 1px solid #d0d0d0;
background: #fff;
margin: -1px 0 0 0;
border-top: 0 none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 0 0 3px 3px;
-moz-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
}
.selectize-dropdown [data-selectable] {
cursor: pointer;
overflow: hidden;
}
.selectize-dropdown [data-selectable] .highlight {
background: rgba(125, 168, 208, 0.2);
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
}
.selectize-dropdown .option,
.selectize-dropdown .optgroup-header {
padding: 5px 8px;
}
.selectize-dropdown .option,
.selectize-dropdown [data-disabled],
.selectize-dropdown [data-disabled] [data-selectable].option {
cursor: inherit;
opacity: 0.5;
}
.selectize-dropdown [data-selectable].option {
opacity: 1;
}
.selectize-dropdown .optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.selectize-dropdown .optgroup-header {
color: #303030;
background: #fff;
cursor: default;
}
.selectize-dropdown .active {
background-color: #f5fafd;
color: #495c68;
}
.selectize-dropdown .active.create {
color: #495c68;
}
.selectize-dropdown .create {
color: rgba(48, 48, 48, 0.5);
}
.selectize-dropdown-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
-webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
cursor: pointer;
}
.selectize-control.single .selectize-input.input-active,
.selectize-control.single .selectize-input.input-active input {
cursor: text;
}
.selectize-control.single .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #808080 transparent transparent transparent;
}
.selectize-control.single .selectize-input.dropdown-active:after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #808080 transparent;
}
.selectize-control.rtl.single .selectize-input:after {
left: 15px;
right: auto;
}
.selectize-control.rtl .selectize-input > input {
margin: 0 4px 0 -2px !important;
}
.selectize-control .selectize-input.disabled {
opacity: 0.5;
background-color: #fafafa;
}
.selectize-control.multi .selectize-input.has-items {
padding-left: 5px;
padding-right: 5px;
}
.selectize-control.multi .selectize-input.disabled [data-value] {
color: #999;
text-shadow: none;
background: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.selectize-control.multi .selectize-input.disabled [data-value],
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
border-color: #e6e6e6;
}
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
background: none;
}
.selectize-control.multi .selectize-input [data-value] {
text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background-color: #1b9dec;
background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
}
.selectize-control.multi .selectize-input [data-value].active {
background-color: #0085d4;
background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
background-image: -o-linear-gradient(top, #008fd8, #0075cf);
background-image: linear-gradient(to bottom, #008fd8, #0075cf);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
}
.selectize-control.single .selectize-input {
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
background-color: #f9f9f9;
background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
}
/* .selectize-control.single .selectize-input,
.selectize-dropdown.single {
border-color: #b8b8b8;
} */
.selectize-dropdown .optgroup-header {
padding-top: 7px;
font-weight: bold;
font-size: 0.85em;
}
.selectize-dropdown .optgroup {
border-top: 1px solid #f0f0f0;
}
.selectize-dropdown .optgroup:first-child {
border-top: 0 none;
}

158
index.js
View File

@@ -1,11 +1,14 @@
#!/usr/bin/env node
require("./src/ImageSequencer");
require('./src/ImageSequencer');
sequencer = ImageSequencer({ ui: false });
var Spinner = require("ora");
var fs = require('fs')
var program = require('commander');
var utils = require('./src/CliUtils')
var program = require("commander");
var readlineSync = require("readline-sync");
var saveSequence = require('./src/cli/saveSequence.js')
var installModule = require('./src/cli/installModule.js')
var sequencerSteps = require('./src/cli/sequencerSteps.js')
function exit(message) {
console.error(message);
@@ -23,24 +26,11 @@ program
.option('--install-module [string]', "Module name space seaprated npm package name")
.parse(process.argv);
if (program.saveSequence) {
var params = program.saveSequence.split(' ');
sequencer.saveSequence(params[0], params[1]);
console.log("\x1b[32m", "Your sequence was saved successfully!!");
} else if (program.installModule) {
console.log(
"\x1b[33m%s\x1b[0m",
"Please wait while your Module is being Installed...\nThis may take a while!"
);
var params = program.installModule.split(' ');
var spinner = Spinner("Now Installing...").start();
require('child_process').execSync(`npm i ${params[1]}`)
sequencer.saveNewModule(params[0], params[1]);
sequencer.loadNewModule(params[0], require(params[1]));
spinner.stop();
console.log("\x1b[32m%s\x1b[0m", "Your module was installed successfully!!");
} else {
if (program.saveSequence) saveSequence(program, sequencer)
else if (program.installModule) installModule(program, sequencer)
else {
// Parse step into an array to allow for multiple steps.
if (!program.step) exit("No steps passed");
program.step = program.step.split(" ");
@@ -49,12 +39,12 @@ if (program.saveSequence) {
if (!program.image) exit("Can't read file.");
// User must input an image.
require("fs").access(program.image, function(err) {
fs.access(program.image, function(err) {
if (err) exit("Can't read file.");
});
// User must input a step. If steps exist, check that every step is a valid step.
if (!program.step || !validateSteps(program.step))
if (!program.step || !(utils.validateSteps(program.step, sequencer)))
exit("Please ensure all steps are valid.");
// If there's no user defined output directory, select a default directory.
@@ -69,6 +59,9 @@ if (program.saveSequence) {
for (var output in step.info.outputs) {
console.log("[" + program.step + "]: " + output + " = " + step[output]);
}
},
notify: function(msg) {
console.log('\x1b[36m%s\x1b[0m','🌟 '+msg);
}
});
@@ -88,124 +81,9 @@ if (program.saveSequence) {
else {
outputFilename = null;
}
require("./src/CliUtils").makedir(program.output, () => {
console.log('Files will be exported to "' + program.output + '"');
if (program.basic)
console.log("Basic mode is enabled, outputting only final image");
sequencerSteps(program, sequencer, outputFilename)
// Iterate through the steps and retrieve their inputs.
program.step.forEach(function(step) {
var options = Object.assign({}, sequencer.modulesInfo(step).inputs);
// If inputs exists, print to console.
if (Object.keys(options).length) {
console.log("[" + step + "]: Inputs");
}
// If inputs exists, print them out with descriptions.
Object.keys(options).forEach(function(input) {
// The array below creates a variable number of spaces. This is done with (length + 1).
// The extra 4 that makes it (length + 5) is to account for the []: characters
console.log(
new Array(step.length + 5).join(" ") +
input +
": " +
options[input].desc
);
});
if (program.config) {
try {
var config = JSON.parse(program.config);
console.log(`The parsed options object: `, config);
} catch (e) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options(Config) is not a not valid JSON Fallback activate`
);
program.config = false;
console.log(e);
}
}
if (program.config && validateConfig(config, options)) {
console.log("Now using Options object");
Object.keys(options).forEach(function(input) {
options[input] = config[input];
});
} else {
// If inputs exist, iterate through them and prompt for values.
Object.keys(options).forEach(function(input) {
var value = readlineSync.question(
"[" +
step +
"]: Enter a value for " +
input +
" (" +
options[input].type +
", default: " +
options[input].default +
"): "
);
options[input] = value;
});
}
// Add the step and its inputs to the sequencer.
sequencer.addSteps(step, options);
});
var spinnerObj = { succeed: () => { }, stop: () => { } };
if (!process.env.TEST)
spinnerObj = Spinner("Your Image is being processed..").start();
// Run the sequencer.
sequencer.run({ progressObj: spinnerObj }, function() {
// Export all images or final image as binary files.
sequencer.exportBin(program.output, program.basic, outputFilename);
//check if spinner was not overriden stop it
if (!spinnerObj.overrideFlag) {
spinnerObj.succeed();
console.log(`\nDone!!`);
}
});
});
});
// Takes an array of steps and checks if they are valid steps for the sequencer.
function validateSteps(steps) {
// Assume all are valid in the beginning.
var valid = true;
steps.forEach(function(step) {
// If any step in the array is not valid (not a property of modulesInfo), set valid to false.
if (!sequencer.modulesInfo().hasOwnProperty(step)) {
valid = false;
}
});
// Return valid. (If all of the steps are valid properties, valid will have remained true).
return valid;
}
//Takes config and options object and checks if all the keys exist in config
function validateConfig(config_, options_) {
options_ = Object.keys(options_);
if (
(function() {
for (var input in options_) {
if (!config_[options_[input]]) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options Object does not have the required details "${
options_[input]
}" not specified. Fallback case activated`
);
return false;
}
}
})() == false
)
return false;
else return true;
}
}

1724
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,13 @@
{
"name": "image-sequencer",
"version": "2.2.3",
"version": "3.0.1",
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
"main": "src/ImageSequencer.js",
"scripts": {
"debug": "TEST=true node ./index.js -i ./examples/images/monarch.png -s invert",
"test": "TEST=true tape test/**/*.js test/*.js | tap-spec; browserify test/modules/image-sequencer.js test/modules/chain.js test/modules/meta-modules.js test/modules/replace.js test/modules/import-export.js test/modules/run.js test/modules/dynamic-imports.js | tape-run --render=\"tap-spec\"",
"setup": "npm i && npm i -g grunt grunt-cli",
"test": "TEST=true tape test/core/*.js test/core/ui/user-interface.js test/core/modules/QR.js | tap-spec; browserify test/core/modules/image-sequencer.js test/core/modules/chain.js test/core/modules/meta-modules.js test/core/modules/replace.js test/core/modules/import-export.js test/core/modules/run.js test/core/modules/dynamic-imports.js test/core/util/parse-input.js test/core/modules/benchmark.js| tape-run --render=\"tap-spec\"",
"test-ui": "jasmine test/spec/*.js",
"setup": "npm i && npm i -g grunt grunt-cli && grunt build",
"start": "grunt serve"
},
"repository": {
@@ -31,20 +32,25 @@
"fisheyegl": "^0.1.2",
"font-awesome": "~4.7.0",
"get-pixels": "~3.3.0",
"gifshot": "^0.4.5",
"image-sequencer-invert": "^1.0.0",
"imagejs": "0.0.9",
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
"jquery": "^3.3.1",
"jsqr": "^0.2.2",
"lodash": "^4.17.5",
"jsdom": "^13.1.0",
"jsqr": "^1.1.1",
"lodash": "^4.17.11",
"ndarray-gaussian-filter": "^1.0.0",
"ora": "^3.0.0",
"pace": "0.0.4",
"qrcode": "^1.3.3",
"readline-sync": "^1.4.7",
"save-pixels": "~2.3.4",
"selectize": "^0.12.6",
"urify": "^2.1.1"
},
"devDependencies": {
"base64-img": "^1.0.4",
"browserify": "16.2.3",
"grunt": "^1.0.3",
"grunt-browser-sync": "^2.2.0",
@@ -54,14 +60,17 @@
"grunt-contrib-watch": "^1.1.0",
"image-filter-core": "~2.0.2",
"image-filter-threshold": "~2.0.1",
"looks-same": "^4.1.0",
"jasmine-core": "^3.3.0",
"jasmine-jquery": "^2.1.1",
"jasmine-spec-reporter": "^4.2.1",
"looks-same": "^7.0.0",
"matchdep": "^2.0.0",
"tap-spec": "^5.0.0",
"tape": ">=4.7.0",
"tape": "^4.9.2",
"tape-run": "^5.0.0",
"uglify-es": "^3.3.7"
},
"homepage": "https://github.com/publiclab/image-sequencer",
"homepage": "https://sequencer.publiclab.org",
"bin": {
"sequencer": "./index.js"
}

View File

@@ -0,0 +1,42 @@
describe('Default sequencer HTML', function() {
var DefaultHtmlSequencerUi = require('../examples/lib/defaultHtmlSequencerUi')
var sequencer = require('../src/ImageSequencer')()
var defaultHtmlSequencerUi;
beforeEach(()=>{
defaultHtmlSequencerUi = new DefaultHtmlSequencerUi(sequencer)
spyOn(defaultHtmlSequencerUi,'onLoad')
spyOn(defaultHtmlSequencerUi,'selectNewStepUi')
spyOn(defaultHtmlSequencerUi,'removeStepUi')
spyOn(defaultHtmlSequencerUi,'addStepUi')
spyOn(defaultHtmlSequencerUi,'importStepsFromUrlHash')
defaultHtmlSequencerUi.onLoad()
defaultHtmlSequencerUi.selectNewStepUi()
defaultHtmlSequencerUi.addStepUi()
defaultHtmlSequencerUi.removeStepUi()
defaultHtmlSequencerUi.importStepsFromUrlHash()
})
it('load default ui', function() {
expect(defaultHtmlSequencerUi.onLoad).toHaveBeenCalled()
})
it('select step ui', function() {
expect(defaultHtmlSequencerUi.selectNewStepUi).toHaveBeenCalled()
})
it('add step ui', function() {
expect(defaultHtmlSequencerUi.addStepUi).toHaveBeenCalled()
})
it('remove step ui', function() {
expect(defaultHtmlSequencerUi.removeStepUi).toHaveBeenCalled()
})
it('import options from url', function() {
expect(defaultHtmlSequencerUi.importStepsFromUrlHash).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,61 @@
var { JSDOM } = require('jsdom');
var DOM = new JSDOM(`<body></body>`);
global.document = DOM.window.document;
describe('Sequencer step HTML', function() {
var DefaultHtmlStepUi = require('../examples/lib/defaultHtmlStepUi')
var sequencer = require('../src/ImageSequencer')()
var defaultHtmlStepUi;
var step = 'brightness'
var options = {
name: "Brightness",
description: "Change the brightness of the image by given percent value"
}
// options = JSON.parse(options)
beforeEach(()=>{
defaultHtmlStepUi = new DefaultHtmlStepUi(sequencer)
spyOn(defaultHtmlStepUi,'getPreview')
spyOn(defaultHtmlStepUi,'onSetup')
spyOn(defaultHtmlStepUi,'onComplete')
spyOn(defaultHtmlStepUi,'onDraw')
spyOn(defaultHtmlStepUi,'onRemove')
spyOn(defaultHtmlStepUi,'notify')
defaultHtmlStepUi.getPreview()
defaultHtmlStepUi.onSetup(step,options)
defaultHtmlStepUi.onComplete(step)
defaultHtmlStepUi.onDraw(step)
defaultHtmlStepUi.onRemove(step)
defaultHtmlStepUi.notify('Step removed','remove-notification')
})
it('result preview ui', function() {
expect(defaultHtmlStepUi.getPreview).toHaveBeenCalled()
})
it('load initial setup ui', function() {
expect(defaultHtmlStepUi.onSetup).toHaveBeenCalledWith(step,options)
})
it('load completion ui', function() {
expect(defaultHtmlStepUi.onComplete).toHaveBeenCalledWith(step)
})
it('draw step ui', function() {
expect(defaultHtmlStepUi.onDraw).toHaveBeenCalledWith(step)
})
it('remove step ui', function() {
expect(defaultHtmlStepUi.onRemove).toHaveBeenCalledWith(step)
})
it('notification ui', function() {
expect(defaultHtmlStepUi.notify).toHaveBeenCalledWith('Step removed','remove-notification')
})
})

8
spec/helpers/reporter.js Normal file
View File

@@ -0,0 +1,8 @@
var SpecReporter = require('jasmine-spec-reporter').SpecReporter;
jasmine.getEnv().clearReporters(); // remove default reporter logs
jasmine.getEnv().addReporter(new SpecReporter({ // add jasmine-spec-reporter
spec: {
displayPending: true
}
}));

View File

@@ -0,0 +1,26 @@
describe('Preview UI HTML', function() {
var InsertPreview = require('../examples/lib/insertPreview')
var sequencer = require('../src/ImageSequencer')()
var insertPreview;
var options = { brightness: 50 }
beforeEach(()=>{
insertPreview = InsertPreview
spyOn(insertPreview,'generatePreview')
spyOn(insertPreview,'updatePreviews')
insertPreview.generatePreview('brightness',options,'src','selector')
insertPreview.updatePreviews('src','selector')
})
it('generate preview ui', function() {
expect(insertPreview.generatePreview).toHaveBeenCalledWith('brightness',options,'src','selector')
})
it('update preview ui', function() {
expect(insertPreview.updatePreviews).toHaveBeenCalledWith('src','selector')
})
})

View File

@@ -0,0 +1,19 @@
describe('Intermediate step HTML', function() {
var IntermediateHtmlStepUi = require('../examples/lib/intermediateHtmlStepUi')
var sequencer = require('../src/ImageSequencer')()
var intermediateHtmlStepUi;
beforeEach(()=>{
intermediateHtmlStepUi = new IntermediateHtmlStepUi(sequencer)
spyOn(intermediateHtmlStepUi,'insertStep')
intermediateHtmlStepUi.insertStep()
})
it('insert step ui', function() {
expect(intermediateHtmlStepUi.insertStep).toHaveBeenCalled()
})
})

11
spec/support/jasmine.json Normal file
View File

@@ -0,0 +1,11 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}

40
spec/urlHash.spec.js Normal file
View File

@@ -0,0 +1,40 @@
describe('URL manipulation methods', function() {
var UrlHash = require('../examples/lib/urlHash')
var urlHash;
var params = {
module: 'brightness',
brightness: 50
}
beforeEach(()=>{
urlHash = UrlHash
spyOn(urlHash,'getUrlHashParameters')
spyOn(urlHash,'getUrlHashParameter')
spyOn(urlHash,'setUrlHashParameters')
spyOn(urlHash,'setUrlHashParameter')
urlHash.getUrlHashParameters()
urlHash.getUrlHashParameter('module')
urlHash.setUrlHashParameters(params)
urlHash.setUrlHashParameter('module','brightness')
})
it('gets url hash params from window hash', function() {
expect(urlHash.getUrlHashParameters).toHaveBeenCalled()
})
it('gets url hash param from params object', function() {
expect(urlHash.getUrlHashParameter).toHaveBeenCalledWith('module')
})
it('accepts param object and sets url hash params', function() {
expect(urlHash.setUrlHashParameters).toHaveBeenCalledWith(params)
})
it('accepts param key-value pair and sets url hash params', function() {
expect(urlHash.setUrlHashParameter).toHaveBeenCalledWith('module','brightness')
})
})

View File

@@ -1,5 +1,5 @@
// add steps to the sequencer
function AddStep(_sequencer, image, name, o) {
return require('./InsertStep')(_sequencer,image,-1,name,o);
function AddStep(_sequencer, name, o) {
return require('./InsertStep')(_sequencer,-1,name,o);
}
module.exports = AddStep;

View File

@@ -1,6 +1,4 @@
const fs = require('fs')
var fs = require('fs')
/*
* This function checks if the directory exists, if not it creates one on the given path
@@ -16,6 +14,45 @@ function makedir(path,callback){
});
};
// Takes an array of steps and checks if they are valid steps for the sequencer.
function validateSteps(steps, sequencer) {
// Assume all are valid in the beginning.
var valid = true;
steps.forEach(function(step) {
// If any step in the array is not valid (not a property of modulesInfo), set valid to false.
if (!sequencer.modulesInfo().hasOwnProperty(step)) {
valid = false;
}
});
// Return valid. (If all of the steps are valid properties, valid will have remained true).
return valid;
}
//Takes config and options object and checks if all the keys exist in config
function validateConfig(config_, options_) {
options_ = Object.keys(options_);
if (
(function() {
for (var input in options_) {
if (!config_[options_[input]]) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options Object does not have the required details "${
options_[input]
}" not specified. Fallback case activated`
);
return false;
}
}
})() == false
)
return false;
else return true;
}
module.exports = exports = {
makedir: makedir
makedir: makedir,
validateSteps: validateSteps,
validateConfig: validateConfig
}

View File

@@ -32,13 +32,11 @@ module.exports = function ExportBin(dir = "./output/", ref, basic, filename) {
if (err) console.error(err)
});
if (filename && basic) {
for (var image in ref.images) {
var steps = ref.images[image].steps;
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(dir + filename, buffer, function() { });
}
var steps = ref.steps;
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(dir + filename, buffer, function() { });
}
else {
getDirectories(dir, function(dirs) {
@@ -50,21 +48,19 @@ module.exports = function ExportBin(dir = "./output/", ref, basic, filename) {
}
fs.mkdir(dir + 'sequencer' + num, function() {
var root = dir + 'sequencer' + num + '/';
for (var image in ref.images) {
var steps = ref.images[image].steps;
if (basic) {
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var steps = ref.steps;
if (basic) {
var datauri = steps.slice(-1)[0].output.src;
var ext = steps.slice(-1)[0].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root + "image" + "_" + (steps.length - 1) + "." + ext, buffer, function() { });
}
else {
for (var i in steps) {
var datauri = steps[i].output.src;
var ext = steps[i].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root + image + "_" + (steps.length - 1) + "." + ext, buffer, function() { });
}
else {
for (var i in steps) {
var datauri = steps[i].output.src;
var ext = steps[i].output.format;
var buffer = require('data-uri-to-buffer')(datauri);
fs.writeFile(root + image + "_" + i + "." + ext, buffer, function() { });
}
fs.writeFile(root + "image" + "_" + i + "." + ext, buffer, function() { });
}
}
});

View File

@@ -24,42 +24,19 @@ function copy(a) {
}
function formatInput(args,format,images) {
images = [];
for (var image in this.images) {
images.push(image);
}
var json_q = {};
var format_i = format;
if (format == "+")
format = ['o_string_a', 'string_a', 'o_object'];
format = ['string_a', 'o_object'];
else if (format == "-")
format = ['o_string_a', 'number_a'];
format = ['number_a'];
else if (format == "^")
format = ['o_string_a', 'number', 'string', 'o_object'];
format = ['number', 'string', 'o_object'];
else if (format == "r")
format = ['o_string_a', 'o_number'];
format = ['o_number'];
else if (format == "l")
format = ['o_string','string','o_function'];
/*
formats:
addSteps :: o_image_a, name_a, o_o
o_string_a, string_a, o_object => { image: [{name,o}] }
removeSteps :: o_image_a, index_a
o_string_a, number_a => { image: [index] }
insertSteps :: o_image_a, index, name, o_o
o_string_a, number, string, o_object => { image: [{index,name,o}] }
run :: o_image_a, o_from
o_string_a, o_number => { image: index }
loadImages :: image, src, o_function
string, string, o_function => { images: [{image:src}], callback }
optionals:
image: o_string_a
options: o_object
from: o_number
callback: o_function
*/
format = ['string','o_function'];
if(format[format.length-1] == "o_object") {
if(objTypeOf(args[args.length-1]) != "Object")
@@ -74,90 +51,56 @@ function formatInput(args,format,images) {
args.push(function(){});
}
if(format[0] == "o_string_a") {
if(args.length == format.length - 1) {
var insert = false;
for (var i in args) {
if (format[parseInt(i)+1].includes( typeof(getPrimitive(args[i])) )){
insert = true;
}
else {insert = false; break;}
}
if(insert)
args.splice(0,0,copy(images));
}
}
else if (format[0] == "o_string" && format_i == "l" && args.length == 2) {
if (typeof(args[0]) == "string") {
var identifier = "image";
var number = 1;
while (this.images.hasOwnProperty(identifier+number)) number++;
args.splice(0,0,identifier+number);
}
}
if(args.length == format.length) {
if(args.length == format.length) {//making of arrays
for (var i in format) {
if (format[i].substr(format[i].length-2,2)=="_a")
args[i] = makeArray(args[i]);
}
}
if (args.length == 1) {
json_q = copy(args[0]);
if(!(format_i == "r" || format_i == "l")) {
for (var img in json_q)
json_q[img] = makeArray(json_q[img]);
if (args.length == 1 ) {
if(format_i == "r") json_q = {0:copy(args[0])};
else if(format_i == "-") {
json_q=[];
json_q= copy(args[0]);
}
}
else if (format_i == "r") {
for (var img in args[0]) json_q[args[0][img]] = args[1];
else if (format_i == "r" ) {
for (var img in args[0]) json_q = {0:args[0]};
}
else if (format_i == "l") {
json_q = {
images: {},
callback: args[2]
image: args[0],
callback: args[1]
}
json_q.images[args[0]] = args[1];
}
else {
for (var img in args[0]) {
var image = args[0][img];
json_q[image] = [];
json_q = [];
if(format_i == "+") {
for(var s in args[1]) {
json_q[image].push({
name: args[1][s],
o: args[2]
for(var s in args[0]) {
json_q.push({
name: args[0][s],
o: args[1]
});
}
}
if(format_i == "-") {
json_q[image] = args[1];
}
if(format_i == "^") {
var size = this.images[image].steps.length;
var index = args[1];
var size = this.steps.length;
var index = args[0];
index = (index==size)?index:index%size;
if (index<0) index += size+1;
json_q[image].push({
json_q.push({
index: index,
name: args[2],
o: args[3]
name: args[1],
o: args[2]
});
}
}
}
if(format_i == "l") {
json_q.loadedimages = [];
for (var i in json_q.images) json_q.loadedimages.push(i);
}
return json_q;
}

View File

@@ -6,7 +6,7 @@ ImageSequencer = function ImageSequencer(options) {
var sequencer = (this.name == "ImageSequencer") ? this : this.sequencer;
options = options || {};
options.inBrowser = options.inBrowser || isBrowser;
options.inBrowser = options.inBrowser === undefined ? isBrowser : options.inBrowser;
options.sequencerCounter = 0;
function objTypeOf(object) {
@@ -24,7 +24,7 @@ ImageSequencer = function ImageSequencer(options) {
if (!typeof (a) == "object") return a;
if (objTypeOf(a) == "Array") return a.slice();
if (objTypeOf(a) == "Object") {
var b = {};
var b = {};
for (var v in a) {
b[v] = copy(a[v]);
}
@@ -42,7 +42,6 @@ ImageSequencer = function ImageSequencer(options) {
modules = require('./Modules'),
sequences = require('./SavedSequences.json'),
formatInput = require('./FormatInput'),
images = {},
inputlog = [],
events = require('./ui/UserInterface')(),
fs = require('fs');
@@ -66,94 +65,78 @@ ImageSequencer = function ImageSequencer(options) {
function addSteps() {
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
var args = [];
var json_q = {};
for (var arg in arguments) { args.push(copy(arguments[arg])); }
json_q = formatInput.call(this_, args, "+");
inputlog.push({ method: "addSteps", json_q: copy(json_q) });
for (var i in json_q)
for (var j in json_q[i])
require("./AddStep")(this_, i, json_q[i][j].name, json_q[i][j].o);
for (var j in json_q)
require("./AddStep")(this_, json_q[j].name, json_q[j].o);
return this;
}
function removeStep(image, index) {
function removeStep(ref, index) {
//remove the step from images[image].steps and redraw remaining images
if (index > 0) {
thisStep = images[image].steps[index];
//var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
thisStep = ref.steps[index];
thisStep.UI.onRemove(thisStep.options.step);
images[image].steps.splice(index, 1);
ref.steps.splice(index, 1);
}
//tell the UI a step has been removed
}
function removeSteps(image, index) {
var run = {}, indices;
function removeSteps(index) {
var indices;
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
var args = [];
for (var arg in arguments) args.push(copy(arguments[arg]));
var json_q = formatInput.call(this_, args, "-");
inputlog.push({ method: "removeSteps", json_q: copy(json_q) });
for (var img in json_q) {
indices = json_q[img].sort(function(a, b) { return b - a });
run[img] = indices[indices.length - 1];
indices = json_q.sort(function(a, b) { return b - a });
for (var i in indices)
removeStep(img, indices[i]);
}
// this.run(run); // This is creating problems
removeStep(this_, indices[i]);
return this;
}
function insertSteps(image, index, name, o) {
var run = {};
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
var args = []
for (var arg in arguments) args.push(arguments[arg]);
var json_q = formatInput.call(this_, args, "^");
inputlog.push({ method: "insertSteps", json_q: copy(json_q) });
for (var img in json_q) {
var details = json_q[img];
var details = json_q;
details = details.sort(function(a, b) { return b.index - a.index });
for (var i in details)
require("./InsertStep")(this_, img, details[i].index, details[i].name, details[i].o);
run[img] = details[details.length - 1].index;
}
// this.run(run); // This is Creating issues
require("./InsertStep")(this_, details[i].index, details[i].name, details[i].o);
return this;
}
// Config is an object which contains the runtime configuration like progress bar
// information and index from which the sequencer should run
function run(config, t_image, t_from) {
let progressObj, index = 0;
var progressObj, index = 0;
config = config || { mode: 'no-arg' };
if (config.index) index = config.index;
if (config.mode != 'test') {
if (config.mode != "no-arg" && typeof config != 'function') {
if (config.progressObj) progressObj = config.progressObj;
delete arguments['0'];
}
}
else {
arguments['0'] = config.mode;
}
var this_ = (this.name == "ImageSequencer") ? this : this.sequencer;
var args = (this.name == "ImageSequencer") ? [] : [this.images];
var args = [];
for (var arg in arguments) args.push(copy(arguments[arg]));
var callback = function() { };
for (var arg in args)
if (objTypeOf(args[arg]) == "Function")
callback = args.splice(arg, 1)[0];
callback = args.splice(arg, 1)[0]; //callback is formed
var json_q = formatInput.call(this_, args, "r");
@@ -164,13 +147,17 @@ ImageSequencer = function ImageSequencer(options) {
function loadImages() {
var args = [];
var prevSteps = this.getSteps().slice(1).map(step=>step.options.name)
var sequencer = this;
sequencer.image = arguments[0];
for (var arg in arguments) args.push(copy(arguments[arg]));
var json_q = formatInput.call(this, args, "l");
if(this.getSteps().length!=0){
this.options.sequencerCounter = 0;
inputlog = [];
this.steps = [];
}
inputlog.push({ method: "loadImages", json_q: copy(json_q) });
var loadedimages = this.copy(json_q.loadedimages);
var ret = {
name: "ImageSequencer Wrapper",
sequencer: this,
@@ -179,22 +166,19 @@ ImageSequencer = function ImageSequencer(options) {
insertSteps: this.insertSteps,
run: this.run,
UI: this.UI,
setUI: this.setUI,
images: loadedimages
setUI: this.setUI
};
function load(i) {
if (i == loadedimages.length) {
json_q.callback.call(ret);
return;
function loadPrevSteps(ref){
if(prevSteps.length!=0){
ref.addSteps(prevSteps)
prevSteps=[];
}
var img = loadedimages[i];
require('./ui/LoadImage')(sequencer, img, json_q.images[img], function() {
load(++i);
});
}
load(0);
require('./ui/LoadImage')(sequencer, "image", json_q.image, function() {
loadPrevSteps(sequencer);
json_q.callback.call(ret);
});
}
function replaceImage(selector, steps, options) {
@@ -203,6 +187,11 @@ ImageSequencer = function ImageSequencer(options) {
return require('./ReplaceImage')(this, selector, steps, options);
}
//returns the steps added
function getSteps(){
return this.steps;
}
function setUI(UI) {
this.events = require('./ui/UserInterface')(UI);
}
@@ -223,8 +212,9 @@ ImageSequencer = function ImageSequencer(options) {
}
}
else {
if (modules[name])
modulesdata = modules[name][1];
if (modules[name]){
modulesdata = modules[name][1];
}
else
modulesdata = { 'inputs': sequences[name]['options'] };
}
@@ -235,9 +225,11 @@ ImageSequencer = function ImageSequencer(options) {
function toCliString() {
var cliStringSteps = `"`, cliOptions = {};
for (var step in this.steps) {
if (this.steps[step].options.name !== "load-image")
cliStringSteps += `${this.steps[step].options.name} `;
for (var inp in modulesInfo(this.steps[step].options.name).inputs) {
var name = (typeof this.steps[step].options !== "undefined")? this.steps[step].options.name : this.steps[step].name
if (name !== "load-image"){
cliStringSteps += `${name} `;
}
for (var inp in modulesInfo(name).inputs) {
cliOptions[inp] = this.steps[step].options[inp];
}
}
@@ -250,13 +242,14 @@ ImageSequencer = function ImageSequencer(options) {
if (step) {
return stepToString(step);
} else {
return copy(this.images.image1.steps).map(stepToString).slice(1).join(',');
return copy(this.steps.map(stepToString).slice(1).join(','));
}
}
// Stringifies one step of the sequence
function stepToString(step) {
let inputs = modulesInfo(step.options.name).inputs || {}, op = {};
var arg = (step.name)?step.name:step.options.name;
let inputs = modulesInfo(arg).inputs || {}, op = {};
for (let input in inputs) {
@@ -268,7 +261,7 @@ ImageSequencer = function ImageSequencer(options) {
}
var configurations = Object.keys(op).map(key => key + ':' + op[key]).join('|');
return `${step.options.name}{${configurations}}`;
return `${arg}{${configurations}}`;
}
// exports the current sequence as an array of JSON steps
@@ -382,19 +375,6 @@ ImageSequencer = function ImageSequencer(options) {
fs.writeFileSync('./src/Modules.js', mods);
}
function createMetaModule(stepsCollection, info) {
var stepsArr = stepsCollection;
if (typeof stepsCollection === 'string')
stepsArr = stringToJSON(stepsCollection);
var metaMod = function() {
this.expandSteps(stepsArr);
return {
isMeta: true
}
}
return [metaMod, info];
}
function saveSequence(name, sequenceString) {
const sequence = stringToJSON(sequenceString);
// Save the given sequence string as a module
@@ -428,8 +408,9 @@ ImageSequencer = function ImageSequencer(options) {
inputlog: inputlog,
modules: modules,
sequences: sequences,
images: images,
events: events,
steps: steps,
image: image,
//user functions
loadImages: loadImages,
@@ -452,9 +433,10 @@ ImageSequencer = function ImageSequencer(options) {
importJSON: importJSON,
loadNewModule: loadNewModule,
saveNewModule: saveNewModule,
createMetaModule: createMetaModule,
createMetaModule: require('./util/createMetaModule'),
saveSequence: saveSequence,
loadModules: loadModules,
getSteps:getSteps,
//other functions
log: log,

View File

@@ -1,33 +1,33 @@
const getStepUtils = require('./util/getStep.js');
// insert one or more steps at a given index in the sequencer
function InsertStep(ref, image, index, name, o) {
function InsertStep(ref, index, name, o) {
if (ref.sequences[name]) {
return ref.importJSON(ref.sequences[name]);
}
function insertStep(image, index, name, o_) {
function insertStep(index, name, o_) {
if (ref.modules[name]) var moduleInfo = ref.modules[name][1];
else {
console.log('Module ' + name + ' not found.');
}
var o = ref.copy(o_);
o.number = ref.options.sequencerCounter++; //Gives a Unique ID to each step
o.name = o_.name || name || moduleInfo.name;
o.description = o_.description || moduleInfo.description;
o.selector = o_.selector || 'ismod-' + name;
o.container = o_.container || ref.options.selector;
o.image = image;
o.inBrowser = ref.options.inBrowser;
if (index == -1) index = ref.images[image].steps.length;
if (index == -1) index = ref.steps.length;
o.step = {
name: o.name,
description: o.description,
ID: o.number,
imageName: o.image,
inBrowser: ref.options.inBrowser,
ui: ref.options.ui,
options: o
@@ -38,10 +38,7 @@ function InsertStep(ref, image, index, name, o) {
ref.modules[name].expandSteps = function expandSteps(stepsArray) {
for (var i in stepsArray) {
let step = stepsArray[i];
console.log(step['name'])
console.log(step['options'])
ref.insertSteps(index + Number.parseInt(i), step['name'], step['options']);
// ref.addSteps(step['name'], step['options']);
}
}
@@ -50,7 +47,7 @@ function InsertStep(ref, image, index, name, o) {
if (!ref.modules[name][1].length) {
UI.onSetup(o.step, { index: index });
ref.images[image].steps.splice(index, 0, ref.modules[name][0](o, UI));
ref.steps.splice(index, 0, ref.modules[name][0](o, UI));
} else {
ref.modules[name][0](o, UI);
}
@@ -58,8 +55,8 @@ function InsertStep(ref, image, index, name, o) {
return true;
}
insertStep(image, index, name, o);
ref.steps = ref.images[image].steps;
insertStep(index, name, o);
ref.steps = ref.steps;
}
module.exports = InsertStep;

View File

@@ -2,6 +2,7 @@
* Core modules and their info files
*/
module.exports = {
'add-qr': require('./modules/AddQR'),
'average': require('./modules/Average'),
'blend': require('./modules/Blend'),
'blur': require('./modules/Blur'),
@@ -13,8 +14,12 @@ module.exports = {
'convolution': require('./modules/Convolution'),
'crop': require('./modules/Crop'),
'decode-qr': require('./modules/DecodeQr'),
'dither': require('./modules/Dither'),
'draw-rectangle': require('./modules/DrawRectangle'),
'dynamic': require('./modules/Dynamic'),
'edge-detect': require('./modules/EdgeDetect'),
'exposure': require('./modules/Exposure'),
'flip-image': require('./modules/FlipImage'),
'fisheye-gl': require('./modules/FisheyeGl'),
'histogram': require('./modules/Histogram'),
'gamma-correction': require('./modules/GammaCorrection'),
@@ -23,8 +28,13 @@ module.exports = {
'invert': require('image-sequencer-invert'),
'ndvi': require('./modules/Ndvi'),
'ndvi-colormap': require('./modules/NdviColormap'),
'paint-bucket': require('./modules/PaintBucket'),
'overlay': require('./modules/Overlay'),
'replace-color':require('./modules/ReplaceColor'),
'resize': require('./modules/Resize'),
'rotate': require('./modules/Rotate'),
'saturation': require('./modules/Saturation')
}
'saturation': require('./modules/Saturation'),
'threshold': require('./modules/Threshold'),
'tint': require('./modules/Tint'),
'white-balance': require('./modules/WhiteBalance')
}

View File

@@ -5,9 +5,8 @@ function Run(ref, json_q, callback, ind, progressObj) {
function drawStep(drawarray, pos) {
if (pos == drawarray.length && drawarray[pos - 1] !== undefined) {
var image = drawarray[pos - 1].image;
if (ref.objTypeOf(callback) == "Function" && ref.images[image].steps.slice(-1)[0].output) {
var steps = ref.images[image].steps;
if (ref.objTypeOf(callback) == "Function" && ref.steps.slice(-1)[0].output) {
var steps = ref.steps;
var out = steps[steps.length - 1].output.src;
callback(out);
return true;
@@ -16,39 +15,39 @@ function Run(ref, json_q, callback, ind, progressObj) {
// so we don't run on the loadImage module:
if (drawarray[pos] !== undefined) {
var image = drawarray[pos].image;
var i = drawarray[pos].i;
var input = ref.images[image].steps[i - 1].output;
var input = ref.steps[i - 1].output;
var step = ref.steps[i];
ref.images[image].steps[i].getStep = function getStep(offset) {
if (i + offset >= ref.images[image].steps.length) return { options: { name: undefined } };
else return ref.images[image].steps.slice(i + offset)[0];
step.getStep = function getStep(offset) {
if (i + offset >= ref.steps.length) return { options: { name: undefined } };
else return ref.steps.slice(i + offset)[0];
};
ref.images[image].steps[i].getIndex = function getIndex() {
step.getIndex = function getIndex() {
return i;
}
for (var util in getStepUtils) {
if (getStepUtils.hasOwnProperty(util)) {
ref.images[image].steps[i][util] = getStepUtils[util];
step[util] = getStepUtils[util];
}
}
// Tell UI that a step is being drawn.
ref.images[image].steps[i].UI.onDraw(ref.images[image].steps[i].options.step);
step.UI.onDraw(step.options.step);
// provides a set of standard tools for each step
var inputForNextStep = require('./RunToolkit')(ref.copy(input));
ref.images[image].steps[i].draw(
step.draw(
inputForNextStep,
function onEachStep() {
// This output is accessible by UI
ref.images[image].steps[i].options.step.output = ref.images[image].steps[i].output.src;
// This output is accessible by UI
ref.steps[i].options.step.output = ref.steps[i].output.src;
// Tell UI that step has been drawn.
ref.images[image].steps[i].UI.onComplete(ref.images[image].steps[i].options.step);
ref.steps[i].UI.onComplete(ref.steps[i].options.step);
drawStep(drawarray, ++pos);
},
@@ -58,36 +57,33 @@ function Run(ref, json_q, callback, ind, progressObj) {
}
function drawSteps(json_q) {
var drawarray = [];
for (var image in json_q) {
var no_steps = ref.images[image].steps.length;
var init = json_q[image];
var drawarray = [],
no_steps = ref.steps.length,
init = json_q[0];
for (var i = 0; i < no_steps - init; i++) {
drawarray.push({ image: image, i: init + i });
drawarray.push({i: init + i });
}
}
drawStep(drawarray, ind);
}
function filter(json_q) {
for (var image in json_q) {
if (json_q[image] == 0 && ref.images[image].steps.length == 1)
delete json_q[image];
else if (json_q[image] == 0) json_q[image]++;
}
for (var image in json_q) {
var prevstep = ref.images[image].steps[json_q[image] - 1];
if (json_q[0] == 0 && ref.steps.length == 1)
delete json_q[0];
else if (json_q[0] == 0) json_q[0]++;
var prevstep = ref.steps[json_q[0] - 1];
while (
typeof prevstep == "undefined" ||
typeof prevstep.output == "undefined"
) {
prevstep = ref.images[image].steps[--json_q[image] - 1];
) {
prevstep = ref.steps[--json_q[0] - 1];
}
}
return json_q;
}
var json_q = filter(json_q);
return drawSteps(json_q);
}
module.exports = Run;

17
src/cli/installModule.js Normal file
View File

@@ -0,0 +1,17 @@
var childProcess = require('child_process')
var Spinner = require('ora');
module.exports = function (program, sequencer) {
console.log(
"\x1b[33m%s\x1b[0m",
"Please wait while your Module is being Installed...\nThis may take a while!"
);
var params = program.installModule.split(' ');
var spinner = Spinner("Now Installing...").start();
childProcess.execSync(`npm i ${params[1]}`)
sequencer.saveNewModule(params[0], params[1]);
sequencer.loadNewModule(params[0], require(params[1]));
spinner.stop();
console.log("\x1b[32m%s\x1b[0m", "Your module was installed successfully!!");
}

7
src/cli/saveSequence.js Normal file
View File

@@ -0,0 +1,7 @@
module.exports = function (program, sequencer) {
var params = program.saveSequence.split(' ');
sequencer.saveSequence(params[0], params[1]);
console.log("\x1b[32m", "Your sequence was saved successfully!!");
}

88
src/cli/sequencerSteps.js Normal file
View File

@@ -0,0 +1,88 @@
var Spinner = require('ora');
var readlineSync = require('readline-sync');
var utils = require('../CliUtils')
module.exports = function (program, sequencer, outputFilename) {
utils.makedir(program.output, () => {
console.log('Files will be exported to "' + program.output + '"');
if (program.basic)
console.log("Basic mode is enabled, outputting only final image");
// Iterate through the steps and retrieve their inputs.
program.step.forEach(function(step) {
var options = Object.assign({}, sequencer.modulesInfo(step).inputs);
// If inputs exists, print to console.
if (Object.keys(options).length) {
console.log("[" + step + "]: Inputs");
}
// If inputs exists, print them out with descriptions.
Object.keys(options).forEach(function(input) {
// The array below creates a variable number of spaces. This is done with (length + 1).
// The extra 4 that makes it (length + 5) is to account for the []: characters
console.log(
new Array(step.length + 5).join(" ") +
input +
": " +
options[input].desc
);
});
if (program.config) {
try {
var config = JSON.parse(program.config);
console.log(`The parsed options object: `, config);
} catch (e) {
console.error(
"\x1b[31m%s\x1b[0m",
`Options(Config) is not a not valid JSON Fallback activate`
);
program.config = false;
console.log(e);
}
}
if (program.config && utils.validateConfig(config, options)) {
console.log("Now using Options object");
Object.keys(options).forEach(function(input) {
options[input] = config[input];
});
} else {
// If inputs exist, iterate through them and prompt for values.
Object.keys(options).forEach(function(input) {
var value = readlineSync.question(
"[" +
step +
"]: Enter a value for " +
input +
" (" +
options[input].type +
", default: " +
options[input].default +
"): "
);
options[input] = value;
});
}
// Add the step and its inputs to the sequencer.
sequencer.addSteps(step, options);
});
var spinnerObj = { succeed: () => { }, stop: () => { } };
if (!process.env.TEST)
spinnerObj = Spinner("Your Image is being processed..").start();
// Run the sequencer.
sequencer.run({ progressObj: spinnerObj }, function() {
// Export all images or final image as binary files.
sequencer.exportBin(program.output, program.basic, outputFilename);
//check if spinner was not overriden stop it
if (!spinnerObj.overrideFlag) {
spinnerObj.succeed();
console.log(`\nDone!!`);
}
});
});
}

View File

@@ -0,0 +1,51 @@
module.exports = function AddQR(options, UI) {
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.size = options.size || defaults.size;
options.qrCodeString = options.qrCodeString || "https://github.com/publiclab/image-sequencer";
var output;
getPixels = require('get-pixels');
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
return getPixels(input.src, function (err, oldPixels) {
function changePixel(r, g, b, a) {
return [r, g, b, a];
}
function extraManipulation(pixels,generateOutput) {
if (err) {
console.log(err);
return;
}
require('./QR')(options, pixels, oldPixels, generateOutput);
}
function output(image, datauri, mimetype) {
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
})
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

48
src/modules/AddQR/QR.js Normal file
View File

@@ -0,0 +1,48 @@
module.exports = exports = function (options, pixels, oldPixels, callback) {
var QRCode = require('qrcode')
QRCode.toDataURL(options.qrCodeString, function (err, url) {
var getPixels = require("get-pixels");
getPixels(url, function (err, qrPixels) {
if (err) {
console.log("Bad image path", image);
}
var imagejs = require('imagejs');
var bitmap = new imagejs.Bitmap({ width: qrPixels.shape[0], height: qrPixels.shape[1] });
bitmap._data.data = qrPixels.data;
var resized = bitmap.resize({
width: options.size, height: options.size,
algorithm: "bicubicInterpolation"
});
qrPixels.data = resized._data.data;
qrPixels.shape = [options.size, options.size, 4];
qrPixels.stride[1] = 4 * options.size;
var width = oldPixels.shape[0],
height = oldPixels.shape[1];
var xe = width - options.size,
ye = height - options.size;
for (var m = 0; m < width; m++) {
for (var n = 0; n < height; n++) {
if (m >= xe && n >= ye) {
pixels.set(m, n, 0, qrPixels.get(m - xe, n - ye, 0))
pixels.set(m, n, 1, qrPixels.get(m - xe, n - ye, 1))
pixels.set(m, n, 2, qrPixels.get(m - xe, n - ye, 2))
pixels.set(m, n, 3, qrPixels.get(m - xe, n - ye, 3))
}
else {
pixels.set(m, n, 0, oldPixels.get(m, n, 0))
pixels.set(m, n, 1, oldPixels.get(m, n, 1))
pixels.set(m, n, 2, oldPixels.get(m, n, 2))
pixels.set(m, n, 3, oldPixels.get(m, n, 3))
}
}
}
callback();
})
})
}

View File

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

View File

@@ -0,0 +1,19 @@
{
"name": "Add QR",
"description": "Adds QR corresponding to the given string",
"url": "https://github.com/publiclab/image-sequencer/tree/master/MODULES.md",
"inputs": {
"size": {
"type": "integer",
"desc": "size of QR code in pixels",
"default": 200
},
"qrCodeString":{
"type": "string",
"desc": "input string to generate QR code",
"default": "https://github.com/publiclab/image-sequencer"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#add-qr-module"
}

View File

@@ -1,65 +1,63 @@
/*
* Average all pixel colors
*/
module.exports = function Average(options, UI){
module.exports = function Average(options, UI) {
options.blur = options.blur || 2
var output;
options.step.metadata = options.step.metadata || {};
function draw(input,callback,progressObj){
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
function changePixel(r, g, b, a){
return [r,g,b,a]
}
// do the averaging
function extraManipulation(pixels){
var sum = [0,0,0,0];
for (var i = 0; i < pixels.data.length; i += 4) {
sum[0] += pixels.data[i + 0];
sum[1] += pixels.data[i + 1];
sum[2] += pixels.data[i + 2];
sum[3] += pixels.data[i + 3];
function extraManipulation(pixels) {
var i = 0, sum = [0, 0, 0, 0];
while (i < pixels.data.length) {
sum[0] += pixels.data[i++];
sum[1] += pixels.data[i++];
sum[2] += pixels.data[i++];
sum[3] += pixels.data[i++];
}
sum[0] = parseInt(sum[0] / (pixels.data.length / 4));
sum[1] = parseInt(sum[1] / (pixels.data.length / 4));
sum[2] = parseInt(sum[2] / (pixels.data.length / 4));
sum[3] = parseInt(sum[3] / (pixels.data.length / 4));
let divisor = pixels.data.length / 4;
for (var i = 0; i < pixels.data.length; i += 4) {
pixels.data[i + 0] = sum[0];
pixels.data[i + 1] = sum[1];
pixels.data[i + 2] = sum[2];
pixels.data[i + 3] = sum[3];
sum[0] = Math.floor(sum[0] / divisor);
sum[1] = Math.floor(sum[1] / divisor);
sum[2] = Math.floor(sum[2] / divisor);
sum[3] = Math.floor(sum[3] / divisor);
i = 0
while (i < pixels.data.length) {
pixels.data[i++] = sum[0];
pixels.data[i++] = sum[1];
pixels.data[i++] = sum[2];
pixels.data[i++] = sum[3];
}
// report back and store average in metadata:
options.step.metadata.averages = sum;
console.log("average: ", sum);
// TODO: refactor into a new "display()" method as per https://github.com/publiclab/image-sequencer/issues/242
if (options.step.inBrowser && options.step.ui) $(options.step.ui).find('.details').append("<p><b>Averages</b> (r, g, b, a): " + sum.join(', ') + "</p>");
return pixels;
}
function output(image, datauri, mimetype){
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = {
src: datauri,
format: mimetype
src: datauri,
format: mimetype
};
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
@@ -69,7 +67,7 @@ module.exports = function Average(options, UI){
}
return {
options: options,
draw: draw,
draw: draw,
output: output,
UI: UI
}

View File

@@ -3,5 +3,5 @@
"description": "Average all pixel color",
"inputs": {
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#average-module"
}

View File

@@ -1,7 +1,9 @@
module.exports = function Dynamic(options, UI, util) {
options.func = options.func || "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }";
options.offset = options.offset || -2;
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.func = options.func || defaults.blend;
options.offset = options.offset || defaults.offset;
var output;
@@ -19,17 +21,23 @@ module.exports = function Dynamic(options, UI, util) {
var getPixels = require('get-pixels');
// convert offset as string to int
if(typeof options.offset === "string") options.offset = parseInt(options.offset);
if (typeof options.offset === "string") options.offset = parseInt(options.offset);
// save first image's pixels
var priorStep = this.getStep(options.offset);
if (priorStep.output === undefined) {
this.output = input;
UI.notify('Offset Unavailable', 'offset-notification');
callback();
}
getPixels(priorStep.output.src, function(err, pixels) {
options.firstImagePixels = pixels;
function changePixel(r2, g2, b2, a2, x, y) {
// blend!
var p = options.firstImagePixels;
let p = options.firstImagePixels;
return options.func(
r2, g2, b2, a2,
p.get(x, y, 0),

View File

@@ -8,10 +8,10 @@
"default": -2
},
"blend": {
"type": "input",
"type": "string",
"desc": "Function to use to blend the two images.",
"default": "function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] }"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blend-module"
}

View File

@@ -1,5 +1,5 @@
module.exports = exports = function(pixels, blur) {
let kernel = kernelGenerator(blur, 1), oldpix = pixels;
let kernel = kernelGenerator(blur, 1), oldpix = require('lodash').cloneDeep(pixels);
kernel = flipKernel(kernel);
for (let i = 0; i < pixels.shape[0]; i++) {

View File

@@ -3,7 +3,8 @@
*/
module.exports = function Blur(options, UI) {
options.blur = options.blur || 2
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.blur = options.blur || defaults.blur;
var output;
function draw(input, callback, progressObj) {
@@ -13,13 +14,9 @@ module.exports = function Blur(options, UI) {
var step = this;
function changePixel(r, g, b, a) {
return [r, g, b, a]
}
function extraManipulation(pixels) {
pixels = require('./Blur')(pixels, options.blur)
return pixels
pixels = require('./Blur')(pixels, options.blur);
return pixels;
}
function output(image, datauri, mimetype) {
@@ -31,7 +28,6 @@ module.exports = function Blur(options, UI) {
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,

View File

@@ -3,13 +3,13 @@
"description": "Applies a Gaussian blur given by the intensity value",
"inputs": {
"blur": {
"type": "range",
"type": "float",
"desc": "Amount of gaussian blur(Less blur gives more detail, typically 0-5)",
"default": "2",
"min": "0",
"max": "5",
"step": "0.25"
"default": 2,
"min": 0,
"max": 5,
"step": 0.25
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blur-module"
}

View File

@@ -1,12 +1,16 @@
/*
* Changes the Image Brightness
*/
module.exports = function Brightness(options, UI) {
module.exports = function Brightness(options,UI){
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
var output;
function draw(input,callback,progressObj){
function draw(input, callback, progressObj) {
options.brightness = options.brightness || defaults.brightness;
progressObj.stop(true);
progressObj.overrideFlag = true;
@@ -17,23 +21,20 @@ module.exports = function Brightness(options,UI){
For eg. progressObj = new SomeProgressModule()
*/
var step = this;
var step = this, val = (options.brightness) / 100.0;;
function changePixel(r, g, b, a){
options.brightness =
options.brightness || 100
var val = (options.brightness)/100.0
function changePixel(r, g, b, a) {
r = val*r<255?val*r:255
g = val*g<255?val*g:255
b = val*b<255?val*b:255
return [r, g, b, a]
r = Math.min(val * r, 255);
g = Math.min(val * g, 255);
b = Math.min(val * b, 255);
return [r, g, b, a];
}
function output(image,datauri,mimetype){
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
step.output = { src: datauri, format: mimetype };
}
@@ -49,7 +50,7 @@ module.exports = function Brightness(options,UI){
}
return {
options: options,
draw: draw,
draw: draw,
output: output,
UI: UI
}

View File

@@ -3,13 +3,12 @@
"description": "Change the brightness of the image by given percent value",
"inputs": {
"brightness": {
"type": "range",
"type": "integer",
"desc": "% brightness for the new image",
"default": "100",
"default": "175",
"min": "0",
"max": "200",
"step": "1"
"max": "200"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#brightness-module"
}

View File

@@ -3,7 +3,9 @@
*/
module.exports = function Channel(options, UI) {
options.channel = options.channel || "green";
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.channel = options.channel || defaults.channel;
var output;
@@ -15,9 +17,9 @@ module.exports = function Channel(options, UI) {
var step = this;
function changePixel(r, g, b, a) {
if (options.channel == "red") return [r, 0, 0, a];
if (options.channel == "green") return [0, g, 0, a];
if (options.channel == "blue") return [0, 0, b, a];
if (options.channel === "red") return [r, 0, 0, a];
if (options.channel === "green") return [0, g, 0, a];
if (options.channel === "blue") return [0, 0, b, a];
}
function output(image, datauri, mimetype) {

View File

@@ -9,5 +9,5 @@
"values": ["red", "green", "blue"]
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#channel-module"
}

View File

@@ -1,16 +1,14 @@
module.exports = function NdviColormapfunction(options, UI) {
module.exports = require('../../util/createMetaModule.js')(
function mapFunction(options) {
options.x = options.x || 0;
options.y = options.y || 0;
options.colormap = options.colormap || "default";
options.h = options.h || 10;
this.expandSteps([
{ 'name': 'gradient', 'options': {} },
{ 'name': 'colormap', 'options': { colormap: options.colormap } },
{ 'name': 'crop', 'options': { 'y': 0, 'h': options.h } },
{ 'name': 'overlay', 'options': { 'x': options.x, 'y': options.y, 'offset': -4 } }
]);
return {
isMeta: true
}
}
// return steps with options:
return [
{ 'name': 'gradient', 'options': {} },
{ 'name': 'colormap', 'options': { colormap: options.colormap } },
{ 'name': 'crop', 'options': { 'y': 0, 'h': options.h } },
{ 'name': 'overlay', 'options': { 'x': options.x, 'y': options.y, 'offset': -4 } }
];
}, {
infoJson: require('./info.json')
}
)[0];

View File

@@ -24,11 +24,10 @@
"default": 0
},
"h": {
"type": "iinteger",
"type": "integer",
"desc": "height of the colorbar",
"default": 10
}
},
"length": 4,
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
}
"docs-link": "https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#colorbar-module"
}

View File

@@ -53,7 +53,7 @@ function colormap(segments) {
var colormaps = {
greyscale: colormap([
[0, [0, 0, 0], [220, 20, 60] ],
[0, [0, 0, 0], [255, 255, 255] ],
[1, [255, 255, 255], [255, 255, 255] ]
]),
@@ -177,7 +177,7 @@ var colormaps = {
fastie: colormap([
[0, [255, 255, 255], [0, 0, 0] ],
[0.167, [0, 0, 0], [255, 255, 255] ],
[0.33, [2, 0, 226], [2, 0, 226] ],
[0.33, [255, 255, 255], [0, 0, 0] ],
[0.5, [0, 0, 0], [140, 140, 255] ],
[0.55, [140, 140, 255], [0, 255, 0] ],
[0.63, [0, 255, 0], [255, 255, 0] ],

View File

@@ -1,9 +1,9 @@
module.exports = function Colormap(options,UI) {
module.exports = function Colormap(options, UI) {
var output;
// This function is called on every draw.
function draw(input,callback,progressObj) {
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
@@ -16,7 +16,7 @@ module.exports = function Colormap(options,UI) {
return [res[0], res[1], res[2], 255];
}
function output(image,datauri,mimetype){
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };

View File

@@ -6,8 +6,8 @@
"type": "select",
"desc": "Name of the Colormap",
"default": "default",
"values": ["default","greyscale","stretched","fastie","brntogrn","blutoredjet","colors16"]
"values": ["default","greyscale","bluwhtgrngis","stretched","fastie","brntogrn","blutoredjet","colors16"]
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#colormap-module"
}

View File

@@ -1,41 +1,41 @@
var _ = require('lodash');
module.exports = exports = function(pixels , contrast){
let oldpix = _.cloneDeep(pixels);
module.exports = exports = function(pixels, contrast) {
let oldpix = _.cloneDeep(pixels);
contrast = Number(contrast)
if (contrast < -100) contrast = -100;
if (contrast < -100) contrast = -100;
if (contrast > 100) contrast = 100;
contrast = (100.0 + contrast) / 100.0;
contrast *= contrast;
for (let i = 0; i < oldpix.shape[0]; i++) {
for (let j = 0; j < oldpix.shape[1]; j++) {
var r = oldpix.get(i,j,0)/255.0;
r -= 0.5;
for (let i = 0; i < pixels.shape[0]; i++) {
for (let j = 0; j < pixels.shape[1]; j++) {
var r = oldpix.get(i, j, 0) / 255.0;
r -= 0.5;
r *= contrast;
r += 0.5;
r += 0.5;
r *= 255;
if (r < 0) r = 0;
if (r > 255) r = 255;
var g = oldpix.get(i,j,1)/255.0;
var g = oldpix.get(i, j, 1) / 255.0;
g -= 0.5;
g *= contrast;
g += 0.5;
g *= 255;
if (g < 0) g = 0;
if (g > 255) g = 255;
var b = oldpix.get(i,j,2)/255.0;
var b = oldpix.get(i, j, 2) / 255.0;
b -= 0.5;
b *= contrast;
b += 0.5;
b *= 255;
if (b < 0) b = 0;
if (b > 255) b = 255;
pixels.set(i, j, 0, r);
pixels.set(i, j, 1, g);

View File

@@ -4,7 +4,8 @@
module.exports = function Contrast(options, UI) {
options.contrast = options.contrast || 70
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.contrast = options.contrast || defaults.contrast;
var output;
function draw(input, callback, progressObj) {
@@ -14,10 +15,6 @@ module.exports = function Contrast(options, UI) {
var step = this;
function changePixel(r, g, b, a) {
return [r, g, b, a]
}
function extraManipulation(pixels) {
pixels = require('./Contrast')(pixels, options.contrast)
return pixels
@@ -32,7 +29,6 @@ module.exports = function Contrast(options, UI) {
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,

View File

@@ -3,10 +3,12 @@
"description": "Change the contrast of the image by given value",
"inputs": {
"contrast": {
"type": "Number",
"type": "integer",
"desc": "contrast for the new image, typically -100 to 100",
"default": 70
"default": 70,
"min": -100,
"max": 100
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#contrast-module"
}

View File

@@ -1,9 +1,9 @@
var _ = require('lodash');
module.exports = exports = function(pixels, constantFactor, kernelValues){
let kernel = kernelGenerator(constantFactor, kernelValues), oldpix = _.cloneDeep(pixels);
kernel = flipKernel(kernel);
module.exports = exports = function(pixels, constantFactor, kernelValues) {
let kernel = kernelGenerator(constantFactor, kernelValues), oldpix = _.cloneDeep(pixels);
kernel = flipKernel(kernel);
for (let i = 0; i < pixels.shape[0]; i++) {
for (let i = 0; i < pixels.shape[0]; i++) {
for (let j = 0; j < pixels.shape[1]; j++) {
let neighboutPos = getNeighbouringPixelPositions([i, j]);
let acc = [0.0, 0.0, 0.0, 0.0];
@@ -15,9 +15,9 @@ module.exports = exports = function(pixels, constantFactor, kernelValues){
acc[3] += (oldpix.get(neighboutPos[a][b][0], neighboutPos[a][b][1], 3) * kernel[a][b]);
}
}
acc[0] = acc[0]%255;
acc[1] = acc[1]%255;
acc[2] = acc[2]%255;
acc[0] = Math.min(acc[0], 255);
acc[1] = Math.min(acc[1], 255);
acc[2] = Math.min(acc[2], 255);
pixels.set(i, j, 0, acc[0]);
pixels.set(i, j, 1, acc[1]);
pixels.set(i, j, 2, acc[2]);
@@ -26,25 +26,25 @@ module.exports = exports = function(pixels, constantFactor, kernelValues){
return pixels;
function kernelGenerator(constantFactor, kernelValues){
kernelValues = kernelValues.split(" ");
for(i = 0 ; i < 9; i++){
function kernelGenerator(constantFactor, kernelValues) {
kernelValues = kernelValues.split(" ");
for (i = 0; i < 9; i++) {
kernelValues[i] = Number(kernelValues[i]) * constantFactor;
}
let k = 0;
let arr = [];
for(i = 0; i < 3; i++){
let columns = [];
for(j = 0; j < 3; j++){
columns.push(kernelValues[k]);
k += 1;
}
arr.push(columns);
}
return arr;
}
let arr = [];
for (i = 0; i < 3; i++) {
let columns = [];
for (j = 0; j < 3; j++) {
columns.push(kernelValues[k]);
k += 1;
}
arr.push(columns);
}
return arr;
}
function getNeighbouringPixelPositions(pixelPosition) {
function getNeighbouringPixelPositions(pixelPosition) {
let x = pixelPosition[0], y = pixelPosition[1], result = [];
for (let i = -1; i <= 1; i++) {
@@ -57,7 +57,7 @@ module.exports = exports = function(pixels, constantFactor, kernelValues){
return result;
}
function flipKernel(kernel) {
function flipKernel(kernel) {
let result = [];
for (let i = kernel.length - 1; i >= 0; i--) {
let arr = [];

View File

@@ -1,7 +1,9 @@
module.exports = function Convolution(options, UI) {
options.kernelValues = options.kernelValues || '1 1 1 1 1 1 1 1 1';
options.constantFactor = options.constantFactor || 1/9;
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.kernelValues = options.kernelValues || defaults.kernelValues;
options.constantFactor = options.constantFactor || defaults.constantFactor;
var output;
function draw(input, callback, progressObj) {
@@ -11,13 +13,9 @@ module.exports = function Convolution(options, UI) {
var step = this;
function changePixel(r, g, b, a) {
return [r, g, b, a]
}
function extraManipulation(pixels) {
pixels = require('./Convolution')(pixels, options.constantFactor, options.kernelValues)
return pixels
pixels = require('./Convolution')(pixels, options.constantFactor, options.kernelValues);
return pixels;
}
function output(image, datauri, mimetype) {
@@ -28,7 +26,6 @@ module.exports = function Convolution(options, UI) {
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,

View File

@@ -3,18 +3,18 @@
"description": "Image Convolution using a given 3x3 kernel matrix <a href='https://en.wikipedia.org/wiki/Kernel_(image_processing)'>Read more</a>",
"inputs": {
"constantFactor":{
"type": "Float",
"type": "float",
"desc": "a constant factor, multiplies all the kernel values by that factor",
"default": 0.1111,
"placeholder": 0.1111
},
"kernelValues": {
"type": "String",
"type": "string",
"desc": "nine space separated numbers representing the kernel values in left to right and top to bottom format.",
"default": "1 1 1 1 1 1 1 1 1",
"placeholder": "1 1 1 1 1 1 1 1 1"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#convolution-module"
}

View File

@@ -1,22 +1,33 @@
module.exports = function Crop(input,options,callback) {
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
var getPixels = require('get-pixels'),
savePixels = require('save-pixels');
options.x = parseInt(options.x) || 0;
options.y = parseInt(options.y) || 0;
options.x = parseInt(options.x) || defaults.x;
options.y = parseInt(options.y) || defaults.y;
getPixels(input.src,function(err,pixels){
options.w = parseInt(options.w) || Math.floor(pixels.shape[0]);
options.h = parseInt(options.h) || Math.floor(pixels.shape[1]);
options.backgroundColor = options.backgroundColor || defaults.backgroundColor;
var ox = options.x;
var oy = options.y;
var w = options.w;
var h = options.h;
var iw = pixels.shape[0]; //Width of Original Image
var ih = pixels.shape[1]; //Height of Original Image
var backgroundArray = [];
backgroundColor = options.backgroundColor.split(" ");
for(var i = 0; i < w ; i++){
backgroundArray = backgroundArray.concat([backgroundColor[0],backgroundColor[1],backgroundColor[2],backgroundColor[3]]);
}
var newarray = new Uint8Array(4*w*h);
for (var n = oy; n < oy + h; n++) {
if(n<ih){
newarray.set(pixels.data.slice(n*4*iw + ox, n*4*iw + ox + 4*w),4*w*(n-oy));
} else {
newarray.set(backgroundArray,4*w*(n-oy));
}
}
pixels.data = newarray;
pixels.shape = [w,h,4];

View File

@@ -19,7 +19,7 @@ module.exports = function CropModule(options, UI) {
// add our custom in-module html ui:
if (options.step.inBrowser && !options.noUI) var ui = require('./Ui.js')(options.step, UI);
var output,
setupComplete = false;
setupComplete = false;
// This function is caled everytime the step has to be redrawn
function draw(input,callback) {
@@ -29,8 +29,23 @@ module.exports = function CropModule(options, UI) {
// save the input image;
// TODO: this should be moved to module API to persist the input image
options.step.input = input.src;
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
require('./Crop')(input, options, function(out, format){
//parse the inputs
parseCornerCoordinateInputs(options,{
src: input.src,
x: { valInp: options.x, type: 'horizontal' },
y: { valInp: options.y, type: 'vertical' },
w: { valInp: options.w, type: 'horizontal' },
h: { valInp: options.h, type: 'vertical' },
}, function (options, coord) {
options.x = parseInt(coord.x.valInp);
options.y = parseInt(coord.y.valInp);
options.w = coord.w.valInp;
options.h = coord.h.valInp;
});
require('./Crop')(input, options, function (out, format) {
// This output is accessible to Image Sequencer
step.output = {

View File

@@ -1,6 +1,6 @@
{
"name": "Crop",
"description": "Crop image to given x, y, w, h in pixels, measured from top left",
"description": "Crop image to given x, y, w, h in pixels or % , measured from top left",
"url": "https://github.com/publiclab/image-sequencer/tree/master/MODULES.md",
"inputs": {
"x": {
@@ -16,13 +16,19 @@
"w": {
"type": "integer",
"desc": "Width of crop",
"default": "(100%)"
"default": "(50%)"
},
"h": {
"type": "integer",
"desc": "Height of crop",
"default": "(100%)"
"default": "(50%)"
},
"backgroundColor": {
"type": "string",
"desc": "Background Color (Four space separated RGBA values)",
"default": "255 255 255 255",
"placeholder": "255 255 255 255"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
}
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#crop-module"
}

View File

@@ -8,7 +8,10 @@ module.exports = function DoNothing(options,UI) {
var getPixels = require('get-pixels');
// This function is called everytime a step has to be redrawn
function draw(input,callback) {
function draw(input,callback,progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
@@ -18,16 +21,25 @@ module.exports = function DoNothing(options,UI) {
var w = pixels.shape[0];
var h = pixels.shape[1];
var decoded = jsQR.decodeQRFromImage(pixels.data,w,h);
var decoded = jsQR(pixels.data,w,h);
// This output is accessible to Image Sequencer
step.output = input;
step.output.data = decoded;
// Tell Image Sequencer that this step is complete
callback();
options.step.qrval = decoded;
options.step.qrval = (decoded)?decoded.data:"undefined";
});
function output(image, datauri, mimetype){
// This output is accessible by Image Sequencer
step.output = {
src: datauri,
format: mimetype
};
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
format: input.format,
image: options.image,
callback: callback
});
}

View File

@@ -5,8 +5,8 @@
},
"outputs": {
"qrval": {
"type": "text"
"type": "string"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#decodeqr-module"
}

View File

@@ -0,0 +1,76 @@
module.exports = function Dither(pixels, type) {
type = type;
let bayerThresholdMap = [
[15, 135, 45, 165],
[195, 75, 225, 105],
[60, 180, 30, 150],
[240, 120, 210, 90]
];
let lumR = [];
let lumG = [];
let lumB = [];
for (let i = 0; i < 256; i++) {
lumR[i] = i * 0.299;
lumG[i] = i * 0.587;
lumB[i] = i * 0.114;
}
let threshold = 129;
let imageDataLength = pixels.data.length; //imageData.data.length;
// Greyscale luminance (sets r pixels to luminance of rgb)
for (let i = 0; i <= imageDataLength; i++) {
pixels.data[i] = Math.floor(lumR[pixels.data[i++]] + lumG[pixels.data[i++]] + lumB[pixels.data[i++]]);
}
let w = pixels.shape[0];
let newPixel, err;
for (let currentPixel = 0; currentPixel <= imageDataLength; currentPixel += 4) {
if (type === "none") {
// No dithering
pixels.data[currentPixel] = pixels.data[currentPixel] < threshold ? 0 : 255;
} else if (type === "bayer") {
// 4x4 Bayer ordered dithering algorithm
let x = currentPixel / 4 % w;
let y = Math.floor(currentPixel / 4 / w);
let map = Math.floor((pixels.data[currentPixel] + bayerThresholdMap[x % 4][y % 4]) / 2);
pixels.data[currentPixel] = (map < threshold) ? 0 : 255;
} else if (type === "floydsteinberg") {
// FloydSteinberg dithering algorithm
newPixel = pixels.data[currentPixel] < 129 ? 0 : 255;
err = Math.floor((pixels.data[currentPixel] - newPixel) / 16);
pixels.data[currentPixel] = newPixel;
pixels.data[currentPixel + 4] += err * 7;
pixels.data[currentPixel + 4 * w - 4] += err * 3;
pixels.data[currentPixel + 4 * w] += err * 5;
pixels.data[currentPixel + 4 * w + 4] += err * 1;
} else {
// Bill Atkinson's dithering algorithm
newPixel = pixels.data[currentPixel] < threshold ? 0 : 255;
err = Math.floor((pixels.data[currentPixel] - newPixel) / 8);
pixels.data[currentPixel] = newPixel;
pixels.data[currentPixel + 4] += err;
pixels.data[currentPixel + 8] += err;
pixels.data[currentPixel + 4 * w - 4] += err;
pixels.data[currentPixel + 4 * w] += err;
pixels.data[currentPixel + 4 * w + 4] += err;
pixels.data[currentPixel + 8 * w] += err;
}
// Set g and b pixels equal to r
pixels.data[currentPixel + 1] = pixels.data[currentPixel + 2] = pixels.data[currentPixel];
}
return pixels;
}

View File

@@ -0,0 +1,38 @@
module.exports = function Dither(options, UI){
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
var output;
function draw(input,callback,progressObj){
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
options.dither = options.dither || defaults.dither;
function extraManipulation(pixels) {
pixels = require('./Dither')(pixels, options.dither)
return pixels
}
function output(image, datauri, mimetype){
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
callback: callback
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

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

View File

@@ -0,0 +1,14 @@
{
"name": "Dither",
"description": "Approximates a color from a mixture of other colors when the required color is not available, creating illusions of the color that is not present actually.<a href='https://en.wikipedia.org/wiki/Dither'>Read more</a>",
"inputs": {
"dither": {
"type": "select",
"desc": "Name of the Dithering Algorithm",
"default": "none",
"values": ["none","floydsteinberg","bayer","Atkinson"]
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#dither-module"
}

View File

@@ -0,0 +1,32 @@
module.exports = exports = function(pixels, options){
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.startingX = options.startingX || defaults.startingX;
options.startingY = options.startingY || defaults.startingY;
var ox = Number(options.startingX),
oy = Number(options.startingY),
iw = pixels.shape[0],
ih = pixels.shape[1],
thickness = Number(options.thickness) || defaults.thickness,
ex = options.endX = Number(options.endX) - thickness || iw - 1,
ey = options.endY = Number(options.endY) -thickness || ih - 1,
color = options.color || defaults.color;
color = color.split(" ");
var drawSide = function(startX, startY, endX, endY){
for (var n=startX; n <= endX+thickness; n++){
for (var k=startY; k <= endY+thickness; k++){
pixels.set(n, k, 0, color[0]);
pixels.set(n, k, 1, color[1]);
pixels.set(n, k, 2, color[2]);
pixels.set(n, k, 3, color[3]);
}
}
}
drawSide(ox, oy, ox, ey); // Left
drawSide(ex, oy, ex, ey); // Right
drawSide(ox, oy, ex, oy); // Top
drawSide(ox, ey, ex, ey); // Bottom
return pixels;
}

View File

@@ -0,0 +1,44 @@
module.exports = function DrawRectangle(options, UI) {
var output;
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
function changePixel(r, g, b, a) {
return [r, g, b, a]
}
function extraManipulation(pixels) {
pixels = require('./DrawRectangle')(pixels, options)
return pixels
}
function output(image, datauri, mimetype) {
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
callback: callback
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

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

View File

@@ -0,0 +1,42 @@
{
"name": "Draw Rectangle",
"description": "It draws a rectangle on the image",
"inputs": {
"startingX":{
"type": "integer",
"desc": "starting x position of the rectangle",
"default": 0
},
"startingY": {
"type": "integer",
"desc": "starting y position of the rectangle",
"default": 0
},
"endX":{
"type": "integer",
"desc": "last x position of the rectangle",
"default": "width"
},
"endY":{
"type": "integer",
"desc": "last y position of the rectangle",
"default": "height"
},
"thickness":{
"type": "integer",
"desc": "thickness of border",
"default": 1
},
"color":{
"type": "string",
"desc": "RGBA values separated by a space",
"default": "0 0 0 255"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#draw-rectangle-module"
}

View File

@@ -3,25 +3,25 @@
"description": "A module which accepts JavaScript math expressions to produce each color channel based on the original image's color. See <a href='https://publiclab.org/wiki/infragram-sandbox'>Infragrammar</a>.",
"inputs": {
"red": {
"type": "input",
"type": "string",
"desc": "Expression to return for red channel with R, G, B, and A inputs",
"default": "r"
},
"green": {
"type": "input",
"type": "string",
"desc": "Expression to return for green channel with R, G, B, and A inputs",
"default": "g"
},
"blue": {
"type": "input",
"type": "string",
"desc": "Expression to return for blue channel with R, G, B, and A inputs",
"default": "b"
},
"monochrome (fallback)": {
"type": "input",
"type": "string",
"desc": "Expression to return with R, G, B, and A inputs; fallback for other channels if none provided",
"default": "r + g + b"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#dynamic-module"
}

View File

@@ -1,174 +1,201 @@
const _ = require('lodash')
// Define kernels for the sobel filter
const kernelx = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
],
kernely = [
[-1,-2,-1],
[ 0, 0, 0],
[ 1, 2, 1]
];
//define kernels for the sobel filter
const kernelx = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]],
kernely = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
let pixelsToBeSupressed = [];
module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, inBrowser) {
let angles = [], mags = [], strongEdgePixels = [], weakEdgePixels = [], notInUI = !inBrowser;
for (var x = 0; x < pixels.shape[0]; x++) {
angles.push([]);
mags.push([]);
for (var y = 0; y < pixels.shape[1]; y++) {
var result = changePixel(
pixels,
pixels.get(x, y, 0),
pixels.get(x, y, 3),
x,
y
);
let pixel = result.pixel;
module.exports = function(pixels, highThresholdRatio, lowThresholdRatio, hysteresis) {
let angles = [], grads = [], strongEdgePixels = [], weakEdgePixels = [];
for (var x = 0; x < pixels.shape[0]; x++) {
grads.push([]);
angles.push([]);
for (var y = 0; y < pixels.shape[1]; y++) {
var result = sobelFilter(
pixels,
x,
y
);
let pixel = result.pixel;
pixels.set(x, y, 0, pixel[0]);
pixels.set(x, y, 1, pixel[1]);
pixels.set(x, y, 2, pixel[2]);
pixels.set(x, y, 3, pixel[3]);
mags.slice(-1)[0].push(pixel[3]);
angles.slice(-1)[0].push(result.angle);
}
grads.slice(-1)[0].push(pixel[3]);
angles.slice(-1)[0].push(result.angle);
}
nonMaxSupress(pixels, mags, angles);
doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, mags, strongEdgePixels, weakEdgePixels);
return pixels;
}
nonMaxSupress(pixels, grads, angles);
doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels);
if(hysteresis.toLowerCase() == 'true') hysteresis(strongEdgePixels, weakEdgePixels);
strongEdgePixels.forEach(pixel => preserve(pixels, pixel));
weakEdgePixels.forEach(pixel => supress(pixels, pixel));
pixelsToBeSupressed.forEach(pixel => supress(pixels, pixel));
return pixels;
}
//changepixel function that convolutes every pixel (sobel filter)
function changePixel(pixels, val, a, x, y) {
let magX = 0.0;
for (let a = 0; a < 3; a++) {
for (let b = 0; b < 3; b++) {
let xn = x + a - 1;
let yn = y + b - 1;
magX += pixels.get(xn, yn, 0) * kernelx[a][b];
}
}
let magY = 0.0;
for (let a = 0; a < 3; a++) {
for (let b = 0; b < 3; b++) {
let xn = x + a - 1;
let yn = y + b - 1;
magY += pixels.get(xn, yn, 0) * kernely[a][b];
}
}
let mag = Math.sqrt(Math.pow(magX, 2) + Math.pow(magY, 2));
let angle = Math.atan2(magY, magX);
return {
pixel:
[val, val, val, mag],
angle: angle
};
function supress(pixels, pixel) {
pixels.set(pixel[0], pixel[1], 0, 0);
pixels.set(pixel[0], pixel[1], 1, 0);
pixels.set(pixel[0], pixel[1], 2, 0);
pixels.set(pixel[0], pixel[1], 3, 255);
}
//Non Maximum Supression without interpolation
function nonMaxSupress(pixels, mags, angles) {
angles = angles.map((arr) => arr.map(convertToDegrees));
for (let i = 1; i < pixels.shape[0] - 1; i++) {
for (let j = 1; j < pixels.shape[1] - 1; j++) {
let angle = angles[i][j];
let pixel = pixels.get(i, j);
if ((angle >= -22.5 && angle <= 22.5) ||
(angle < -157.5 && angle >= -180))
if ((mags[i][j] >= mags[i][j + 1]) &&
(mags[i][j] >= mags[i][j - 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 22.5 && angle <= 67.5) ||
(angle < -112.5 && angle >= -157.5))
if ((mags[i][j] >= mags[i + 1][j + 1]) &&
(mags[i][j] >= mags[i - 1][j - 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 67.5 && angle <= 112.5) ||
(angle < -67.5 && angle >= -112.5))
if ((mags[i][i] >= mags[i + 1][j]) &&
(mags[i][j] >= mags[i][j]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
else if ((angle >= 112.5 && angle <= 157.5) ||
(angle < -22.5 && angle >= -67.5))
if ((mags[i][j] >= mags[i + 1][j - 1]) &&
(mags[i][j] >= mags[i - 1][j + 1]))
pixels.set(i, j, 3, mags[i][j]);
else
pixels.set(i, j, 3, 0);
}
}
function preserve(pixels, pixel) {
pixels.set(pixel[0], pixel[1], 0, 255);
pixels.set(pixel[0], pixel[1], 1, 255);
pixels.set(pixel[0], pixel[1], 2, 255);
pixels.set(pixel[0], pixel[1], 3, 255);
}
//Converts radians to degrees
// sobelFilter function that convolves sobel kernel over every pixel
function sobelFilter(pixels, x, y) {
let val = pixels.get(x, y, 0),
gradX = 0.0,
gradY = 0.0;
for (let a = 0; a < 3; a++) {
for (let b = 0; b < 3; b++) {
let xn = x + a - 1,
yn = y + b - 1;
if (isOutOfBounds(pixels, xn, yn)) {
gradX += pixels.get(xn+1, yn+1, 0) * kernelx[a][b];
gradY += pixels.get(xn+1, yn+1, 0) * kernely[a][b];
}
else {
gradX += pixels.get(xn, yn, 0) * kernelx[a][b];
gradY += pixels.get(xn, yn, 0) * kernely[a][b];
}
}
}
const grad = Math.sqrt(Math.pow(gradX, 2) + Math.pow(gradY, 2)),
angle = Math.atan2(gradY, gradX);
return {
pixel: [val, val, val, grad],
angle: angle
};
}
function categorizeAngle(angle){
if ((angle >= -22.5 && angle <= 22.5) || (angle < -157.5 && angle >= -180)) return 1;
else if ((angle >= 22.5 && angle <= 67.5) || (angle < -112.5 && angle >= -157.5)) return 2;
else if ((angle >= 67.5 && angle <= 112.5) || (angle < -67.5 && angle >= -112.5)) return 3;
else if ((angle >= 112.5 && angle <= 157.5) || (angle < -22.5 && angle >= -67.5)) return 4;
/* Category Map
* 1 => E-W
* 2 => NE-SW
* 3 => N-S
* 4 => NW-SE
*/
}
function isOutOfBounds(pixels, x, y){
return ((x < 0) || (y < 0) || (x >= pixels.shape[0]) || (y >= pixels.shape[1]));
}
const removeElem = (arr = [], elem) => {
return arr = arr.filter((arrelem) => {
return arrelem !== elem;
})
}
// Non Maximum Supression without interpolation
function nonMaxSupress(pixels, grads, angles) {
angles = angles.map((arr) => arr.map(convertToDegrees));
for (let x = 0; x < pixels.shape[0]; x++) {
for (let y = 0; y < pixels.shape[1]; y++) {
let angleCategory = categorizeAngle(angles[x][y]);
if (!isOutOfBounds(pixels, x - 1, y - 1) && !isOutOfBounds(pixels, x+1, y+1)){
switch (angleCategory){
case 1:
if (!((grads[x][y] >= grads[x][y + 1]) && (grads[x][y] >= grads[x][y - 1]))) {
pixelsToBeSupressed.push([x, y]);
}
break;
case 2:
if (!((grads[x][y] >= grads[x + 1][y + 1]) && (grads[x][y] >= grads[x - 1][y - 1]))){
pixelsToBeSupressed.push([x, y]);
}
break;
case 3:
if (!((grads[x][y] >= grads[x + 1][y]) && (grads[x][y] >= grads[x - 1][y]))) {
pixelsToBeSupressed.push([x, y]);
}
break;
case 4:
if (!((grads[x][y] >= grads[x + 1][y - 1]) && (grads[x][y] >= grads[x - 1][y + 1]))) {
pixelsToBeSupressed.push([x, y]);
}
break;
}
}
}
}
}
// Converts radians to degrees
var convertToDegrees = radians => (radians * 180) / Math.PI;
//Finds the max value in a 2d array like mags
// Finds the max value in a 2d array like grads
var findMaxInMatrix = arr => Math.max(...arr.map(el => el.map(val => !!val ? val : 0)).map(el => Math.max(...el)));
//Applies the double threshold to the image
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, mags, strongEdgePixels, weakEdgePixels) {
// Applies the double threshold to the image
function doubleThreshold(pixels, highThresholdRatio, lowThresholdRatio, grads, strongEdgePixels, weakEdgePixels) {
const highThreshold = findMaxInMatrix(mags) * highThresholdRatio;
const lowThreshold = highThreshold * lowThresholdRatio;
const highThreshold = findMaxInMatrix(grads) * highThresholdRatio,
lowThreshold = highThreshold * lowThresholdRatio;
for (let i = 0; i < pixels.shape[0]; i++) {
for (let j = 0; j < pixels.shape[1]; j++) {
let pixelPos = [i, j];
for (let x = 0; x < pixels.shape[0]; x++) {
for (let y = 0; y < pixels.shape[1]; y++) {
let pixelPos = [x, y];
mags[i][j] > lowThreshold
? mags[i][j] > highThreshold
? strongEdgePixels.push(pixelPos)
: weakEdgePixels.push(pixelPos)
: pixels.set(i, j, 3, 0);
if (grads[x][y] > lowThreshold){
if (grads[x][y] > highThreshold) {
strongEdgePixels.push(pixelPos);
}
else {
weakEdgePixels.push(pixelPos);
}
}
else {
pixelsToBeSupressed.push(pixelPos);
}
}
strongEdgePixels.forEach(pix => pixels.set(pix[0], pix[1], 3, 255));
}
}
// hysteresis edge tracking algorithm -- not working as of now
/* function hysteresis(pixels) {
function getNeighbouringPixelPositions(pixelPosition) {
let x = pixelPosition[0], y = pixelPosition[1]
return [[x + 1, y + 1],
[x + 1, y],
[x + 1, y - 1],
[x, y + 1],
[x, y - 1],
[x - 1, y + 1],
[x - 1, y],
[x - 1, y - 1]]
function hysteresis(strongEdgePixels, weakEdgePixels){
strongEdgePixels.forEach(pixel => {
let x = pixel[0],
y = pixel[1];
if (weakEdgePixels.includes([x+1, y])) {
removeElem(weakEdgePixels, [x+1, y]);
}
else if (weakEdgePixels.includes([x-1, y])) {
removeElem(weakEdgePixels, [x-1, y]);
}
//This can potentially be improved see https://en.wikipedia.org/wiki/Connected-component_labeling
for (weakPixel in weakEdgePixels) {
let neighbourPixels = getNeighbouringPixelPositions(weakEdgePixels[weakPixel])
for (pixel in neighbourPixels) {
if (strongEdgePixels.find(el => _.isEqual(el, neighbourPixels[pixel]))) {
pixels.set(weakPixel[0], weakPixel[1], 3, 255)
weakEdgePixels.splice(weakPixel, weakPixel)
break
}
}
}
weakEdgePixels.forEach(pix => pixels.set(pix[0], pix[1], 3, 0))
return pixels
} */
else if (weakEdgePixels.includes([x, y+1])) {
removeElem(weakEdgePixels, [x, y+1]);
}
else if(weakEdgePixels.includes([x, y-1])) {
removeElem(weakEdgePixels, [x, y-1]);
}
})
}

View File

@@ -3,9 +3,11 @@
*/
module.exports = function edgeDetect(options, UI) {
options.blur = options.blur || 2;
options.highThresholdRatio = options.highThresholdRatio || 0.2;
options.lowThresholdRatio = options.lowThresholdRatio || 0.15;
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.blur = options.blur || defaults.blur;
options.highThresholdRatio = options.highThresholdRatio || defaults.highThresholdRatio;
options.lowThresholdRatio = options.lowThresholdRatio || defaults.lowThresholdRatio;
options.hystereis = options.hysteresis || defaults.hysteresis;
var output;
@@ -18,22 +20,19 @@ module.exports = function edgeDetect(options, UI) {
var step = this;
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
function extraManipulation(pixels) {
pixels = require('ndarray-gaussian-filter')(pixels, options.blur);
pixels = require('./EdgeUtils')(pixels, options.highThresholdRatio, options.lowThresholdRatio, options.inBrowser);
return pixels;
}
// Extra Manipulation function used as an enveloper for applying gaussian blur and Convolution
function changePixel(r, g, b, a) {
return [(r + g + b) / 3, (r + g + b) / 3, (r + g + b) / 3, a];
}
function extraManipulation(pixels) {
pixels = require('ndarray-gaussian-filter')(pixels, options.blur);
pixels = require('./EdgeUtils')(pixels, options.highThresholdRatio, options.lowThresholdRatio, options.hystereis);
return pixels;
}
function output(image, datauri, mimetype) {
// This output is accessible by Image Sequencer
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
@@ -45,7 +44,6 @@ module.exports = function edgeDetect(options, UI) {
inBrowser: options.inBrowser,
callback: callback
});
}
return {

View File

@@ -1,22 +1,37 @@
{
"name": "Detect Edges",
"description": "this module detects edges using the Canny method, which first Gaussian blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge.<a href='https://en.wikipedia.org/wiki/Canny_edge_detector'> Read more. </a>",
"description": "This module detects edges using the Canny method, which first Gaussian blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge.<a href='https://en.wikipedia.org/wiki/Canny_edge_detector'> Read more. </a>",
"inputs": {
"blur": {
"type": "integer",
"desc": "amount of gaussian blur(Less blur gives more detail, typically 0-5)",
"default": 2
"type": "float",
"desc": "Amount of gaussian blur(Less blur gives more detail, typically 0-5)",
"default": 2,
"min": 0,
"max": 5,
"step": 0.25
},
"highThresholdRatio":{
"type": "float",
"desc": "The high threshold ratio for the image",
"default": 0.2
"default": 0.2,
"min": 0,
"max": 1,
"step": 0.25
},
"lowThresholdRatio": {
"type": "float",
"desc": "The low threshold value for the image",
"default": 0.15
}
"default": 0.15,
"min": 0,
"max": 1,
"step": 0.05
},
"hysteresis": {
"type": "select",
"desc": "Toggle Hysteresis",
"values": ["false", "true"],
"default": "false"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#edge-detect-module"
}

View File

@@ -0,0 +1,50 @@
/*
* Changes the Image Exposure
*/
module.exports = function Exposure(options,UI){
var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
var output;
function draw(input,callback,progressObj){
options.exposure = options.exposure || defaults.exposure
var exposure = Math.pow(2, options.exposure);
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
function changePixel(r, g, b, a){
r = Math.min(255, r*exposure)
g = Math.min(255, g*exposure)
b = Math.min(255, b*exposure)
return [r, g, b, a]
}
function output(image,datauri,mimetype){
// This output is accessible by Image Sequencer
step.output = {src:datauri,format:mimetype};
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

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

View File

@@ -0,0 +1,15 @@
{
"name": "Exposure",
"description": "Change the exposure of the image by given exposure value",
"inputs": {
"exposure": {
"type": "float",
"desc": "exposure value for the new image",
"default": 1,
"min": -3,
"max": 4,
"step": 0.05
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
}

View File

@@ -63,5 +63,5 @@
"default": "(inbuilt)"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#fisheyegl-module"
}

View File

@@ -0,0 +1,51 @@
/*
* Flip the image on vertical/horizontal axis.
*/
module.exports = function FlipImage(options, UI) {
options.Axis = options.Axis || require('./info.json').inputs.Axis.default;
var output,
getPixels = require('get-pixels');
function draw(input, callback, progressObj) {
progressObj.stop(true);
progressObj.overrideFlag = true;
var step = this;
return getPixels(input.src, function(err, oldPixels){
function changePixel(r, g, b, a) {
return [r, g, b, a];
}
function extraManipulation(pixels) {
if (err){
console.log(err);
return;
}
return require('./flipImage')(oldPixels, pixels, options.Axis);
}
function output(image, datauri, mimetype) {
step.output = { src: datauri, format: mimetype };
}
return require('../_nomodule/PixelManipulation.js')(input, {
output: output,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback
});
})
}
return {
options: options,
draw: draw,
output: output,
UI: UI
}
}

View File

@@ -0,0 +1,31 @@
module.exports = function flipImage(oldPixels, pixels, axis) {
var width = oldPixels.shape[0],
height = oldPixels.shape[1];
function copyPixel(x1, y1, x2, y2){
pixels.set(x1, y1, 0, oldPixels.get(x2, y2, 0))
pixels.set(x1, y1, 1, oldPixels.get(x2, y2, 1))
pixels.set(x1, y1, 2, oldPixels.get(x2, y2, 2))
pixels.set(x1, y1, 3, oldPixels.get(x2, y2, 3))
}
function flip(){
if(axis.toLowerCase() == 'vertical'){
for (var n=0; n < width; n++){
for (var m=0; m < height; m++){
copyPixel(n, m, n, height - m - 1);
}
}
}
else {
for (var n=0; n < width; n++){
for (var m=0; m < height; m++){
copyPixel(n, m, width - n - 1, m);
}
}
}
}
flip();
return pixels;
}

View File

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

View File

@@ -0,0 +1,13 @@
{
"name": "Flip Image",
"description": "Flip The Image On The Specified Axis.",
"inputs": {
"Axis": {
"type": "select",
"desc": "Axis",
"default": "Vertical",
"values": ["Horizontal", "Vertical"]
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#flipimage-module"
}

Some files were not shown because too many files have changed in this diff Show More