├── .gitignore ├── demo ├── images │ ├── plus.png │ ├── minus.png │ └── help-browser-2.png ├── static │ └── css │ │ ├── nist_logo.png │ │ ├── nist_logo_mark.png │ │ └── NISTPages.css ├── style.css ├── spinner.js ├── spinner-slider.js ├── index.html └── demo.js ├── .eslintrc.json ├── LICENSE ├── package.json ├── webpack.config.js ├── README.md └── openseadragon-filtering.js /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject/ 2 | .directory 3 | dist/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /demo/images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/plus.png -------------------------------------------------------------------------------- /demo/images/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/minus.png -------------------------------------------------------------------------------- /demo/images/help-browser-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/images/help-browser-2.png -------------------------------------------------------------------------------- /demo/static/css/nist_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/static/css/nist_logo.png -------------------------------------------------------------------------------- /demo/static/css/nist_logo_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usnistgov/OpenSeadragonFiltering/HEAD/demo/static/css/nist_logo_mark.png -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/eslint-config-defaults/configurations/google.js", 3 | "rules": { 4 | "indent": [2, 4], 5 | "newline-after-var": 0, 6 | "dot-location": [2, "property"] 7 | }, 8 | "globals": { 9 | "window": true, 10 | "define": true, 11 | "require": true, 12 | "Caman": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software was developed at the National Institute of Standards and 2 | Technology by employees of the Federal Government in the course of 3 | their official duties. Pursuant to title 17 Section 105 of the United 4 | States Code this software is not subject to copyright protection and is 5 | in the public domain. This software is an experimental system. NIST assumes 6 | no responsibility whatsoever for its use by other parties, and makes no 7 | guarantees, expressed or implied, about its quality, reliability, or 8 | any other characteristic. We would appreciate acknowledgement if the 9 | software is used. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openseadragon-filtering", 3 | "version": "1.0.0", 4 | "description": "OpenSeadragon plugin providing filtering capabilities to the images.", 5 | "main": "openseadragon-filtering.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/usnistgov/OpenSeadragonFiltering.git" 9 | }, 10 | "keywords": [ 11 | "openseadragon", 12 | "filtering", 13 | "image", 14 | "processing", 15 | "zoom", 16 | "seadragon", 17 | "deepzoom" 18 | ], 19 | "author": "Antoine Vandecreme", 20 | "license": "SEE LICENSE IN LICENSE", 21 | "bugs": { 22 | "url": "https://github.com/usnistgov/OpenSeadragonFiltering/issues" 23 | }, 24 | "homepage": "https://github.com/usnistgov/OpenSeadragonFiltering#readme", 25 | "dependencies": { 26 | "openseadragon": ">=2.1" 27 | }, 28 | "devDependencies": { 29 | "copy-webpack-plugin": "^5.0.0", 30 | "css-loader": "^0.23.1", 31 | "eslint": "^2.1.0", 32 | "eslint-config-defaults": "^9.0.0", 33 | "eslint-loader": "^1.3.0", 34 | "file-loader": "^3.0.1", 35 | "jquery": "^3.3.0", 36 | "style-loader": "^0.13.0", 37 | "webpack": "^4.29.5", 38 | "webpack-cli": "^3.2.3", 39 | "webpack-jquery-ui": "^2.0.1" 40 | }, 41 | "scripts": { 42 | "build": "webpack", 43 | "watch": "webpack --watch", 44 | "clean": "rm -rf dist" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /demo/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | This software was developed at the National Institute of Standards and 3 | Technology by employees of the Federal Government in the course of 4 | their official duties. Pursuant to title 17 Section 105 of the United 5 | States Code this software is not subject to copyright protection and is 6 | in the public domain. This software is an experimental system. NIST assumes 7 | no responsibility whatsoever for its use by other parties, and makes no 8 | guarantees, expressed or implied, about its quality, reliability, or 9 | any other characteristic. We would appreciate acknowledgement if the 10 | software is used. 11 | */ 12 | .demo { 13 | line-height: normal; 14 | } 15 | 16 | .demo h3 { 17 | margin-top: 5px; 18 | margin-bottom: 5px; 19 | } 20 | 21 | #openseadragon { 22 | width: 100%; 23 | height: 700px; 24 | background-color: black; 25 | } 26 | 27 | .wdzt-table-layout { 28 | display: table; 29 | } 30 | 31 | .wdzt-row-layout { 32 | display: table-row; 33 | } 34 | 35 | .wdzt-cell-layout { 36 | display: table-cell; 37 | } 38 | 39 | .wdzt-full-width { 40 | width: 100%; 41 | } 42 | 43 | .wdzt-menu-slider { 44 | margin-left: 10px; 45 | margin-right: 10px; 46 | } 47 | 48 | .column-2 { 49 | width: 50%; 50 | vertical-align: top; 51 | padding: 3px; 52 | } 53 | 54 | #available { 55 | list-style-type: none; 56 | } 57 | 58 | ul { 59 | padding: 0; 60 | border: 1px solid black; 61 | min-height: 25px; 62 | } 63 | 64 | li { 65 | padding: 3px; 66 | } 67 | 68 | #selected { 69 | list-style-type: none; 70 | } 71 | 72 | .button { 73 | cursor: pointer; 74 | vertical-align: text-top; 75 | } 76 | 77 | .filterLabel { 78 | min-width: 120px; 79 | } 80 | 81 | #selected .filterLabel { 82 | cursor: move; 83 | } 84 | -------------------------------------------------------------------------------- /demo/static/css/NISTPages.css: -------------------------------------------------------------------------------- 1 | .nist { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | .nist-header { 6 | box-sizing: border-box; 7 | background: #000; 8 | color: #fff; 9 | padding: 10px 20px 4px; 10 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 11 | position: relative; 12 | margin-bottom: 0px; 13 | } 14 | .nist-header:after { 15 | content: ""; 16 | display: table; 17 | clear: both; 18 | } 19 | .nist-header * { 20 | box-sizing: inherit; 21 | } 22 | .nist-header a, .nist-header a:link, .nist-header a:visited { 23 | color: #EEE; 24 | text-decoration: none; 25 | } 26 | .nist-header h1 { 27 | margin: 0 10px 0 0; 28 | float: left; 29 | font-size: 0; 30 | } 31 | .nist-header h1 a { 32 | font-weight: normal; 33 | font-size: 20px; 34 | height: 40px; 35 | width: 280px; 36 | background: url('nist_logo.png') no-repeat; 37 | background-size: 280px auto; 38 | text-indent: -9999px; 39 | overflow: hidden; 40 | display: inline-block; 41 | } 42 | .nist-header-text { 43 | position: absolute; 44 | left: -9999px; 45 | background: yellow; 46 | } 47 | .nist-links { 48 | float: right; 49 | } 50 | a.nist-links-button { 51 | display: inline-block; 52 | padding: 6px; 53 | margin: 0 2px; 54 | border-radius: 3px; 55 | border: 1px solid #666; 56 | font-size: 14px; 57 | } 58 | .nist-header a.nist-links-button:hover, 59 | .nist-header a.nist-links-button:focus { 60 | background: #ccc; 61 | color: #333; 62 | } 63 | 64 | @media screen and (max-width: 700px) { 65 | .nist-header { 66 | padding: 6px 10px; 67 | } 68 | .nist-header h1 a { 69 | height: 26px; 70 | width: 80px; 71 | background: url('nist_logo_mark.png') 0 4px no-repeat; 72 | background-size: 80px auto; 73 | } 74 | .mobile-hide { 75 | display:none !important; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /demo/spinner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of 4 | * their official duties. Pursuant to title 17 Section 105 of the United 5 | * States Code this software is not subject to copyright protection and is 6 | * in the public domain. This software is an experimental system. NIST assumes 7 | * no responsibility whatsoever for its use by other parties, and makes no 8 | * guarantees, expressed or implied, about its quality, reliability, or 9 | * any other characteristic. We would appreciate acknowledgement if the 10 | * software is used. 11 | */ 12 | 13 | define('spinner', function() { 14 | /** 15 | * This class is an improvement over the basic jQuery spinner to support 16 | * 'Enter' to update the value (with validity checks). 17 | * @param {Object} options Options object 18 | * @return {Spinner} A spinner object 19 | */ 20 | return function(options) { 21 | 22 | options.$element.html(''); 24 | 25 | var $spinner = options.$element.find('input'); 26 | var value = options.init; 27 | $spinner.spinner({ 28 | min: options.min, 29 | max: options.max, 30 | step: options.step, 31 | spin: function(event, ui) { 32 | /*jshint unused:true */ 33 | value = ui.value; 34 | options.updateCallback(value); 35 | } 36 | }); 37 | $spinner.val(value); 38 | $spinner.keyup(function(e) { 39 | if (e.which === 13) { 40 | if (!this.value.match(/^-?\d?\.?\d*$/)) { 41 | this.value = options.init; 42 | } else if (options.min !== undefined && 43 | this.value < options.min) { 44 | this.value = options.min; 45 | } else if (options.max !== undefined && 46 | this.value > options.max) { 47 | this.value = options.max; 48 | } 49 | value = this.value; 50 | options.updateCallback(value); 51 | } 52 | }); 53 | 54 | this.getValue = function() { 55 | return value; 56 | }; 57 | 58 | }; 59 | 60 | }); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | 5 | module.exports = { 6 | mode: "development", 7 | context: __dirname, 8 | entry: ['./demo/demo.js'], 9 | output: { 10 | filename: 'demo-bundle.js', 11 | path: path.resolve(__dirname, 'dist'), 12 | publicPath:'dist/' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | enforce: "pre", 19 | loader: 'eslint-loader', 20 | exclude: /node_modules/, 21 | options: { 22 | configFile: '.eslintrc.json' 23 | } 24 | }, 25 | { 26 | test: /\.(jpe?g|png|gif)$/i, 27 | loader: 'file-loader', 28 | query: { 29 | name: '[name].[ext]', 30 | outputPath: 'images/' 31 | }, 32 | }, 33 | { 34 | test: /\.css$/, 35 | include: /node_modules/, 36 | use: ['style-loader', 'css-loader'] 37 | }, 38 | { 39 | test: /\.css$/, 40 | exclude: /node_modules/, 41 | use: ['style-loader', 'css-loader'] 42 | } 43 | ] 44 | }, 45 | resolve: { 46 | // options for resolving module requests 47 | // (does not apply to resolving to loaders) 48 | modules: [ 49 | "node_modules", 50 | path.join(__dirname, "demo") 51 | ], 52 | // directories where to look for modules 53 | extensions: [".js", ".json", ".jsx", ".css"] 54 | }, 55 | plugins: [ 56 | new CopyWebpackPlugin([{ 57 | from: 'node_modules/openseadragon/build/openseadragon/images', 58 | to: 'images' 59 | }, { 60 | from: 'demo/images', 61 | to: 'images' 62 | }, { 63 | from: 'demo/static', 64 | to: 'static' 65 | }, { 66 | from: 'demo/*' 67 | } 68 | ]), 69 | new webpack.LoaderOptionsPlugin({ 70 | test: /\.js$/, 71 | context: __dirname, 72 | debug: true, 73 | options: { 74 | eslint: { 75 | configFile: '.eslintrc.json' 76 | } 77 | } 78 | }), 79 | new webpack.ProvidePlugin( 80 | { 81 | $: 'jquery', 82 | jQuery: 'jquery', 83 | 'window.jQuery': 'jquery', 84 | 'window.$': 'jquery' 85 | } 86 | ) 87 | ] 88 | }; 89 | -------------------------------------------------------------------------------- /demo/spinner-slider.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of 4 | * their official duties. Pursuant to title 17 Section 105 of the United 5 | * States Code this software is not subject to copyright protection and is 6 | * in the public domain. This software is an experimental system. NIST assumes 7 | * no responsibility whatsoever for its use by other parties, and makes no 8 | * guarantees, expressed or implied, about its quality, reliability, or 9 | * any other characteristic. We would appreciate acknowledgement if the 10 | * software is used. 11 | */ 12 | 13 | define('spinner-slider', function() { 14 | 15 | var idIncrement = 0; 16 | 17 | return function(options) { 18 | 19 | this.hash = idIncrement++; 20 | 21 | var spinnerId = 'wdzt-spinner-slider-spinner-' + this.hash; 22 | var sliderId = 'wdzt-spinner-slider-slider-' + this.hash; 23 | 24 | var value = options.init; 25 | 26 | 27 | options.$element.html( 28 | '
' + 29 | '
' + 30 | '
' + 31 | ' ' + 33 | '
' + 34 | '
' + 35 | '
' + 36 | '
' + 37 | '
' + 38 | '
' + 39 | '
'); 40 | 41 | var $slider = options.$element.find('#' + sliderId) 42 | .slider({ 43 | min: options.min, 44 | max: options.sliderMax !== undefined ? 45 | options.sliderMax : options.max, 46 | step: options.step, 47 | value: value, 48 | slide: function(event, ui) { 49 | /*jshint unused:true */ 50 | value = ui.value; 51 | $spinner.spinner('value', value); 52 | options.updateCallback(value); 53 | } 54 | }); 55 | var $spinner = options.$element.find('#' + spinnerId) 56 | .spinner({ 57 | min: options.min, 58 | max: options.max, 59 | step: options.step, 60 | spin: function(event, ui) { 61 | /*jshint unused:true */ 62 | value = ui.value; 63 | $slider.slider('value', value); 64 | options.updateCallback(value); 65 | } 66 | }); 67 | $spinner.val(value); 68 | $spinner.keyup(function(e) { 69 | if (e.which === 13) { 70 | value = $spinner.spinner('value'); 71 | $slider.slider('value', value); 72 | options.updateCallback(value); 73 | } 74 | }); 75 | 76 | 77 | this.getValue = function() { 78 | return value; 79 | }; 80 | 81 | }; 82 | }); -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | OpenSeadragon Filtering 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |

29 | 30 |

31 | 36 |
37 | 38 |
39 |

OpenSeadragon filtering plugin demo.

40 |
41 | 42 |
43 |

44 | Demo of the OpenSeadragon filtering plugin. 45 | Code and documentation are available on 46 | GitHub. 47 |

48 |

49 | Add/remove filters to visualize the effects. 50 |

51 |
52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |

Available filters

61 |
    62 |
63 | 64 |

Selected filters

65 |
    66 | 67 |

    Drag and drop the selected filters to set their order.

    68 |
    69 |
    70 |
    71 |
    72 | 73 | 83 | 84 |
    85 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This [OpenSeadragon](http://openseadragon.github.io/) plugin provides 2 | the capability to add filters to the images. 3 | 4 | A demo is available [here](https://pages.nist.gov/OpenSeadragonFiltering/). 5 | 6 | This plugin requires OpenSeadragon 2.1+. 7 | 8 | ### Basic usage 9 | 10 | Increase the brightness: 11 | `````javascript 12 | var viewer = new OpenSeadragon.Viewer(...); 13 | viewer.setFilterOptions({ 14 | filters: { 15 | processors: OpenSeadragon.Filters.BRIGHTNESS(50) 16 | } 17 | }); 18 | ````` 19 | 20 | Decrease the brightness and invert the image: 21 | `````javascript 22 | var viewer = new OpenSeadragon.Viewer(...); 23 | viewer.setFilterOptions({ 24 | filters: { 25 | processors: [ 26 | OpenSeadragon.Filters.BRIGHTNESS(-50), 27 | OpenSeadragon.Filters.INVERT() 28 | ] 29 | } 30 | }); 31 | ````` 32 | 33 | ### Specify on which items (TiledImage) to apply the filters 34 | 35 | Increase the brightness on item 0 and invert items 1 and 2: 36 | `````javascript 37 | var viewer = new OpenSeadragon.Viewer(...); 38 | viewer.setFilterOptions({ 39 | filters: [{ 40 | items: viewer.world.getItemAt(0), 41 | processors: [ 42 | OpenSeadragon.Filters.BRIGHTNESS(50) 43 | ] 44 | }, { 45 | items: [viewer.world.getItemAt(1), viewer.world.getItemAt(2)], 46 | processors: [ 47 | OpenSeadragon.Filters.INVERT() 48 | ] 49 | }] 50 | }); 51 | ````` 52 | Note the items property. If it is not specified, the filter is applied to all 53 | items. 54 | 55 | ### Load mode optimization 56 | 57 | By default, the filters are applied asynchronously. This means that the tiles are 58 | cleared from the canvas and re-downloaded (note that the browser probably cached 59 | them though) before having the filter applied. This avoids to hang the browser 60 | if the filtering operation is slow. It also allows to use asynchronous filters 61 | like the ones provided by [CamanJS](http://camanjs.com). 62 | 63 | However, if you have only fast and synchronous filters, you can force the 64 | synchronous mode by setting `loadMode: 'sync'`: 65 | 66 | `````javascript 67 | var viewer = new OpenSeadragon.Viewer(...); 68 | viewer.setFilterOptions({ 69 | filters: { 70 | processors: OpenSeadragon.Filters.BRIGHTNESS(50) 71 | }, 72 | loadMode: 'sync' 73 | }); 74 | ````` 75 | 76 | To visualize the difference between async and sync mode, one can compare how 77 | the brightness (sync) and contrast (async) filters load in the 78 | [demo](https://pages.nist.gov/OpenSeadragonFiltering/). 79 | 80 | ### Provided filters 81 | 82 | This plugin already include some filters which are accessible via 83 | OpenSeadragon.Filters: 84 | 85 | * Thresholding 86 | 87 | Set all pixels equals or above the specified threshold to white and the others 88 | to black. For colored images, the average of the 3 channels is compared to the 89 | threshold. The specified threshold must be between 0 and 255. 90 | 91 | `````javascript 92 | var viewer = new OpenSeadragon.Viewer(...); 93 | viewer.setFilterOptions({ 94 | filters: { 95 | processors: OpenSeadragon.Filters.THRESHOLDING(threshold) 96 | } 97 | }); 98 | ````` 99 | 100 | * Brightness 101 | 102 | Shift the intensity of the pixels by the specified adjustment 103 | (between -255 and 255). 104 | 105 | `````javascript 106 | var viewer = new OpenSeadragon.Viewer(...); 107 | viewer.setFilterOptions({ 108 | filters: { 109 | processors: OpenSeadragon.Filters.BRIGHTNESS(adjustment) 110 | } 111 | }); 112 | ````` 113 | 114 | * Invert 115 | 116 | Invert the colors of the image. 117 | 118 | `````javascript 119 | var viewer = new OpenSeadragon.Viewer(...); 120 | viewer.setFilterOptions({ 121 | filters: { 122 | processors: OpenSeadragon.Filters.INVERT() 123 | } 124 | }); 125 | ````` 126 | 127 | * Morphological operations 128 | 129 | [Erosion](https://en.wikipedia.org/wiki/Erosion_%28morphology%29) 130 | and [dilation](https://en.wikipedia.org/wiki/Dilation_%28morphology%29) 131 | over a square kernel are supported by the generic 132 | [morphological operation](https://en.wikipedia.org/wiki/Mathematical_morphology) 133 | filter 134 | 135 | `````javascript 136 | var viewer = new OpenSeadragon.Viewer(...); 137 | viewer.setFilterOptions({ 138 | filters: { 139 | processors: [ 140 | // Erosion over a 3x3 kernel 141 | OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(3, Math.min), 142 | 143 | // Dilation over a 5x5 kernel 144 | OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION(5, Math.max), 145 | ] 146 | } 147 | }); 148 | ````` 149 | 150 | * Convolution 151 | 152 | Apply a [convolution kernel](https://en.wikipedia.org/wiki/Kernel_%28image_processing%29#Convolution). 153 | 154 | `````javascript 155 | var viewer = new OpenSeadragon.Viewer(...); 156 | viewer.setFilterOptions({ 157 | filters: { 158 | processors: OpenSeadragon.Filters.CONVOLUTION([ 159 | 0, -1, 0, 160 | -1, 5, -1, 161 | 0, -1, 0]) 162 | } 163 | }); 164 | ````` 165 | 166 | * Colormap 167 | 168 | Apply a [colormap](http://cxc.harvard.edu/ciao/threads/auxlut/index.html#auxlut) to the averaged RGB values of the image. 169 | Colormaps are defined by a series of [R,G,B] stops. 170 | Grayscale values of 0-255 are mapped to the interpolated stop values. 171 | A variable centerpoint allows adjustment of the colormap. 172 | 173 | `````javascript 174 | var viewer = new OpenSeadragon.Viewer(...); 175 | viewer.setFilterOptions({ 176 | filters: { 177 | processors: OpenSeadragon.Filters.COLORMAP([ 178 | [0, 0, 0], 179 | [0, 128, 0], 180 | [0, 250, 0], 181 | [0, 255, 0]], 128) 182 | } 183 | }); 184 | ````` 185 | 186 | ### Integration with CamanJS 187 | 188 | [CamanJS](http://camanjs.com) supports a wide range of filters. They can be 189 | reused with this plugin like this: 190 | 191 | `````javascript 192 | var viewer = new OpenSeadragon.Viewer(...); 193 | viewer.setFilterOptions({ 194 | filters: { 195 | processors: function(context, callback) { 196 | Caman(context.canvas, function() { 197 | this.sepia(50); 198 | this.vibrance(40); 199 | // Do not forget to call this.render with the callback 200 | this.render(callback); 201 | }); 202 | } 203 | } 204 | }); 205 | ````` 206 | 207 | Note: Caman is caching every canvas it processes. This causes two issues with 208 | this plugin: 209 | 1. It creates a memory leak because OpenSeadragon creates a lot of canvases 210 | which do not get garbage collected anymore. 211 | 2. Non-caman filters in between 2 camans filters get ignored. 212 | 213 | There isn't any clean way to 214 | [disable the cache system](https://github.com/meltingice/CamanJS/issues/185), 215 | however one can use this hack to prevent any caching: 216 | 217 | `````javascript 218 | Caman.Store.put = function() {}; 219 | 220 | var viewer = new OpenSeadragon.Viewer(...); 221 | viewer.setFilterOptions({ 222 | filters: { 223 | processors: [ 224 | function(context, callback) { 225 | Caman(context.canvas, function() { 226 | this.sepia(50); 227 | // Do not forget to call this.render with the callback 228 | this.render(callback); 229 | }); 230 | }, 231 | OpenSeadragon.Filters.INVERT(), 232 | function(context, callback) { 233 | Caman(context.canvas, function() { 234 | this.vibrance(40); 235 | // Do not forget to call this.render with the callback 236 | this.render(callback); 237 | }); 238 | } 239 | ] 240 | } 241 | }); 242 | ````` 243 | 244 | ### Implementing customs filters 245 | 246 | To implement a custom filter, one need to create a function taking a 247 | [2D context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) 248 | and a callback as parameters. When that function is called by the plugin, 249 | the context will be a tile's canvas context. One should use 250 | [context.getImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData) 251 | to retrieve the pixels values and 252 | [context.putImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData) 253 | to save the modified pixels. 254 | The callback method must be called when the filtering is done. The provided 255 | filters are good examples for such implementations. 256 | 257 | ### Edge effects 258 | 259 | This plugin is working on tiles and does not currently handle tiles edges. 260 | This means that if you are using kernel based filters, you should expect 261 | edge effects around tiles. 262 | 263 | ### Build the demo 264 | 265 | To build the demo run `npm install` and then `npm run-script build`. 266 | The result of the build will be in the `dist` folder. 267 | 268 | ### Disclaimer: 269 | 270 | This software was developed at the National Institute of Standards and 271 | Technology by employees of the Federal Government in the course of 272 | their official duties. Pursuant to title 17 Section 105 of the United 273 | States Code this software is not subject to copyright protection and is 274 | in the public domain. This software is an experimental system. NIST assumes 275 | no responsibility whatsoever for its use by other parties, and makes no 276 | guarantees, expressed or implied, about its quality, reliability, or 277 | any other characteristic. We would appreciate acknowledgement if the 278 | software is used. 279 | -------------------------------------------------------------------------------- /openseadragon-filtering.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of 4 | * their official duties. Pursuant to title 17 Section 105 of the United 5 | * States Code this software is not subject to copyright protection and is 6 | * in the public domain. This software is an experimental system. NIST assumes 7 | * no responsibility whatsoever for its use by other parties, and makes no 8 | * guarantees, expressed or implied, about its quality, reliability, or 9 | * any other characteristic. We would appreciate acknowledgement if the 10 | * software is used. 11 | */ 12 | 13 | /** 14 | * 15 | * @author Antoine Vandecreme 16 | */ 17 | (function() { 18 | 19 | 'use strict'; 20 | 21 | var $ = window.OpenSeadragon; 22 | if (!$) { 23 | $ = require('openseadragon'); 24 | if (!$) { 25 | throw new Error('OpenSeadragon is missing.'); 26 | } 27 | } 28 | // Requires OpenSeadragon >=2.1 29 | if (!$.version || $.version.major < 2 || 30 | $.version.major === 2 && $.version.minor < 1) { 31 | throw new Error( 32 | 'Filtering plugin requires OpenSeadragon version >= 2.1'); 33 | } 34 | 35 | $.Viewer.prototype.setFilterOptions = function(options) { 36 | if (!this.filterPluginInstance) { 37 | options = options || {}; 38 | options.viewer = this; 39 | this.filterPluginInstance = new $.FilterPlugin(options); 40 | } else { 41 | setOptions(this.filterPluginInstance, options); 42 | } 43 | }; 44 | 45 | /** 46 | * @class FilterPlugin 47 | * @param {Object} options The options 48 | * @param {OpenSeadragon.Viewer} options.viewer The viewer to attach this 49 | * plugin to. 50 | * @param {String} [options.loadMode='async'] Set to sync to have the filters 51 | * applied synchronously. It will only work if the filters are all synchronous. 52 | * Note that depending on how complex the filters are, it may also hang the browser. 53 | * @param {Object[]} options.filters The filters to apply to the images. 54 | * @param {OpenSeadragon.TiledImage[]} options.filters[x].items The tiled images 55 | * on which to apply the filter. 56 | * @param {function|function[]} options.filters[x].processors The processing 57 | * function(s) to apply to the images. The parameters of this function are 58 | * the context to modify and a callback to call upon completion. 59 | */ 60 | $.FilterPlugin = function(options) { 61 | options = options || {}; 62 | if (!options.viewer) { 63 | throw new Error('A viewer must be specified.'); 64 | } 65 | var self = this; 66 | this.viewer = options.viewer; 67 | 68 | this.viewer.addHandler('tile-loaded', tileLoadedHandler); 69 | this.viewer.addHandler('tile-drawing', tileDrawingHandler); 70 | 71 | // filterIncrement allows to determine whether a tile contains the 72 | // latest filters results. 73 | this.filterIncrement = 0; 74 | 75 | setOptions(this, options); 76 | 77 | 78 | function tileLoadedHandler(event) { 79 | var processors = getFiltersProcessors(self, event.tiledImage); 80 | if (processors.length === 0) { 81 | return; 82 | } 83 | var tile = event.tile; 84 | var image = event.image; 85 | if (image !== null && image !== undefined) { 86 | var canvas = window.document.createElement('canvas'); 87 | canvas.width = image.width; 88 | canvas.height = image.height; 89 | var context = canvas.getContext('2d'); 90 | context.drawImage(image, 0, 0); 91 | tile._renderedContext = context; 92 | var callback = event.getCompletionCallback(); 93 | applyFilters(context, processors, callback); 94 | tile._filterIncrement = self.filterIncrement; 95 | } 96 | } 97 | 98 | 99 | function applyFilters(context, filtersProcessors, callback) { 100 | if (callback) { 101 | var currentIncrement = self.filterIncrement; 102 | var callbacks = []; 103 | for (var i = 0; i < filtersProcessors.length - 1; i++) { 104 | (function(i) { 105 | callbacks[i] = function() { 106 | // If the increment has changed, stop the computation 107 | // chain immediately. 108 | if (self.filterIncrement !== currentIncrement) { 109 | return; 110 | } 111 | filtersProcessors[i + 1](context, callbacks[i + 1]); 112 | }; 113 | })(i); 114 | } 115 | callbacks[filtersProcessors.length - 1] = function() { 116 | // If the increment has changed, do not call the callback. 117 | // (We don't want OSD to draw an outdated tile in the canvas). 118 | if (self.filterIncrement !== currentIncrement) { 119 | return; 120 | } 121 | callback(); 122 | }; 123 | filtersProcessors[0](context, callbacks[0]); 124 | } else { 125 | for (var i = 0; i < filtersProcessors.length; i++) { 126 | filtersProcessors[i](context, function() { 127 | }); 128 | } 129 | } 130 | } 131 | 132 | function tileDrawingHandler(event) { 133 | var tile = event.tile; 134 | var rendered = event.rendered; 135 | if (rendered._filterIncrement === self.filterIncrement) { 136 | return; 137 | } 138 | var processors = getFiltersProcessors(self, event.tiledImage); 139 | if (processors.length === 0) { 140 | if (rendered._originalImageData) { 141 | // Restore initial data. 142 | rendered.putImageData(rendered._originalImageData, 0, 0); 143 | delete rendered._originalImageData; 144 | } 145 | rendered._filterIncrement = self.filterIncrement; 146 | return; 147 | } 148 | 149 | if (rendered._originalImageData) { 150 | // The tile has been previously filtered (by another filter), 151 | // restore it first. 152 | rendered.putImageData(rendered._originalImageData, 0, 0); 153 | } else { 154 | rendered._originalImageData = rendered.getImageData( 155 | 0, 0, rendered.canvas.width, rendered.canvas.height); 156 | } 157 | 158 | if (tile._renderedContext) { 159 | if (tile._filterIncrement === self.filterIncrement) { 160 | var imgData = tile._renderedContext.getImageData(0, 0, 161 | tile._renderedContext.canvas.width, 162 | tile._renderedContext.canvas.height); 163 | rendered.putImageData(imgData, 0, 0); 164 | delete tile._renderedContext; 165 | delete tile._filterIncrement; 166 | rendered._filterIncrement = self.filterIncrement; 167 | return; 168 | } 169 | delete tile._renderedContext; 170 | delete tile._filterIncrement; 171 | } 172 | applyFilters(rendered, processors); 173 | rendered._filterIncrement = self.filterIncrement; 174 | } 175 | }; 176 | 177 | function setOptions(instance, options) { 178 | options = options || {}; 179 | var filters = options.filters; 180 | instance.filters = !filters ? [] : 181 | $.isArray(filters) ? filters : [filters]; 182 | for (var i = 0; i < instance.filters.length; i++) { 183 | var filter = instance.filters[i]; 184 | if (!filter.processors) { 185 | throw new Error('Filter processors must be specified.'); 186 | } 187 | filter.processors = $.isArray(filter.processors) ? 188 | filter.processors : [filter.processors]; 189 | } 190 | instance.filterIncrement++; 191 | 192 | if (options.loadMode === 'sync') { 193 | instance.viewer.forceRedraw(); 194 | } else { 195 | var itemsToReset = []; 196 | for (var i = 0; i < instance.filters.length; i++) { 197 | var filter = instance.filters[i]; 198 | if (!filter.items) { 199 | itemsToReset = getAllItems(instance.viewer.world); 200 | break; 201 | } 202 | if ($.isArray(filter.items)) { 203 | for (var j = 0; j < filter.items.length; j++) { 204 | addItemToReset(filter.items[j], itemsToReset); 205 | } 206 | } else { 207 | addItemToReset(filter.items, itemsToReset); 208 | } 209 | } 210 | for (var i = 0; i < itemsToReset.length; i++) { 211 | itemsToReset[i].reset(); 212 | } 213 | } 214 | } 215 | 216 | function addItemToReset(item, itemsToReset) { 217 | if (itemsToReset.indexOf(item) >= 0) { 218 | throw new Error('An item can not have filters ' + 219 | 'assigned multiple times.'); 220 | } 221 | itemsToReset.push(item); 222 | } 223 | 224 | function getAllItems(world) { 225 | var result = []; 226 | for (var i = 0; i < world.getItemCount(); i++) { 227 | result.push(world.getItemAt(i)); 228 | } 229 | return result; 230 | } 231 | 232 | function getFiltersProcessors(instance, item) { 233 | if (instance.filters.length === 0) { 234 | return []; 235 | } 236 | 237 | var globalProcessors = null; 238 | for (var i = 0; i < instance.filters.length; i++) { 239 | var filter = instance.filters[i]; 240 | if (!filter.items) { 241 | globalProcessors = filter.processors; 242 | } else if (filter.items === item || 243 | $.isArray(filter.items) && filter.items.indexOf(item) >= 0) { 244 | return filter.processors; 245 | } 246 | } 247 | return globalProcessors ? globalProcessors : []; 248 | } 249 | 250 | $.Filters = { 251 | THRESHOLDING: function(threshold) { 252 | if (threshold < 0 || threshold > 255) { 253 | throw new Error('Threshold must be between 0 and 255.'); 254 | } 255 | return function(context, callback) { 256 | var imgData = context.getImageData( 257 | 0, 0, context.canvas.width, context.canvas.height); 258 | var pixels = imgData.data; 259 | for (var i = 0; i < pixels.length; i += 4) { 260 | var r = pixels[i]; 261 | var g = pixels[i + 1]; 262 | var b = pixels[i + 2]; 263 | var v = (r + g + b) / 3; 264 | pixels[i] = pixels[i + 1] = pixels[i + 2] = 265 | v < threshold ? 0 : 255; 266 | } 267 | context.putImageData(imgData, 0, 0); 268 | callback(); 269 | }; 270 | }, 271 | BRIGHTNESS: function(adjustment) { 272 | if (adjustment < -255 || adjustment > 255) { 273 | throw new Error( 274 | 'Brightness adjustment must be between -255 and 255.'); 275 | } 276 | var precomputedBrightness = []; 277 | for (var i = 0; i < 256; i++) { 278 | precomputedBrightness[i] = i + adjustment; 279 | } 280 | return function(context, callback) { 281 | var imgData = context.getImageData( 282 | 0, 0, context.canvas.width, context.canvas.height); 283 | var pixels = imgData.data; 284 | for (var i = 0; i < pixels.length; i += 4) { 285 | pixels[i] = precomputedBrightness[pixels[i]]; 286 | pixels[i + 1] = precomputedBrightness[pixels[i + 1]]; 287 | pixels[i + 2] = precomputedBrightness[pixels[i + 2]]; 288 | } 289 | context.putImageData(imgData, 0, 0); 290 | callback(); 291 | }; 292 | }, 293 | CONTRAST: function(adjustment) { 294 | if (adjustment < 0) { 295 | throw new Error('Contrast adjustment must be positive.'); 296 | } 297 | var precomputedContrast = []; 298 | for (var i = 0; i < 256; i++) { 299 | precomputedContrast[i] = i * adjustment; 300 | } 301 | return function(context, callback) { 302 | var imgData = context.getImageData( 303 | 0, 0, context.canvas.width, context.canvas.height); 304 | var pixels = imgData.data; 305 | for (var i = 0; i < pixels.length; i += 4) { 306 | pixels[i] = precomputedContrast[pixels[i]]; 307 | pixels[i + 1] = precomputedContrast[pixels[i + 1]]; 308 | pixels[i + 2] = precomputedContrast[pixels[i + 2]]; 309 | } 310 | context.putImageData(imgData, 0, 0); 311 | callback(); 312 | }; 313 | }, 314 | GAMMA: function(adjustment) { 315 | if (adjustment < 0) { 316 | throw new Error('Gamma adjustment must be positive.'); 317 | } 318 | var precomputedGamma = []; 319 | for (var i = 0; i < 256; i++) { 320 | precomputedGamma[i] = Math.pow(i / 255, adjustment) * 255; 321 | } 322 | return function(context, callback) { 323 | var imgData = context.getImageData( 324 | 0, 0, context.canvas.width, context.canvas.height); 325 | var pixels = imgData.data; 326 | for (var i = 0; i < pixels.length; i += 4) { 327 | pixels[i] = precomputedGamma[pixels[i]]; 328 | pixels[i + 1] = precomputedGamma[pixels[i + 1]]; 329 | pixels[i + 2] = precomputedGamma[pixels[i + 2]]; 330 | } 331 | context.putImageData(imgData, 0, 0); 332 | callback(); 333 | }; 334 | }, 335 | GREYSCALE: function() { 336 | return function(context, callback) { 337 | var imgData = context.getImageData( 338 | 0, 0, context.canvas.width, context.canvas.height); 339 | var pixels = imgData.data; 340 | for (var i = 0; i < pixels.length; i += 4) { 341 | var val = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3; 342 | pixels[i] = val; 343 | pixels[i + 1] = val; 344 | pixels[i + 2] = val; 345 | } 346 | context.putImageData(imgData, 0, 0); 347 | callback(); 348 | }; 349 | }, 350 | INVERT: function() { 351 | var precomputedInvert = []; 352 | for (var i = 0; i < 256; i++) { 353 | precomputedInvert[i] = 255 - i; 354 | } 355 | return function(context, callback) { 356 | var imgData = context.getImageData( 357 | 0, 0, context.canvas.width, context.canvas.height); 358 | var pixels = imgData.data; 359 | for (var i = 0; i < pixels.length; i += 4) { 360 | pixels[i] = precomputedInvert[pixels[i]]; 361 | pixels[i + 1] = precomputedInvert[pixels[i + 1]]; 362 | pixels[i + 2] = precomputedInvert[pixels[i + 2]]; 363 | } 364 | context.putImageData(imgData, 0, 0); 365 | callback(); 366 | }; 367 | }, 368 | MORPHOLOGICAL_OPERATION: function(kernelSize, comparator) { 369 | if (kernelSize % 2 === 0) { 370 | throw new Error('The kernel size must be an odd number.'); 371 | } 372 | var kernelHalfSize = Math.floor(kernelSize / 2); 373 | 374 | if (!comparator) { 375 | throw new Error('A comparator must be defined.'); 376 | } 377 | 378 | return function(context, callback) { 379 | var width = context.canvas.width; 380 | var height = context.canvas.height; 381 | var imgData = context.getImageData(0, 0, width, height); 382 | var originalPixels = context.getImageData(0, 0, width, height) 383 | .data; 384 | var offset; 385 | 386 | for (var y = 0; y < height; y++) { 387 | for (var x = 0; x < width; x++) { 388 | offset = (y * width + x) * 4; 389 | var r = originalPixels[offset]; 390 | var g = originalPixels[offset + 1]; 391 | var b = originalPixels[offset + 2]; 392 | for (var j = 0; j < kernelSize; j++) { 393 | for (var i = 0; i < kernelSize; i++) { 394 | var pixelX = x + i - kernelHalfSize; 395 | var pixelY = y + j - kernelHalfSize; 396 | if (pixelX >= 0 && pixelX < width && 397 | pixelY >= 0 && pixelY < height) { 398 | offset = (pixelY * width + pixelX) * 4; 399 | r = comparator(originalPixels[offset], r); 400 | g = comparator( 401 | originalPixels[offset + 1], g); 402 | b = comparator( 403 | originalPixels[offset + 2], b); 404 | } 405 | } 406 | } 407 | imgData.data[offset] = r; 408 | imgData.data[offset + 1] = g; 409 | imgData.data[offset + 2] = b; 410 | } 411 | } 412 | context.putImageData(imgData, 0, 0); 413 | callback(); 414 | }; 415 | }, 416 | CONVOLUTION: function(kernel) { 417 | if (!$.isArray(kernel)) { 418 | throw new Error('The kernel must be an array.'); 419 | } 420 | var kernelSize = Math.sqrt(kernel.length); 421 | if ((kernelSize + 1) % 2 !== 0) { 422 | throw new Error('The kernel must be a square matrix with odd' + 423 | 'width and height.'); 424 | } 425 | var kernelHalfSize = (kernelSize - 1) / 2; 426 | 427 | return function(context, callback) { 428 | var width = context.canvas.width; 429 | var height = context.canvas.height; 430 | var imgData = context.getImageData(0, 0, width, height); 431 | var originalPixels = context.getImageData(0, 0, width, height) 432 | .data; 433 | var offset; 434 | 435 | for (var y = 0; y < height; y++) { 436 | for (var x = 0; x < width; x++) { 437 | var r = 0; 438 | var g = 0; 439 | var b = 0; 440 | for (var j = 0; j < kernelSize; j++) { 441 | for (var i = 0; i < kernelSize; i++) { 442 | var pixelX = x + i - kernelHalfSize; 443 | var pixelY = y + j - kernelHalfSize; 444 | if (pixelX >= 0 && pixelX < width && 445 | pixelY >= 0 && pixelY < height) { 446 | offset = (pixelY * width + pixelX) * 4; 447 | var weight = kernel[j * kernelSize + i]; 448 | r += originalPixels[offset] * weight; 449 | g += originalPixels[offset + 1] * weight; 450 | b += originalPixels[offset + 2] * weight; 451 | } 452 | } 453 | } 454 | offset = (y * width + x) * 4; 455 | imgData.data[offset] = r; 456 | imgData.data[offset + 1] = g; 457 | imgData.data[offset + 2] = b; 458 | } 459 | } 460 | context.putImageData(imgData, 0, 0); 461 | callback(); 462 | }; 463 | }, 464 | COLORMAP: function(cmap, ctr) { 465 | var resampledCmap = cmap.slice(0); 466 | var diff = 255 - ctr; 467 | for(var i = 0; i < 256; i++) { 468 | var position = 0; 469 | if(i > ctr) { 470 | position = Math.min((i - ctr) / diff * 128 + 128,255) | 0; 471 | }else{ 472 | position = Math.max(0, i / (ctr / 128)) | 0; 473 | } 474 | resampledCmap[i] = cmap[position]; 475 | } 476 | return function(context, callback) { 477 | var imgData = context.getImageData( 478 | 0, 0, context.canvas.width, context.canvas.height); 479 | var pxl = imgData.data; 480 | for (var i = 0; i < pxl.length; i += 4) { 481 | var v = (pxl[i] + pxl[i + 1] + pxl[i + 2]) / 3 | 0; 482 | var c = resampledCmap[v]; 483 | pxl[i] = c[0]; 484 | pxl[i + 1] = c[1]; 485 | pxl[i + 2] = c[2]; 486 | } 487 | context.putImageData(imgData, 0, 0); 488 | callback(); 489 | }; 490 | } 491 | }; 492 | 493 | }()); 494 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This software was developed at the National Institute of Standards and 3 | * Technology by employees of the Federal Government in the course of 4 | * their official duties. Pursuant to title 17 Section 105 of the United 5 | * States Code this software is not subject to copyright protection and is 6 | * in the public domain. This software is an experimental system. NIST assumes 7 | * no responsibility whatsoever for its use by other parties, and makes no 8 | * guarantees, expressed or implied, about its quality, reliability, or 9 | * any other characteristic. We would appreciate acknowledgement if the 10 | * software is used. 11 | */ 12 | 13 | /** 14 | * 15 | * @author Antoine Vandecreme 16 | */ 17 | 18 | require('file-loader?name=[name].[ext]!./index.html'); 19 | require('style-loader?name=[name].[ext]!./style.css'); 20 | 21 | 22 | var $ = require('jquery'); 23 | require('webpack-jquery-ui'); 24 | require('webpack-jquery-ui/css'); 25 | var Spinner = require('./spinner'); 26 | var SpinnerSlider = require('./spinner-slider'); 27 | 28 | var OpenSeadragon = require('openseadragon'); 29 | require('../openseadragon-filtering'); 30 | var viewer = new OpenSeadragon({ 31 | id: 'openseadragon', 32 | prefixUrl: 'images/', 33 | tileSources: '//openseadragon.github.io/example-images/highsmith/highsmith.dzi', 34 | crossOriginPolicy: 'Anonymous' 35 | }); 36 | 37 | // Prevent Caman from caching the canvas because without this: 38 | // 1. We have a memory leak 39 | // 2. Non-caman filters in between 2 camans filters get ignored. 40 | var caman = Caman; 41 | caman.Store.put = function() {}; 42 | 43 | // List of filters with their templates. 44 | var availableFilters = [ 45 | { 46 | name: 'Invert', 47 | generate: function() { 48 | return { 49 | html: '', 50 | getParams: function() { 51 | return ''; 52 | }, 53 | getFilter: function() { 54 | /*eslint new-cap: 0*/ 55 | return OpenSeadragon.Filters.INVERT(); 56 | }, 57 | sync: true 58 | }; 59 | } 60 | }, { 61 | name: 'Colormap', 62 | generate: function(updateCallback) { 63 | var cmaps = { 64 | aCm: [ [0,0,0], [0,4,0], [0,8,0], [0,12,0], [0,16,0], [0,20,0], [0,24,0], [0,28,0], [0,32,0], [0,36,0], [0,40,0], [0,44,0], [0,48,0], [0,52,0], [0,56,0], [0,60,0], [0,64,0], [0,68,0], [0,72,0], [0,76,0], [0,80,0], [0,85,0], [0,89,0], [0,93,0], [0,97,0], [0,101,0], [0,105,0], [0,109,0], [0,113,0], [0,117,0], [0,121,0], [0,125,0], [0,129,2], [0,133,5], [0,137,7], [0,141,10], [0,145,13], [0,149,15], [0,153,18], [0,157,21], [0,161,23], [0,165,26], [0,170,29], [0,174,31], [0,178,34], [0,182,37], [0,186,39], [0,190,42], [0,194,45], [0,198,47], [0,202,50], [0,206,53], [0,210,55], [0,214,58], [0,218,61], [0,222,63], [0,226,66], [0,230,69], [0,234,71], [0,238,74], [0,242,77], [0,246,79], [0,250,82], [0,255,85], [3,251,87], [7,247,90], [11,243,92], [15,239,95], [19,235,98], [23,231,100], [27,227,103], [31,223,106], [35,219,108], [39,215,111], [43,211,114], [47,207,116], [51,203,119], [55,199,122], [59,195,124], [63,191,127], [67,187,130], [71,183,132], [75,179,135], [79,175,138], [83,171,140], [87,167,143], [91,163,146], [95,159,148], [99,155,151], [103,151,154], [107,147,156], [111,143,159], [115,139,162], [119,135,164], [123,131,167], [127,127,170], [131,123,172], [135,119,175], [139,115,177], [143,111,180], [147,107,183], [151,103,185], [155,99,188], [159,95,191], [163,91,193], [167,87,196], [171,83,199], [175,79,201], [179,75,204], [183,71,207], [187,67,209], [191,63,212], [195,59,215], [199,55,217], [203,51,220], [207,47,223], [211,43,225], [215,39,228], [219,35,231], [223,31,233], [227,27,236], [231,23,239], [235,19,241], [239,15,244], [243,11,247], [247,7,249], [251,3,252], [255,0,255], [255,0,251], [255,0,247], [255,0,244], [255,0,240], [255,0,237], [255,0,233], [255,0,230], [255,0,226], [255,0,223], [255,0,219], [255,0,216], [255,0,212], [255,0,208], [255,0,205], [255,0,201], [255,0,198], [255,0,194], [255,0,191], [255,0,187], [255,0,184], [255,0,180], [255,0,177], [255,0,173], [255,0,170], [255,0,166], [255,0,162], [255,0,159], [255,0,155], [255,0,152], [255,0,148], [255,0,145], [255,0,141], [255,0,138], [255,0,134], [255,0,131], [255,0,127], [255,0,123], [255,0,119], [255,0,115], [255,0,112], [255,0,108], [255,0,104], [255,0,100], [255,0,96], [255,0,92], [255,0,88], [255,0,85], [255,0,81], [255,0,77], [255,0,73], [255,0,69], [255,0,65], [255,0,61], [255,0,57], [255,0,54], [255,0,50], [255,0,46], [255,0,42], [255,0,38], [255,0,34], [255,0,30], [255,0,27], [255,0,23], [255,0,19], [255,0,15], [255,0,11], [255,0,7], [255,0,3], [255,0,0], [255,4,0], [255,8,0], [255,12,0], [255,17,0], [255,21,0], [255,25,0], [255,30,0], [255,34,0], [255,38,0], [255,43,0], [255,47,0], [255,51,0], [255,56,0], [255,60,0], [255,64,0], [255,69,0], [255,73,0], [255,77,0], [255,82,0], [255,86,0], [255,90,0], [255,95,0], [255,99,0], [255,103,0], [255,108,0], [255,112,0], [255,116,0], [255,121,0], [255,125,0], [255,129,0], [255,133,0], [255,138,0], [255,142,0], [255,146,0], [255,151,0], [255,155,0], [255,159,0], [255,164,0], [255,168,0], [255,172,0], [255,177,0], [255,181,0], [255,185,0], [255,190,0], [255,194,0], [255,198,0], [255,203,0], [255,207,0], [255,211,0], [255,216,0], [255,220,0], [255,224,0], [255,229,0], [255,233,0], [255,237,0], [255,242,0], [255,246,0], [255,250,0], [255,255,0]], 65 | bCm: [ [0,0,0], [0,0,4], [0,0,8], [0,0,12], [0,0,16], [0,0,20], [0,0,24], [0,0,28], [0,0,32], [0,0,36], [0,0,40], [0,0,44], [0,0,48], [0,0,52], [0,0,56], [0,0,60], [0,0,64], [0,0,68], [0,0,72], [0,0,76], [0,0,80], [0,0,85], [0,0,89], [0,0,93], [0,0,97], [0,0,101], [0,0,105], [0,0,109], [0,0,113], [0,0,117], [0,0,121], [0,0,125], [0,0,129], [0,0,133], [0,0,137], [0,0,141], [0,0,145], [0,0,149], [0,0,153], [0,0,157], [0,0,161], [0,0,165], [0,0,170], [0,0,174], [0,0,178], [0,0,182], [0,0,186], [0,0,190], [0,0,194], [0,0,198], [0,0,202], [0,0,206], [0,0,210], [0,0,214], [0,0,218], [0,0,222], [0,0,226], [0,0,230], [0,0,234], [0,0,238], [0,0,242], [0,0,246], [0,0,250], [0,0,255], [3,0,251], [7,0,247], [11,0,243], [15,0,239], [19,0,235], [23,0,231], [27,0,227], [31,0,223], [35,0,219], [39,0,215], [43,0,211], [47,0,207], [51,0,203], [55,0,199], [59,0,195], [63,0,191], [67,0,187], [71,0,183], [75,0,179], [79,0,175], [83,0,171], [87,0,167], [91,0,163], [95,0,159], [99,0,155], [103,0,151], [107,0,147], [111,0,143], [115,0,139], [119,0,135], [123,0,131], [127,0,127], [131,0,123], [135,0,119], [139,0,115], [143,0,111], [147,0,107], [151,0,103], [155,0,99], [159,0,95], [163,0,91], [167,0,87], [171,0,83], [175,0,79], [179,0,75], [183,0,71], [187,0,67], [191,0,63], [195,0,59], [199,0,55], [203,0,51], [207,0,47], [211,0,43], [215,0,39], [219,0,35], [223,0,31], [227,0,27], [231,0,23], [235,0,19], [239,0,15], [243,0,11], [247,0,7], [251,0,3], [255,0,0], [255,3,0], [255,7,0], [255,11,0], [255,15,0], [255,19,0], [255,23,0], [255,27,0], [255,31,0], [255,35,0], [255,39,0], [255,43,0], [255,47,0], [255,51,0], [255,55,0], [255,59,0], [255,63,0], [255,67,0], [255,71,0], [255,75,0], [255,79,0], [255,83,0], [255,87,0], [255,91,0], [255,95,0], [255,99,0], [255,103,0], [255,107,0], [255,111,0], [255,115,0], [255,119,0], [255,123,0], [255,127,0], [255,131,0], [255,135,0], [255,139,0], [255,143,0], [255,147,0], [255,151,0], [255,155,0], [255,159,0], [255,163,0], [255,167,0], [255,171,0], [255,175,0], [255,179,0], [255,183,0], [255,187,0], [255,191,0], [255,195,0], [255,199,0], [255,203,0], [255,207,0], [255,211,0], [255,215,0], [255,219,0], [255,223,0], [255,227,0], [255,231,0], [255,235,0], [255,239,0], [255,243,0], [255,247,0], [255,251,0], [255,255,0], [255,255,3], [255,255,7], [255,255,11], [255,255,15], [255,255,19], [255,255,23], [255,255,27], [255,255,31], [255,255,35], [255,255,39], [255,255,43], [255,255,47], [255,255,51], [255,255,55], [255,255,59], [255,255,63], [255,255,67], [255,255,71], [255,255,75], [255,255,79], [255,255,83], [255,255,87], [255,255,91], [255,255,95], [255,255,99], [255,255,103], [255,255,107], [255,255,111], [255,255,115], [255,255,119], [255,255,123], [255,255,127], [255,255,131], [255,255,135], [255,255,139], [255,255,143], [255,255,147], [255,255,151], [255,255,155], [255,255,159], [255,255,163], [255,255,167], [255,255,171], [255,255,175], [255,255,179], [255,255,183], [255,255,187], [255,255,191], [255,255,195], [255,255,199], [255,255,203], [255,255,207], [255,255,211], [255,255,215], [255,255,219], [255,255,223], [255,255,227], [255,255,231], [255,255,235], [255,255,239], [255,255,243], [255,255,247], [255,255,251], [255,255,255]], 66 | bbCm: [ [0,0,0], [2,0,0], [4,0,0], [6,0,0], [8,0,0], [10,0,0], [12,0,0], [14,0,0], [16,0,0], [18,0,0], [20,0,0], [22,0,0], [24,0,0], [26,0,0], [28,0,0], [30,0,0], [32,0,0], [34,0,0], [36,0,0], [38,0,0], [40,0,0], [42,0,0], [44,0,0], [46,0,0], [48,0,0], [50,0,0], [52,0,0], [54,0,0], [56,0,0], [58,0,0], [60,0,0], [62,0,0], [64,0,0], [66,0,0], [68,0,0], [70,0,0], [72,0,0], [74,0,0], [76,0,0], [78,0,0], [80,0,0], [82,0,0], [84,0,0], [86,0,0], [88,0,0], [90,0,0], [92,0,0], [94,0,0], [96,0,0], [98,0,0], [100,0,0], [102,0,0], [104,0,0], [106,0,0], [108,0,0], [110,0,0], [112,0,0], [114,0,0], [116,0,0], [118,0,0], [120,0,0], [122,0,0], [124,0,0], [126,0,0], [128,1,0], [130,3,0], [132,5,0], [134,7,0], [136,9,0], [138,11,0], [140,13,0], [142,15,0], [144,17,0], [146,19,0], [148,21,0], [150,23,0], [152,25,0], [154,27,0], [156,29,0], [158,31,0], [160,33,0], [162,35,0], [164,37,0], [166,39,0], [168,41,0], [170,43,0], [172,45,0], [174,47,0], [176,49,0], [178,51,0], [180,53,0], [182,55,0], [184,57,0], [186,59,0], [188,61,0], [190,63,0], [192,65,0], [194,67,0], [196,69,0], [198,71,0], [200,73,0], [202,75,0], [204,77,0], [206,79,0], [208,81,0], [210,83,0], [212,85,0], [214,87,0], [216,89,0], [218,91,0], [220,93,0], [222,95,0], [224,97,0], [226,99,0], [228,101,0], [230,103,0], [232,105,0], [234,107,0], [236,109,0], [238,111,0], [240,113,0], [242,115,0], [244,117,0], [246,119,0], [248,121,0], [250,123,0], [252,125,0], [255,127,0], [255,129,1], [255,131,3], [255,133,5], [255,135,7], [255,137,9], [255,139,11], [255,141,13], [255,143,15], [255,145,17], [255,147,19], [255,149,21], [255,151,23], [255,153,25], [255,155,27], [255,157,29], [255,159,31], [255,161,33], [255,163,35], [255,165,37], [255,167,39], [255,169,41], [255,171,43], [255,173,45], [255,175,47], [255,177,49], [255,179,51], [255,181,53], [255,183,55], [255,185,57], [255,187,59], [255,189,61], [255,191,63], [255,193,65], [255,195,67], [255,197,69], [255,199,71], [255,201,73], [255,203,75], [255,205,77], [255,207,79], [255,209,81], [255,211,83], [255,213,85], [255,215,87], [255,217,89], [255,219,91], [255,221,93], [255,223,95], [255,225,97], [255,227,99], [255,229,101], [255,231,103], [255,233,105], [255,235,107], [255,237,109], [255,239,111], [255,241,113], [255,243,115], [255,245,117], [255,247,119], [255,249,121], [255,251,123], [255,253,125], [255,255,127], [255,255,129], [255,255,131], [255,255,133], [255,255,135], [255,255,137], [255,255,139], [255,255,141], [255,255,143], [255,255,145], [255,255,147], [255,255,149], [255,255,151], [255,255,153], [255,255,155], [255,255,157], [255,255,159], [255,255,161], [255,255,163], [255,255,165], [255,255,167], [255,255,169], [255,255,171], [255,255,173], [255,255,175], [255,255,177], [255,255,179], [255,255,181], [255,255,183], [255,255,185], [255,255,187], [255,255,189], [255,255,191], [255,255,193], [255,255,195], [255,255,197], [255,255,199], [255,255,201], [255,255,203], [255,255,205], [255,255,207], [255,255,209], [255,255,211], [255,255,213], [255,255,215], [255,255,217], [255,255,219], [255,255,221], [255,255,223], [255,255,225], [255,255,227], [255,255,229], [255,255,231], [255,255,233], [255,255,235], [255,255,237], [255,255,239], [255,255,241], [255,255,243], [255,255,245], [255,255,247], [255,255,249], [255,255,251], [255,255,253], [255,255,255]], 67 | blueCm: [ [0,0,0], [0,0,1], [0,0,2], [0,0,3], [0,0,4], [0,0,5], [0,0,6], [0,0,7], [0,0,8], [0,0,9], [0,0,10], [0,0,11], [0,0,12], [0,0,13], [0,0,14], [0,0,15], [0,0,16], [0,0,17], [0,0,18], [0,0,19], [0,0,20], [0,0,21], [0,0,22], [0,0,23], [0,0,24], [0,0,25], [0,0,26], [0,0,27], [0,0,28], [0,0,29], [0,0,30], [0,0,31], [0,0,32], [0,0,33], [0,0,34], [0,0,35], [0,0,36], [0,0,37], [0,0,38], [0,0,39], [0,0,40], [0,0,41], [0,0,42], [0,0,43], [0,0,44], [0,0,45], [0,0,46], [0,0,47], [0,0,48], [0,0,49], [0,0,50], [0,0,51], [0,0,52], [0,0,53], [0,0,54], [0,0,55], [0,0,56], [0,0,57], [0,0,58], [0,0,59], [0,0,60], [0,0,61], [0,0,62], [0,0,63], [0,0,64], [0,0,65], [0,0,66], [0,0,67], [0,0,68], [0,0,69], [0,0,70], [0,0,71], [0,0,72], [0,0,73], [0,0,74], [0,0,75], [0,0,76], [0,0,77], [0,0,78], [0,0,79], [0,0,80], [0,0,81], [0,0,82], [0,0,83], [0,0,84], [0,0,85], [0,0,86], [0,0,87], [0,0,88], [0,0,89], [0,0,90], [0,0,91], [0,0,92], [0,0,93], [0,0,94], [0,0,95], [0,0,96], [0,0,97], [0,0,98], [0,0,99], [0,0,100], [0,0,101], [0,0,102], [0,0,103], [0,0,104], [0,0,105], [0,0,106], [0,0,107], [0,0,108], [0,0,109], [0,0,110], [0,0,111], [0,0,112], [0,0,113], [0,0,114], [0,0,115], [0,0,116], [0,0,117], [0,0,118], [0,0,119], [0,0,120], [0,0,121], [0,0,122], [0,0,123], [0,0,124], [0,0,125], [0,0,126], [0,0,127], [0,0,128], [0,0,129], [0,0,130], [0,0,131], [0,0,132], [0,0,133], [0,0,134], [0,0,135], [0,0,136], [0,0,137], [0,0,138], [0,0,139], [0,0,140], [0,0,141], [0,0,142], [0,0,143], [0,0,144], [0,0,145], [0,0,146], [0,0,147], [0,0,148], [0,0,149], [0,0,150], [0,0,151], [0,0,152], [0,0,153], [0,0,154], [0,0,155], [0,0,156], [0,0,157], [0,0,158], [0,0,159], [0,0,160], [0,0,161], [0,0,162], [0,0,163], [0,0,164], [0,0,165], [0,0,166], [0,0,167], [0,0,168], [0,0,169], [0,0,170], [0,0,171], [0,0,172], [0,0,173], [0,0,174], [0,0,175], [0,0,176], [0,0,177], [0,0,178], [0,0,179], [0,0,180], [0,0,181], [0,0,182], [0,0,183], [0,0,184], [0,0,185], [0,0,186], [0,0,187], [0,0,188], [0,0,189], [0,0,190], [0,0,191], [0,0,192], [0,0,193], [0,0,194], [0,0,195], [0,0,196], [0,0,197], [0,0,198], [0,0,199], [0,0,200], [0,0,201], [0,0,202], [0,0,203], [0,0,204], [0,0,205], [0,0,206], [0,0,207], [0,0,208], [0,0,209], [0,0,210], [0,0,211], [0,0,212], [0,0,213], [0,0,214], [0,0,215], [0,0,216], [0,0,217], [0,0,218], [0,0,219], [0,0,220], [0,0,221], [0,0,222], [0,0,223], [0,0,224], [0,0,225], [0,0,226], [0,0,227], [0,0,228], [0,0,229], [0,0,230], [0,0,231], [0,0,232], [0,0,233], [0,0,234], [0,0,235], [0,0,236], [0,0,237], [0,0,238], [0,0,239], [0,0,240], [0,0,241], [0,0,242], [0,0,243], [0,0,244], [0,0,245], [0,0,246], [0,0,247], [0,0,248], [0,0,249], [0,0,250], [0,0,251], [0,0,252], [0,0,253], [0,0,254], [0,0,255]], 68 | coolCm: [ [0,0,0], [0,0,1], [0,0,3], [0,0,5], [0,0,7], [0,0,9], [0,0,11], [0,0,13], [0,0,15], [0,0,17], [0,0,18], [0,0,20], [0,0,22], [0,0,24], [0,0,26], [0,0,28], [0,0,30], [0,0,32], [0,0,34], [0,0,35], [0,0,37], [0,0,39], [0,0,41], [0,0,43], [0,0,45], [0,0,47], [0,0,49], [0,0,51], [0,0,52], [0,0,54], [0,0,56], [0,0,58], [0,0,60], [0,0,62], [0,0,64], [0,0,66], [0,0,68], [0,0,69], [0,0,71], [0,0,73], [0,0,75], [0,0,77], [0,0,79], [0,0,81], [0,0,83], [0,0,85], [0,0,86], [0,0,88], [0,0,90], [0,0,92], [0,0,94], [0,0,96], [0,0,98], [0,0,100], [0,0,102], [0,0,103], [0,0,105], [0,1,107], [0,2,109], [0,4,111], [0,5,113], [0,6,115], [0,8,117], [0,9,119], [0,10,120], [0,12,122], [0,13,124], [0,14,126], [0,16,128], [0,17,130], [0,18,132], [0,20,134], [0,21,136], [0,23,137], [0,24,139], [0,25,141], [0,27,143], [0,28,145], [1,29,147], [1,31,149], [1,32,151], [1,33,153], [1,35,154], [2,36,156], [2,37,158], [2,39,160], [2,40,162], [2,42,164], [3,43,166], [3,44,168], [3,46,170], [3,47,171], [4,48,173], [4,50,175], [4,51,177], [4,52,179], [4,54,181], [5,55,183], [5,56,185], [5,58,187], [5,59,188], [5,61,190], [6,62,192], [6,63,194], [6,65,196], [6,66,198], [7,67,200], [7,69,202], [7,70,204], [7,71,205], [7,73,207], [8,74,209], [8,75,211], [8,77,213], [8,78,215], [8,80,217], [9,81,219], [9,82,221], [9,84,222], [9,85,224], [9,86,226], [10,88,228], [10,89,230], [10,90,232], [10,92,234], [11,93,236], [11,94,238], [11,96,239], [11,97,241], [11,99,243], [12,100,245], [12,101,247], [12,103,249], [12,104,251], [12,105,253], [13,107,255], [13,108,255], [13,109,255], [13,111,255], [14,112,255], [14,113,255], [14,115,255], [14,116,255], [14,118,255], [15,119,255], [15,120,255], [15,122,255], [15,123,255], [15,124,255], [16,126,255], [16,127,255], [16,128,255], [16,130,255], [17,131,255], [17,132,255], [17,134,255], [17,135,255], [17,136,255], [18,138,255], [18,139,255], [18,141,255], [18,142,255], [18,143,255], [19,145,255], [19,146,255], [19,147,255], [19,149,255], [19,150,255], [20,151,255], [20,153,255], [20,154,255], [20,155,255], [21,157,255], [21,158,255], [21,160,255], [21,161,255], [21,162,255], [22,164,255], [22,165,255], [22,166,255], [22,168,255], [22,169,255], [23,170,255], [23,172,255], [23,173,255], [23,174,255], [24,176,255], [24,177,255], [24,179,255], [24,180,255], [24,181,255], [25,183,255], [25,184,255], [25,185,255], [29,187,255], [32,188,255], [36,189,255], [40,191,255], [44,192,255], [47,193,255], [51,195,255], [55,196,255], [58,198,255], [62,199,255], [66,200,255], [69,202,255], [73,203,255], [77,204,255], [81,206,255], [84,207,255], [88,208,255], [92,210,255], [95,211,255], [99,212,255], [103,214,255], [106,215,255], [110,217,255], [114,218,255], [118,219,255], [121,221,255], [125,222,255], [129,223,255], [132,225,255], [136,226,255], [140,227,255], [143,229,255], [147,230,255], [151,231,255], [155,233,255], [158,234,255], [162,236,255], [166,237,255], [169,238,255], [173,240,255], [177,241,255], [180,242,255], [184,244,255], [188,245,255], [192,246,255], [195,248,255], [199,249,255], [203,250,255], [206,252,255], [210,253,255], [214,255,255], [217,255,255], [221,255,255], [225,255,255], [229,255,255], [232,255,255], [236,255,255], [240,255,255], [243,255,255], [247,255,255], [251,255,255], [255,255,255]], 69 | cubehelix0Cm: [ [0,0,0], [2,1,2], [5,2,5], [5,2,5], [6,2,6], [7,2,7], [10,3,10], [12,5,12], [13,5,14], [14,5,16], [15,5,17], [16,6,20], [17,7,22], [18,8,24], [19,9,26], [20,10,28], [21,11,30], [22,12,33], [22,13,34], [22,14,36], [22,15,38], [24,16,40], [25,17,43], [25,18,45], [25,19,46], [25,20,48], [25,22,50], [25,23,51], [25,25,53], [25,26,54], [25,28,56], [25,28,57], [25,29,59], [25,30,61], [25,33,62], [25,35,63], [25,36,65], [25,37,67], [25,38,68], [25,40,70], [25,43,71], [24,45,72], [23,46,73], [22,48,73], [22,49,75], [22,51,76], [22,52,76], [22,54,76], [22,56,76], [22,57,77], [22,59,78], [22,61,79], [21,63,79], [20,66,79], [20,67,79], [20,68,79], [20,68,79], [20,71,79], [20,73,79], [20,75,78], [20,77,77], [20,79,76], [20,80,76], [20,81,76], [21,83,75], [22,85,74], [22,86,73], [22,89,72], [22,91,71], [23,92,71], [24,93,71], [25,94,71], [26,96,70], [28,99,68], [28,100,68], [29,101,67], [30,102,66], [31,102,65], [32,103,64], [33,104,63], [35,105,62], [38,107,61], [39,107,60], [39,108,59], [40,109,58], [43,110,57], [45,112,56], [47,113,55], [49,113,54], [51,114,53], [54,116,52], [58,117,51], [60,117,50], [62,117,49], [63,117,48], [66,118,48], [68,119,48], [71,119,48], [73,119,48], [76,119,48], [79,120,47], [81,121,46], [84,122,45], [87,122,45], [91,122,45], [94,122,46], [96,122,47], [99,122,48], [103,122,48], [107,122,48], [109,122,49], [112,122,50], [114,122,51], [118,122,52], [122,122,53], [124,122,54], [127,122,55], [130,122,56], [133,122,57], [137,122,58], [140,122,60], [142,122,62], [145,122,63], [149,122,66], [153,122,68], [155,121,70], [158,120,72], [160,119,73], [162,119,75], [164,119,77], [165,119,79], [169,119,81], [173,119,84], [175,119,86], [176,119,89], [178,119,91], [181,119,95], [183,119,99], [186,120,102], [188,121,104], [191,122,107], [192,122,110], [193,122,114], [195,122,117], [197,122,119], [198,122,122], [200,122,126], [201,122,130], [202,123,132], [203,124,135], [204,124,137], [204,125,141], [205,126,144], [206,127,147], [207,127,151], [209,127,155], [209,128,158], [210,129,160], [211,130,163], [211,131,167], [211,132,170], [211,133,173], [211,134,175], [211,135,178], [211,136,182], [211,137,186], [211,138,188], [211,139,191], [211,140,193], [211,142,196], [211,145,198], [210,146,201], [209,147,204], [209,147,206], [209,150,209], [209,153,211], [208,153,213], [207,154,215], [206,155,216], [206,157,218], [206,158,220], [206,160,221], [205,163,224], [204,165,226], [203,167,228], [202,169,230], [201,170,232], [201,172,233], [201,173,234], [200,175,235], [199,176,236], [198,178,237], [197,181,238], [196,183,239], [196,185,239], [196,187,239], [196,188,239], [195,191,240], [193,193,242], [193,194,242], [193,195,242], [193,196,242], [193,198,242], [193,199,242], [193,201,242], [193,204,242], [193,206,242], [193,208,242], [193,209,242], [193,211,242], [193,212,242], [193,214,242], [194,215,242], [195,217,242], [196,219,242], [196,220,242], [196,221,242], [197,223,241], [198,225,240], [198,226,239], [200,228,239], [201,229,239], [202,230,239], [203,231,239], [204,232,239], [205,233,239], [206,234,239], [207,235,239], [208,236,239], [209,237,239], [210,238,239], [212,238,239], [214,239,239], [215,240,239], [216,242,239], [218,243,239], [220,243,239], [221,244,239], [224,246,239], [226,247,239], [228,247,240], [230,247,241], [232,247,242], [234,248,242], [237,249,242], [238,249,243], [240,249,243], [242,249,244], [243,251,246], [244,252,247], [246,252,249], [248,252,250], [249,252,252], [251,253,253], [253,254,254], [255,255,255]], 70 | cubehelix1Cm: [ [0,0,0], [2,0,2], [5,0,5], [6,0,7], [8,0,10], [10,0,12], [12,1,15], [15,2,17], [17,2,20], [18,2,22], [20,2,25], [21,2,29], [22,2,33], [23,3,35], [24,4,38], [25,5,40], [25,6,44], [25,7,48], [26,8,51], [27,9,53], [28,10,56], [28,11,59], [28,12,63], [27,14,66], [26,16,68], [25,17,71], [25,18,73], [25,19,74], [25,20,76], [24,22,80], [22,25,84], [22,27,85], [21,28,87], [20,30,89], [19,33,91], [17,35,94], [16,37,95], [14,39,96], [12,40,96], [11,43,99], [10,45,102], [8,47,102], [6,49,103], [5,51,104], [2,54,104], [0,58,104], [0,60,104], [0,62,104], [0,63,104], [0,66,104], [0,68,104], [0,71,104], [0,73,104], [0,76,104], [0,79,103], [0,81,102], [0,84,102], [0,86,99], [0,89,96], [0,91,96], [0,94,95], [0,96,94], [0,99,91], [0,102,89], [0,103,86], [0,105,84], [0,107,81], [0,109,79], [0,112,76], [0,113,73], [0,115,71], [0,117,68], [0,119,65], [0,122,61], [0,124,58], [0,125,56], [0,127,53], [0,128,51], [0,129,48], [0,130,45], [0,132,42], [0,135,38], [0,136,35], [0,136,33], [0,137,30], [3,138,26], [7,140,22], [10,140,21], [12,140,19], [15,140,17], [19,141,14], [22,142,10], [26,142,8], [29,142,6], [33,142,5], [38,142,2], [43,142,0], [46,142,0], [50,142,0], [53,142,0], [57,141,0], [62,141,0], [66,140,0], [72,140,0], [79,140,0], [83,139,0], [87,138,0], [91,137,0], [98,136,0], [104,135,0], [108,134,0], [113,133,0], [117,132,0], [123,131,0], [130,130,0], [134,129,0], [138,128,0], [142,127,0], [149,126,0], [155,124,0], [159,123,0], [164,121,1], [168,119,2], [174,118,6], [181,117,10], [185,116,12], [189,115,15], [193,114,17], [197,113,21], [200,113,24], [204,112,28], [209,110,33], [214,109,38], [217,108,41], [221,107,45], [224,107,48], [228,105,54], [232,104,61], [234,103,64], [237,102,68], [239,102,71], [243,102,77], [247,102,84], [249,101,89], [250,100,94], [252,99,99], [253,99,105], [255,99,112], [255,99,117], [255,99,122], [255,99,127], [255,99,131], [255,99,136], [255,99,140], [255,100,147], [255,102,155], [255,102,159], [255,102,164], [255,102,168], [255,103,174], [255,104,181], [255,105,185], [255,106,189], [255,107,193], [255,108,200], [255,109,206], [255,111,210], [255,113,215], [255,114,219], [253,117,224], [252,119,229], [250,120,232], [249,121,236], [247,122,239], [244,124,244], [242,127,249], [240,130,251], [238,132,253], [237,135,255], [234,136,255], [232,138,255], [229,140,255], [226,142,255], [224,145,255], [222,147,255], [221,150,255], [219,153,255], [215,156,255], [211,160,255], [209,162,255], [208,164,255], [206,165,255], [204,169,255], [201,173,255], [199,175,255], [198,178,255], [196,181,255], [193,183,255], [191,186,255], [189,188,255], [187,191,255], [186,193,255], [185,195,255], [184,197,255], [183,198,255], [182,202,255], [181,206,255], [180,208,255], [179,209,255], [178,211,255], [177,214,255], [175,216,255], [175,218,255], [175,220,255], [175,221,255], [175,224,255], [175,226,255], [176,228,255], [177,230,255], [178,232,255], [179,234,255], [181,237,255], [181,238,255], [182,238,255], [183,239,255], [184,240,252], [186,242,249], [187,243,249], [189,243,248], [191,244,247], [192,245,246], [194,246,245], [196,247,244], [198,248,243], [201,249,242], [203,250,242], [204,251,242], [206,252,242], [210,252,240], [214,252,239], [216,252,240], [219,252,241], [221,252,242], [224,253,242], [226,255,242], [229,255,243], [232,255,243], [234,255,244], [238,255,246], [242,255,247], [243,255,248], [245,255,249], [247,255,249], [249,255,251], [252,255,253], [255,255,255]], 71 | greenCm: [ [0,0,0], [0,1,0], [0,2,0], [0,3,0], [0,4,0], [0,5,0], [0,6,0], [0,7,0], [0,8,0], [0,9,0], [0,10,0], [0,11,0], [0,12,0], [0,13,0], [0,14,0], [0,15,0], [0,16,0], [0,17,0], [0,18,0], [0,19,0], [0,20,0], [0,21,0], [0,22,0], [0,23,0], [0,24,0], [0,25,0], [0,26,0], [0,27,0], [0,28,0], [0,29,0], [0,30,0], [0,31,0], [0,32,0], [0,33,0], [0,34,0], [0,35,0], [0,36,0], [0,37,0], [0,38,0], [0,39,0], [0,40,0], [0,41,0], [0,42,0], [0,43,0], [0,44,0], [0,45,0], [0,46,0], [0,47,0], [0,48,0], [0,49,0], [0,50,0], [0,51,0], [0,52,0], [0,53,0], [0,54,0], [0,55,0], [0,56,0], [0,57,0], [0,58,0], [0,59,0], [0,60,0], [0,61,0], [0,62,0], [0,63,0], [0,64,0], [0,65,0], [0,66,0], [0,67,0], [0,68,0], [0,69,0], [0,70,0], [0,71,0], [0,72,0], [0,73,0], [0,74,0], [0,75,0], [0,76,0], [0,77,0], [0,78,0], [0,79,0], [0,80,0], [0,81,0], [0,82,0], [0,83,0], [0,84,0], [0,85,0], [0,86,0], [0,87,0], [0,88,0], [0,89,0], [0,90,0], [0,91,0], [0,92,0], [0,93,0], [0,94,0], [0,95,0], [0,96,0], [0,97,0], [0,98,0], [0,99,0], [0,100,0], [0,101,0], [0,102,0], [0,103,0], [0,104,0], [0,105,0], [0,106,0], [0,107,0], [0,108,0], [0,109,0], [0,110,0], [0,111,0], [0,112,0], [0,113,0], [0,114,0], [0,115,0], [0,116,0], [0,117,0], [0,118,0], [0,119,0], [0,120,0], [0,121,0], [0,122,0], [0,123,0], [0,124,0], [0,125,0], [0,126,0], [0,127,0], [0,128,0], [0,129,0], [0,130,0], [0,131,0], [0,132,0], [0,133,0], [0,134,0], [0,135,0], [0,136,0], [0,137,0], [0,138,0], [0,139,0], [0,140,0], [0,141,0], [0,142,0], [0,143,0], [0,144,0], [0,145,0], [0,146,0], [0,147,0], [0,148,0], [0,149,0], [0,150,0], [0,151,0], [0,152,0], [0,153,0], [0,154,0], [0,155,0], [0,156,0], [0,157,0], [0,158,0], [0,159,0], [0,160,0], [0,161,0], [0,162,0], [0,163,0], [0,164,0], [0,165,0], [0,166,0], [0,167,0], [0,168,0], [0,169,0], [0,170,0], [0,171,0], [0,172,0], [0,173,0], [0,174,0], [0,175,0], [0,176,0], [0,177,0], [0,178,0], [0,179,0], [0,180,0], [0,181,0], [0,182,0], [0,183,0], [0,184,0], [0,185,0], [0,186,0], [0,187,0], [0,188,0], [0,189,0], [0,190,0], [0,191,0], [0,192,0], [0,193,0], [0,194,0], [0,195,0], [0,196,0], [0,197,0], [0,198,0], [0,199,0], [0,200,0], [0,201,0], [0,202,0], [0,203,0], [0,204,0], [0,205,0], [0,206,0], [0,207,0], [0,208,0], [0,209,0], [0,210,0], [0,211,0], [0,212,0], [0,213,0], [0,214,0], [0,215,0], [0,216,0], [0,217,0], [0,218,0], [0,219,0], [0,220,0], [0,221,0], [0,222,0], [0,223,0], [0,224,0], [0,225,0], [0,226,0], [0,227,0], [0,228,0], [0,229,0], [0,230,0], [0,231,0], [0,232,0], [0,233,0], [0,234,0], [0,235,0], [0,236,0], [0,237,0], [0,238,0], [0,239,0], [0,240,0], [0,241,0], [0,242,0], [0,243,0], [0,244,0], [0,245,0], [0,246,0], [0,247,0], [0,248,0], [0,249,0], [0,250,0], [0,251,0], [0,252,0], [0,253,0], [0,254,0], [0,255,0]], 72 | greyCm: [ [0,0,0], [1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5], [6,6,6], [7,7,7], [8,8,8], [9,9,9], [10,10,10], [11,11,11], [12,12,12], [13,13,13], [14,14,14], [15,15,15], [16,16,16], [17,17,17], [18,18,18], [19,19,19], [20,20,20], [21,21,21], [22,22,22], [23,23,23], [24,24,24], [25,25,25], [26,26,26], [27,27,27], [28,28,28], [29,29,29], [30,30,30], [31,31,31], [32,32,32], [33,33,33], [34,34,34], [35,35,35], [36,36,36], [37,37,37], [38,38,38], [39,39,39], [40,40,40], [41,41,41], [42,42,42], [43,43,43], [44,44,44], [45,45,45], [46,46,46], [47,47,47], [48,48,48], [49,49,49], [50,50,50], [51,51,51], [52,52,52], [53,53,53], [54,54,54], [55,55,55], [56,56,56], [57,57,57], [58,58,58], [59,59,59], [60,60,60], [61,61,61], [62,62,62], [63,63,63], [64,64,64], [65,65,65], [66,66,66], [67,67,67], [68,68,68], [69,69,69], [70,70,70], [71,71,71], [72,72,72], [73,73,73], [74,74,74], [75,75,75], [76,76,76], [77,77,77], [78,78,78], [79,79,79], [80,80,80], [81,81,81], [82,82,82], [83,83,83], [84,84,84], [85,85,85], [86,86,86], [87,87,87], [88,88,88], [89,89,89], [90,90,90], [91,91,91], [92,92,92], [93,93,93], [94,94,94], [95,95,95], [96,96,96], [97,97,97], [98,98,98], [99,99,99], [100,100,100], [101,101,101], [102,102,102], [103,103,103], [104,104,104], [105,105,105], [106,106,106], [107,107,107], [108,108,108], [109,109,109], [110,110,110], [111,111,111], [112,112,112], [113,113,113], [114,114,114], [115,115,115], [116,116,116], [117,117,117], [118,118,118], [119,119,119], [120,120,120], [121,121,121], [122,122,122], [123,123,123], [124,124,124], [125,125,125], [126,126,126], [127,127,127], [128,128,128], [129,129,129], [130,130,130], [131,131,131], [132,132,132], [133,133,133], [134,134,134], [135,135,135], [136,136,136], [137,137,137], [138,138,138], [139,139,139], [140,140,140], [141,141,141], [142,142,142], [143,143,143], [144,144,144], [145,145,145], [146,146,146], [147,147,147], [148,148,148], [149,149,149], [150,150,150], [151,151,151], [152,152,152], [153,153,153], [154,154,154], [155,155,155], [156,156,156], [157,157,157], [158,158,158], [159,159,159], [160,160,160], [161,161,161], [162,162,162], [163,163,163], [164,164,164], [165,165,165], [166,166,166], [167,167,167], [168,168,168], [169,169,169], [170,170,170], [171,171,171], [172,172,172], [173,173,173], [174,174,174], [175,175,175], [176,176,176], [177,177,177], [178,178,178], [179,179,179], [180,180,180], [181,181,181], [182,182,182], [183,183,183], [184,184,184], [185,185,185], [186,186,186], [187,187,187], [188,188,188], [189,189,189], [190,190,190], [191,191,191], [192,192,192], [193,193,193], [194,194,194], [195,195,195], [196,196,196], [197,197,197], [198,198,198], [199,199,199], [200,200,200], [201,201,201], [202,202,202], [203,203,203], [204,204,204], [205,205,205], [206,206,206], [207,207,207], [208,208,208], [209,209,209], [210,210,210], [211,211,211], [212,212,212], [213,213,213], [214,214,214], [215,215,215], [216,216,216], [217,217,217], [218,218,218], [219,219,219], [220,220,220], [221,221,221], [222,222,222], [223,223,223], [224,224,224], [225,225,225], [226,226,226], [227,227,227], [228,228,228], [229,229,229], [230,230,230], [231,231,231], [232,232,232], [233,233,233], [234,234,234], [235,235,235], [236,236,236], [237,237,237], [238,238,238], [239,239,239], [240,240,240], [241,241,241], [242,242,242], [243,243,243], [244,244,244], [245,245,245], [246,246,246], [247,247,247], [248,248,248], [249,249,249], [250,250,250], [251,251,251], [252,252,252], [253,253,253], [254,254,254], [255,255,255]], 73 | heCm: [ [0,0,0], [42,0,10], [85,0,21], [127,0,31], [127,0,47], [127,0,63], [127,0,79], [127,0,95], [127,0,102], [127,0,109], [127,0,116], [127,0,123], [127,0,131], [127,0,138], [127,0,145], [127,0,152], [127,0,159], [127,8,157], [127,17,155], [127,25,153], [127,34,151], [127,42,149], [127,51,147], [127,59,145], [127,68,143], [127,76,141], [127,85,139], [127,93,136], [127,102,134], [127,110,132], [127,119,130], [127,127,128], [127,129,126], [127,131,124], [127,133,122], [127,135,120], [127,137,118], [127,139,116], [127,141,114], [127,143,112], [127,145,110], [127,147,108], [127,149,106], [127,151,104], [127,153,102], [127,155,100], [127,157,98], [127,159,96], [127,161,94], [127,163,92], [127,165,90], [127,167,88], [127,169,86], [127,171,84], [127,173,82], [127,175,80], [127,177,77], [127,179,75], [127,181,73], [127,183,71], [127,185,69], [127,187,67], [127,189,65], [127,191,63], [128,191,64], [129,191,65], [130,191,66], [131,192,67], [132,192,68], [133,192,69], [134,192,70], [135,193,71], [136,193,72], [137,193,73], [138,193,74], [139,194,75], [140,194,76], [141,194,77], [142,194,78], [143,195,79], [144,195,80], [145,195,81], [146,195,82], [147,196,83], [148,196,84], [149,196,85], [150,196,86], [151,196,87], [152,197,88], [153,197,89], [154,197,90], [155,197,91], [156,198,92], [157,198,93], [158,198,94], [159,198,95], [160,199,96], [161,199,97], [162,199,98], [163,199,99], [164,200,100], [165,200,101], [166,200,102], [167,200,103], [168,201,104], [169,201,105], [170,201,106], [171,201,107], [172,202,108], [173,202,109], [174,202,110], [175,202,111], [176,202,112], [177,203,113], [178,203,114], [179,203,115], [180,203,116], [181,204,117], [182,204,118], [183,204,119], [184,204,120], [185,205,121], [186,205,122], [187,205,123], [188,205,124], [189,206,125], [190,206,126], [191,206,127], [191,206,128], [192,207,129], [192,207,130], [193,208,131], [193,208,132], [194,208,133], [194,209,134], [195,209,135], [195,209,136], [196,210,137], [196,210,138], [197,211,139], [197,211,140], [198,211,141], [198,212,142], [199,212,143], [199,212,144], [200,213,145], [200,213,146], [201,214,147], [201,214,148], [202,214,149], [202,215,150], [203,215,151], [203,216,152], [204,216,153], [204,216,154], [205,217,155], [205,217,156], [206,217,157], [206,218,158], [207,218,159], [207,219,160], [208,219,161], [208,219,162], [209,220,163], [209,220,164], [210,220,165], [210,221,166], [211,221,167], [211,222,168], [212,222,169], [212,222,170], [213,223,171], [213,223,172], [214,223,173], [214,224,174], [215,224,175], [215,225,176], [216,225,177], [216,225,178], [217,226,179], [217,226,180], [218,226,181], [218,227,182], [219,227,183], [219,228,184], [220,228,185], [220,228,186], [221,229,187], [221,229,188], [222,230,189], [222,230,190], [223,230,191], [223,231,192], [224,231,193], [224,231,194], [225,232,195], [225,232,196], [226,233,197], [226,233,198], [227,233,199], [227,234,200], [228,234,201], [228,234,202], [229,235,203], [229,235,204], [230,236,205], [230,236,206], [231,236,207], [231,237,208], [232,237,209], [232,237,210], [233,238,211], [233,238,212], [234,239,213], [234,239,214], [235,239,215], [235,240,216], [236,240,217], [236,240,218], [237,241,219], [237,241,220], [238,242,221], [238,242,222], [239,242,223], [239,243,224], [240,243,225], [240,244,226], [241,244,227], [241,244,228], [242,245,229], [242,245,230], [243,245,231], [243,246,232], [244,246,233], [244,247,234], [245,247,235], [245,247,236], [246,248,237], [246,248,238], [247,248,239], [247,249,240], [248,249,241], [248,250,242], [249,250,243], [249,250,244], [250,251,245], [250,251,246], [251,251,247], [251,252,248], [252,252,249], [252,253,250], [253,253,251], [253,253,252], [254,254,253], [254,254,254], [255,255,255]], 74 | heatCm: [ [0,0,0], [2,1,0], [5,2,0], [8,3,0], [11,4,0], [14,5,0], [17,6,0], [20,7,0], [23,8,0], [26,9,0], [29,10,0], [32,11,0], [35,12,0], [38,13,0], [41,14,0], [44,15,0], [47,16,0], [50,17,0], [53,18,0], [56,19,0], [59,20,0], [62,21,0], [65,22,0], [68,23,0], [71,24,0], [74,25,0], [77,26,0], [80,27,0], [83,28,0], [85,29,0], [88,30,0], [91,31,0], [94,32,0], [97,33,0], [100,34,0], [103,35,0], [106,36,0], [109,37,0], [112,38,0], [115,39,0], [118,40,0], [121,41,0], [124,42,0], [127,43,0], [130,44,0], [133,45,0], [136,46,0], [139,47,0], [142,48,0], [145,49,0], [148,50,0], [151,51,0], [154,52,0], [157,53,0], [160,54,0], [163,55,0], [166,56,0], [169,57,0], [171,58,0], [174,59,0], [177,60,0], [180,61,0], [183,62,0], [186,63,0], [189,64,0], [192,65,0], [195,66,0], [198,67,0], [201,68,0], [204,69,0], [207,70,0], [210,71,0], [213,72,0], [216,73,0], [219,74,0], [222,75,0], [225,76,0], [228,77,0], [231,78,0], [234,79,0], [237,80,0], [240,81,0], [243,82,0], [246,83,0], [249,84,0], [252,85,0], [255,86,0], [255,87,0], [255,88,0], [255,89,0], [255,90,0], [255,91,0], [255,92,0], [255,93,0], [255,94,0], [255,95,0], [255,96,0], [255,97,0], [255,98,0], [255,99,0], [255,100,0], [255,101,0], [255,102,0], [255,103,0], [255,104,0], [255,105,0], [255,106,0], [255,107,0], [255,108,0], [255,109,0], [255,110,0], [255,111,0], [255,112,0], [255,113,0], [255,114,0], [255,115,0], [255,116,0], [255,117,0], [255,118,0], [255,119,0], [255,120,0], [255,121,0], [255,122,0], [255,123,0], [255,124,0], [255,125,0], [255,126,0], [255,127,0], [255,128,0], [255,129,0], [255,130,0], [255,131,0], [255,132,0], [255,133,0], [255,134,0], [255,135,0], [255,136,0], [255,137,0], [255,138,0], [255,139,0], [255,140,0], [255,141,0], [255,142,0], [255,143,0], [255,144,0], [255,145,0], [255,146,0], [255,147,0], [255,148,0], [255,149,0], [255,150,0], [255,151,0], [255,152,0], [255,153,0], [255,154,0], [255,155,0], [255,156,0], [255,157,0], [255,158,0], [255,159,0], [255,160,0], [255,161,0], [255,162,0], [255,163,0], [255,164,0], [255,165,0], [255,166,3], [255,167,6], [255,168,9], [255,169,12], [255,170,15], [255,171,18], [255,172,21], [255,173,24], [255,174,27], [255,175,30], [255,176,33], [255,177,36], [255,178,39], [255,179,42], [255,180,45], [255,181,48], [255,182,51], [255,183,54], [255,184,57], [255,185,60], [255,186,63], [255,187,66], [255,188,69], [255,189,72], [255,190,75], [255,191,78], [255,192,81], [255,193,85], [255,194,88], [255,195,91], [255,196,94], [255,197,97], [255,198,100], [255,199,103], [255,200,106], [255,201,109], [255,202,112], [255,203,115], [255,204,118], [255,205,121], [255,206,124], [255,207,127], [255,208,130], [255,209,133], [255,210,136], [255,211,139], [255,212,142], [255,213,145], [255,214,148], [255,215,151], [255,216,154], [255,217,157], [255,218,160], [255,219,163], [255,220,166], [255,221,170], [255,222,173], [255,223,176], [255,224,179], [255,225,182], [255,226,185], [255,227,188], [255,228,191], [255,229,194], [255,230,197], [255,231,200], [255,232,203], [255,233,206], [255,234,209], [255,235,212], [255,236,215], [255,237,218], [255,238,221], [255,239,224], [255,240,227], [255,241,230], [255,242,233], [255,243,236], [255,244,239], [255,245,242], [255,246,245], [255,247,248], [255,248,251], [255,249,255], [255,250,255], [255,251,255], [255,252,255], [255,253,255], [255,254,255], [255,255,255]], 75 | rainbowCm: [ [255,0,255], [250,0,255], [245,0,255], [240,0,255], [235,0,255], [230,0,255], [225,0,255], [220,0,255], [215,0,255], [210,0,255], [205,0,255], [200,0,255], [195,0,255], [190,0,255], [185,0,255], [180,0,255], [175,0,255], [170,0,255], [165,0,255], [160,0,255], [155,0,255], [150,0,255], [145,0,255], [140,0,255], [135,0,255], [130,0,255], [125,0,255], [120,0,255], [115,0,255], [110,0,255], [105,0,255], [100,0,255], [95,0,255], [90,0,255], [85,0,255], [80,0,255], [75,0,255], [70,0,255], [65,0,255], [60,0,255], [55,0,255], [50,0,255], [45,0,255], [40,0,255], [35,0,255], [30,0,255], [25,0,255], [20,0,255], [15,0,255], [10,0,255], [5,0,255], [0,0,255], [0,5,255], [0,10,255], [0,15,255], [0,20,255], [0,25,255], [0,30,255], [0,35,255], [0,40,255], [0,45,255], [0,50,255], [0,55,255], [0,60,255], [0,65,255], [0,70,255], [0,75,255], [0,80,255], [0,85,255], [0,90,255], [0,95,255], [0,100,255], [0,105,255], [0,110,255], [0,115,255], [0,120,255], [0,125,255], [0,130,255], [0,135,255], [0,140,255], [0,145,255], [0,150,255], [0,155,255], [0,160,255], [0,165,255], [0,170,255], [0,175,255], [0,180,255], [0,185,255], [0,190,255], [0,195,255], [0,200,255], [0,205,255], [0,210,255], [0,215,255], [0,220,255], [0,225,255], [0,230,255], [0,235,255], [0,240,255], [0,245,255], [0,250,255], [0,255,255], [0,255,250], [0,255,245], [0,255,240], [0,255,235], [0,255,230], [0,255,225], [0,255,220], [0,255,215], [0,255,210], [0,255,205], [0,255,200], [0,255,195], [0,255,190], [0,255,185], [0,255,180], [0,255,175], [0,255,170], [0,255,165], [0,255,160], [0,255,155], [0,255,150], [0,255,145], [0,255,140], [0,255,135], [0,255,130], [0,255,125], [0,255,120], [0,255,115], [0,255,110], [0,255,105], [0,255,100], [0,255,95], [0,255,90], [0,255,85], [0,255,80], [0,255,75], [0,255,70], [0,255,65], [0,255,60], [0,255,55], [0,255,50], [0,255,45], [0,255,40], [0,255,35], [0,255,30], [0,255,25], [0,255,20], [0,255,15], [0,255,10], [0,255,5], [0,255,0], [5,255,0], [10,255,0], [15,255,0], [20,255,0], [25,255,0], [30,255,0], [35,255,0], [40,255,0], [45,255,0], [50,255,0], [55,255,0], [60,255,0], [65,255,0], [70,255,0], [75,255,0], [80,255,0], [85,255,0], [90,255,0], [95,255,0], [100,255,0], [105,255,0], [110,255,0], [115,255,0], [120,255,0], [125,255,0], [130,255,0], [135,255,0], [140,255,0], [145,255,0], [150,255,0], [155,255,0], [160,255,0], [165,255,0], [170,255,0], [175,255,0], [180,255,0], [185,255,0], [190,255,0], [195,255,0], [200,255,0], [205,255,0], [210,255,0], [215,255,0], [220,255,0], [225,255,0], [230,255,0], [235,255,0], [240,255,0], [245,255,0], [250,255,0], [255,255,0], [255,250,0], [255,245,0], [255,240,0], [255,235,0], [255,230,0], [255,225,0], [255,220,0], [255,215,0], [255,210,0], [255,205,0], [255,200,0], [255,195,0], [255,190,0], [255,185,0], [255,180,0], [255,175,0], [255,170,0], [255,165,0], [255,160,0], [255,155,0], [255,150,0], [255,145,0], [255,140,0], [255,135,0], [255,130,0], [255,125,0], [255,120,0], [255,115,0], [255,110,0], [255,105,0], [255,100,0], [255,95,0], [255,90,0], [255,85,0], [255,80,0], [255,75,0], [255,70,0], [255,65,0], [255,60,0], [255,55,0], [255,50,0], [255,45,0], [255,40,0], [255,35,0], [255,30,0], [255,25,0], [255,20,0], [255,15,0], [255,10,0], [255,5,0], [255,0,0]], 76 | redCm: [ [0,0,0], [1,0,0], [2,0,0], [3,0,0], [4,0,0], [5,0,0], [6,0,0], [7,0,0], [8,0,0], [9,0,0], [10,0,0], [11,0,0], [12,0,0], [13,0,0], [14,0,0], [15,0,0], [16,0,0], [17,0,0], [18,0,0], [19,0,0], [20,0,0], [21,0,0], [22,0,0], [23,0,0], [24,0,0], [25,0,0], [26,0,0], [27,0,0], [28,0,0], [29,0,0], [30,0,0], [31,0,0], [32,0,0], [33,0,0], [34,0,0], [35,0,0], [36,0,0], [37,0,0], [38,0,0], [39,0,0], [40,0,0], [41,0,0], [42,0,0], [43,0,0], [44,0,0], [45,0,0], [46,0,0], [47,0,0], [48,0,0], [49,0,0], [50,0,0], [51,0,0], [52,0,0], [53,0,0], [54,0,0], [55,0,0], [56,0,0], [57,0,0], [58,0,0], [59,0,0], [60,0,0], [61,0,0], [62,0,0], [63,0,0], [64,0,0], [65,0,0], [66,0,0], [67,0,0], [68,0,0], [69,0,0], [70,0,0], [71,0,0], [72,0,0], [73,0,0], [74,0,0], [75,0,0], [76,0,0], [77,0,0], [78,0,0], [79,0,0], [80,0,0], [81,0,0], [82,0,0], [83,0,0], [84,0,0], [85,0,0], [86,0,0], [87,0,0], [88,0,0], [89,0,0], [90,0,0], [91,0,0], [92,0,0], [93,0,0], [94,0,0], [95,0,0], [96,0,0], [97,0,0], [98,0,0], [99,0,0], [100,0,0], [101,0,0], [102,0,0], [103,0,0], [104,0,0], [105,0,0], [106,0,0], [107,0,0], [108,0,0], [109,0,0], [110,0,0], [111,0,0], [112,0,0], [113,0,0], [114,0,0], [115,0,0], [116,0,0], [117,0,0], [118,0,0], [119,0,0], [120,0,0], [121,0,0], [122,0,0], [123,0,0], [124,0,0], [125,0,0], [126,0,0], [127,0,0], [128,0,0], [129,0,0], [130,0,0], [131,0,0], [132,0,0], [133,0,0], [134,0,0], [135,0,0], [136,0,0], [137,0,0], [138,0,0], [139,0,0], [140,0,0], [141,0,0], [142,0,0], [143,0,0], [144,0,0], [145,0,0], [146,0,0], [147,0,0], [148,0,0], [149,0,0], [150,0,0], [151,0,0], [152,0,0], [153,0,0], [154,0,0], [155,0,0], [156,0,0], [157,0,0], [158,0,0], [159,0,0], [160,0,0], [161,0,0], [162,0,0], [163,0,0], [164,0,0], [165,0,0], [166,0,0], [167,0,0], [168,0,0], [169,0,0], [170,0,0], [171,0,0], [172,0,0], [173,0,0], [174,0,0], [175,0,0], [176,0,0], [177,0,0], [178,0,0], [179,0,0], [180,0,0], [181,0,0], [182,0,0], [183,0,0], [184,0,0], [185,0,0], [186,0,0], [187,0,0], [188,0,0], [189,0,0], [190,0,0], [191,0,0], [192,0,0], [193,0,0], [194,0,0], [195,0,0], [196,0,0], [197,0,0], [198,0,0], [199,0,0], [200,0,0], [201,0,0], [202,0,0], [203,0,0], [204,0,0], [205,0,0], [206,0,0], [207,0,0], [208,0,0], [209,0,0], [210,0,0], [211,0,0], [212,0,0], [213,0,0], [214,0,0], [215,0,0], [216,0,0], [217,0,0], [218,0,0], [219,0,0], [220,0,0], [221,0,0], [222,0,0], [223,0,0], [224,0,0], [225,0,0], [226,0,0], [227,0,0], [228,0,0], [229,0,0], [230,0,0], [231,0,0], [232,0,0], [233,0,0], [234,0,0], [235,0,0], [236,0,0], [237,0,0], [238,0,0], [239,0,0], [240,0,0], [241,0,0], [242,0,0], [243,0,0], [244,0,0], [245,0,0], [246,0,0], [247,0,0], [248,0,0], [249,0,0], [250,0,0], [251,0,0], [252,0,0], [253,0,0], [254,0,0], [255,0,0]], 77 | standardCm: [ [0,0,0], [0,0,3], [1,1,6], [2,2,9], [3,3,12], [4,4,15], [5,5,18], [6,6,21], [7,7,24], [8,8,27], [9,9,30], [10,10,33], [10,10,36], [11,11,39], [12,12,42], [13,13,45], [14,14,48], [15,15,51], [16,16,54], [17,17,57], [18,18,60], [19,19,63], [20,20,66], [20,20,69], [21,21,72], [22,22,75], [23,23,78], [24,24,81], [25,25,85], [26,26,88], [27,27,91], [28,28,94], [29,29,97], [30,30,100], [30,30,103], [31,31,106], [32,32,109], [33,33,112], [34,34,115], [35,35,118], [36,36,121], [37,37,124], [38,38,127], [39,39,130], [40,40,133], [40,40,136], [41,41,139], [42,42,142], [43,43,145], [44,44,148], [45,45,151], [46,46,154], [47,47,157], [48,48,160], [49,49,163], [50,50,166], [51,51,170], [51,51,173], [52,52,176], [53,53,179], [54,54,182], [55,55,185], [56,56,188], [57,57,191], [58,58,194], [59,59,197], [60,60,200], [61,61,203], [61,61,206], [62,62,209], [63,63,212], [64,64,215], [65,65,218], [66,66,221], [67,67,224], [68,68,227], [69,69,230], [70,70,233], [71,71,236], [71,71,239], [72,72,242], [73,73,245], [74,74,248], [75,75,251], [76,76,255], [0,78,0], [1,80,1], [2,82,2], [3,84,3], [4,87,4], [5,89,5], [6,91,6], [7,93,7], [8,95,8], [9,97,9], [9,99,9], [10,101,10], [11,103,11], [12,105,12], [13,108,13], [14,110,14], [15,112,15], [16,114,16], [17,116,17], [18,118,18], [18,120,18], [19,122,19], [20,124,20], [21,126,21], [22,129,22], [23,131,23], [24,133,24], [25,135,25], [26,137,26], [27,139,27], [27,141,27], [28,143,28], [29,145,29], [30,147,30], [31,150,31], [32,152,32], [33,154,33], [34,156,34], [35,158,35], [36,160,36], [36,162,36], [37,164,37], [38,166,38], [39,168,39], [40,171,40], [41,173,41], [42,175,42], [43,177,43], [44,179,44], [45,181,45], [45,183,45], [46,185,46], [47,187,47], [48,189,48], [49,192,49], [50,194,50], [51,196,51], [52,198,52], [53,200,53], [54,202,54], [54,204,54], [55,206,55], [56,208,56], [57,210,57], [58,213,58], [59,215,59], [60,217,60], [61,219,61], [62,221,62], [63,223,63], [63,225,63], [64,227,64], [65,229,65], [66,231,66], [67,234,67], [68,236,68], [69,238,69], [70,240,70], [71,242,71], [72,244,72], [72,246,72], [73,248,73], [74,250,74], [75,252,75], [76,255,76], [78,0,0], [80,1,1], [82,2,2], [84,3,3], [86,4,4], [88,5,5], [91,6,6], [93,7,7], [95,8,8], [97,8,8], [99,9,9], [101,10,10], [103,11,11], [105,12,12], [107,13,13], [109,14,14], [111,15,15], [113,16,16], [115,16,16], [118,17,17], [120,18,18], [122,19,19], [124,20,20], [126,21,21], [128,22,22], [130,23,23], [132,24,24], [134,24,24], [136,25,25], [138,26,26], [140,27,27], [142,28,28], [144,29,29], [147,30,30], [149,31,31], [151,32,32], [153,32,32], [155,33,33], [157,34,34], [159,35,35], [161,36,36], [163,37,37], [165,38,38], [167,39,39], [169,40,40], [171,40,40], [174,41,41], [176,42,42], [178,43,43], [180,44,44], [182,45,45], [184,46,46], [186,47,47], [188,48,48], [190,48,48], [192,49,49], [194,50,50], [196,51,51], [198,52,52], [201,53,53], [203,54,54], [205,55,55], [207,56,56], [209,56,56], [211,57,57], [213,58,58], [215,59,59], [217,60,60], [219,61,61], [221,62,62], [223,63,63], [225,64,64], [228,64,64], [230,65,65], [232,66,66], [234,67,67], [236,68,68], [238,69,69], [240,70,70], [242,71,71], [244,72,72], [246,72,72], [248,73,73], [250,74,74], [252,75,75], [255,76,76]] 78 | }; 79 | var cmapOptions = ''; 80 | Object.keys(cmaps).forEach(function(c) { 81 | cmapOptions += ''; 82 | }); 83 | var $html = $('
    ' + 84 | ' Colormap:
    ' + 86 | ' Center: ' + 87 | '
    '); 88 | var cmapUpdate = function() { 89 | var val = $('#cmapSelect').val(); 90 | $('#cmapSelect').change(function() { 91 | updateCallback(val); 92 | }); 93 | return cmaps[val]; 94 | }; 95 | var spinnerSlider = new SpinnerSlider({ 96 | $element: $html.find('#cmapCenter'), 97 | init: 128, 98 | min: 1, 99 | sliderMax: 254, 100 | step: 1, 101 | updateCallback: updateCallback 102 | }); 103 | return { 104 | html: $html, 105 | getParams: function() { 106 | return spinnerSlider.getValue(); 107 | }, 108 | getFilter: function() { 109 | /*eslint new-cap: 0*/ 110 | return OpenSeadragon.Filters.COLORMAP(cmapUpdate(), spinnerSlider.getValue()); 111 | }, 112 | sync: true 113 | }; 114 | } 115 | }, { 116 | name: 'Colorize', 117 | help: 'The adjustment range (strength) is from 0 to 100.' + 118 | 'The higher the value, the closer the colors in the ' + 119 | 'image shift towards the given adjustment color.' + 120 | 'Color values are between 0 to 255', 121 | generate: function(updateCallback) { 122 | var redSpinnerId = 'redSpinner-' + idIncrement; 123 | var greenSpinnerId = 'greenSpinner-' + idIncrement; 124 | var blueSpinnerId = 'blueSpinner-' + idIncrement; 125 | var strengthSpinnerId = 'strengthSpinner-' + idIncrement; 126 | /*eslint max-len: 0*/ 127 | var $html = $('
    ' + 128 | '
    ' + 129 | '
    ' + 130 | ' Red: ' + 131 | '
    ' + 132 | '
    ' + 133 | ' Green: ' + 134 | '
    ' + 135 | '
    ' + 136 | ' Blue: ' + 137 | '
    ' + 138 | '
    ' + 139 | ' Strength: ' + 140 | '
    ' + 141 | '
    ' + 142 | '
    '); 143 | var redSpinner = new Spinner({ 144 | $element: $html.find('#' + redSpinnerId), 145 | init: 100, 146 | min: 0, 147 | max: 255, 148 | step: 1, 149 | updateCallback: updateCallback 150 | }); 151 | var greenSpinner = new Spinner({ 152 | $element: $html.find('#' + greenSpinnerId), 153 | init: 20, 154 | min: 0, 155 | max: 255, 156 | step: 1, 157 | updateCallback: updateCallback 158 | }); 159 | var blueSpinner = new Spinner({ 160 | $element: $html.find('#' + blueSpinnerId), 161 | init: 20, 162 | min: 0, 163 | max: 255, 164 | step: 1, 165 | updateCallback: updateCallback 166 | }); 167 | var strengthSpinner = new Spinner({ 168 | $element: $html.find('#' + strengthSpinnerId), 169 | init: 50, 170 | min: 0, 171 | max: 100, 172 | step: 1, 173 | updateCallback: updateCallback 174 | }); 175 | return { 176 | html: $html, 177 | getParams: function() { 178 | var red = redSpinner.getValue(); 179 | var green = greenSpinner.getValue(); 180 | var blue = blueSpinner.getValue(); 181 | var strength = strengthSpinner.getValue(); 182 | return 'R: ' + red + ' G: ' + green + ' B: ' + blue + 183 | ' S: ' + strength; 184 | }, 185 | getFilter: function() { 186 | var red = redSpinner.getValue(); 187 | var green = greenSpinner.getValue(); 188 | var blue = blueSpinner.getValue(); 189 | var strength = strengthSpinner.getValue(); 190 | return function(context, callback) { 191 | caman(context.canvas, function() { 192 | this.colorize(red, green, blue, strength); 193 | this.render(callback); 194 | }); 195 | }; 196 | } 197 | }; 198 | } 199 | }, { 200 | name: 'Contrast', 201 | help: 'Range is from 0 to infinity, although sane values are from 0 ' + 202 | 'to 4 or 5. Values between 0 and 1 will lessen the contrast ' + 203 | 'while values greater than 1 will increase it.', 204 | generate: function(updateCallback) { 205 | var $html = $('
    '); 206 | var spinnerSlider = new SpinnerSlider({ 207 | $element: $html, 208 | init: 1.3, 209 | min: 0, 210 | sliderMax: 4, 211 | step: 0.1, 212 | updateCallback: updateCallback 213 | }); 214 | return { 215 | html: $html, 216 | getParams: function() { 217 | return spinnerSlider.getValue(); 218 | }, 219 | getFilter: function() { 220 | return OpenSeadragon.Filters.CONTRAST( 221 | spinnerSlider.getValue()); 222 | }, 223 | sync: true 224 | }; 225 | } 226 | }, { 227 | name: 'Exposure', 228 | help: 'Range is -100 to 100. Values < 0 will decrease ' + 229 | 'exposure while values > 0 will increase exposure', 230 | generate: function(updateCallback) { 231 | var $html = $('
    '); 232 | var spinnerSlider = new SpinnerSlider({ 233 | $element: $html, 234 | init: 10, 235 | min: -100, 236 | max: 100, 237 | step: 1, 238 | updateCallback: updateCallback 239 | }); 240 | return { 241 | html: $html, 242 | getParams: function() { 243 | return spinnerSlider.getValue(); 244 | }, 245 | getFilter: function() { 246 | var value = spinnerSlider.getValue(); 247 | return function(context, callback) { 248 | caman(context.canvas, function() { 249 | this.exposure(value); 250 | this.render(callback); // don't forget to call the callback. 251 | }); 252 | }; 253 | } 254 | }; 255 | } 256 | }, { 257 | name: 'Gamma', 258 | help: 'Range is from 0 to infinity, although sane values ' + 259 | 'are from 0 to 4 or 5. Values between 0 and 1 will ' + 260 | 'lessen the contrast while values greater than 1 will increase it.', 261 | generate: function(updateCallback) { 262 | var $html = $('
    '); 263 | var spinnerSlider = new SpinnerSlider({ 264 | $element: $html, 265 | init: 0.5, 266 | min: 0, 267 | sliderMax: 5, 268 | step: 0.1, 269 | updateCallback: updateCallback 270 | }); 271 | return { 272 | html: $html, 273 | getParams: function() { 274 | return spinnerSlider.getValue(); 275 | }, 276 | getFilter: function() { 277 | var value = spinnerSlider.getValue(); 278 | return OpenSeadragon.Filters.GAMMA(value); 279 | } 280 | }; 281 | } 282 | }, { 283 | name: 'Hue', 284 | help: 'hue value is between 0 to 100 representing the ' + 285 | 'percentage of Hue shift in the 0 to 360 range', 286 | generate: function(updateCallback) { 287 | var $html = $('
    '); 288 | var spinnerSlider = new SpinnerSlider({ 289 | $element: $html, 290 | init: 20, 291 | min: 0, 292 | max: 100, 293 | step: 1, 294 | updateCallback: updateCallback 295 | }); 296 | return { 297 | html: $html, 298 | getParams: function() { 299 | return spinnerSlider.getValue(); 300 | }, 301 | getFilter: function() { 302 | var value = spinnerSlider.getValue(); 303 | return function(context, callback) { 304 | caman(context.canvas, function() { 305 | this.hue(value); 306 | this.render(callback); // don't forget to call the callback. 307 | }); 308 | }; 309 | } 310 | }; 311 | } 312 | }, { 313 | name: 'Saturation', 314 | help: 'saturation value has to be between -100 and 100', 315 | generate: function(updateCallback) { 316 | var $html = $('
    '); 317 | var spinnerSlider = new SpinnerSlider({ 318 | $element: $html, 319 | init: 50, 320 | min: -100, 321 | max: 100, 322 | step: 1, 323 | updateCallback: updateCallback 324 | }); 325 | return { 326 | html: $html, 327 | getParams: function() { 328 | return spinnerSlider.getValue(); 329 | }, 330 | getFilter: function() { 331 | var value = spinnerSlider.getValue(); 332 | return function(context, callback) { 333 | caman(context.canvas, function() { 334 | this.saturation(value); 335 | this.render(callback); // don't forget to call the callback. 336 | }); 337 | }; 338 | } 339 | }; 340 | } 341 | }, { 342 | name: 'Vibrance', 343 | help: 'vibrance value has to be between -100 and 100', 344 | generate: function(updateCallback) { 345 | var $html = $('
    '); 346 | var spinnerSlider = new SpinnerSlider({ 347 | $element: $html, 348 | init: 50, 349 | min: -100, 350 | max: 100, 351 | step: 1, 352 | updateCallback: updateCallback 353 | }); 354 | return { 355 | html: $html, 356 | getParams: function() { 357 | return spinnerSlider.getValue(); 358 | }, 359 | getFilter: function() { 360 | var value = spinnerSlider.getValue(); 361 | return function(context, callback) { 362 | caman(context.canvas, function() { 363 | this.vibrance(value); 364 | this.render(callback); // don't forget to call the callback. 365 | }); 366 | }; 367 | } 368 | }; 369 | } 370 | }, { 371 | name: 'Sepia', 372 | help: 'sepia value has to be between 0 and 100', 373 | generate: function(updateCallback) { 374 | var $html = $('
    '); 375 | var spinnerSlider = new SpinnerSlider({ 376 | $element: $html, 377 | init: 50, 378 | min: 0, 379 | max: 100, 380 | step: 1, 381 | updateCallback: updateCallback 382 | }); 383 | return { 384 | html: $html, 385 | getParams: function() { 386 | return spinnerSlider.getValue(); 387 | }, 388 | getFilter: function() { 389 | var value = spinnerSlider.getValue(); 390 | return function(context, callback) { 391 | caman(context.canvas, function() { 392 | this.sepia(value); 393 | this.render(callback); // don't forget to call the callback. 394 | }); 395 | }; 396 | } 397 | }; 398 | } 399 | }, { 400 | name: 'Noise', 401 | help: 'Noise cannot be smaller than 0', 402 | generate: function(updateCallback) { 403 | var $html = $('
    '); 404 | var spinnerSlider = new SpinnerSlider({ 405 | $element: $html, 406 | init: 50, 407 | min: 0, 408 | step: 1, 409 | updateCallback: updateCallback 410 | }); 411 | return { 412 | html: $html, 413 | getParams: function() { 414 | return spinnerSlider.getValue(); 415 | }, 416 | getFilter: function() { 417 | var value = spinnerSlider.getValue(); 418 | return function(context, callback) { 419 | caman(context.canvas, function() { 420 | this.noise(value); 421 | this.render(callback); // don't forget to call the callback. 422 | }); 423 | }; 424 | } 425 | }; 426 | } 427 | }, { 428 | name: 'Greyscale', 429 | generate: function() { 430 | return { 431 | html: '', 432 | getParams: function() { 433 | return ''; 434 | }, 435 | getFilter: function() { 436 | return OpenSeadragon.Filters.GREYSCALE(); 437 | }, 438 | sync: true 439 | }; 440 | } 441 | }, { 442 | name: 'Sobel Edge', 443 | generate: function() { 444 | return { 445 | html: '', 446 | getParams: function() { 447 | return ''; 448 | }, 449 | getFilter: function() { 450 | return function(context, callback) { 451 | var imgData = context.getImageData( 452 | 0, 0, context.canvas.width, context.canvas.height); 453 | var pixels = imgData.data; 454 | var originalPixels = context.getImageData(0, 0, context.canvas.width, context.canvas.height).data; 455 | var oneRowOffset = context.canvas.width * 4; 456 | var onePixelOffset = 4; 457 | var Gy, Gx; 458 | var idx = 0; 459 | for (var i = 1; i < context.canvas.height - 1; i += 1) { 460 | idx = oneRowOffset * i + 4; 461 | for (var j = 1; j < context.canvas.width - 1; j += 1) { 462 | Gy = originalPixels[idx - onePixelOffset + oneRowOffset] + 2 * originalPixels[idx + oneRowOffset] + originalPixels[idx + onePixelOffset + oneRowOffset]; 463 | Gy = Gy - (originalPixels[idx - onePixelOffset - oneRowOffset] + 2 * originalPixels[idx - oneRowOffset] + originalPixels[idx + onePixelOffset - oneRowOffset]); 464 | Gx = originalPixels[idx + onePixelOffset - oneRowOffset] + 2 * originalPixels[idx + onePixelOffset] + originalPixels[idx + onePixelOffset + oneRowOffset]; 465 | Gx = Gx - (originalPixels[idx - onePixelOffset - oneRowOffset] + 2 * originalPixels[idx - onePixelOffset] + originalPixels[idx - onePixelOffset + oneRowOffset]); 466 | pixels[idx] = Math.sqrt(Gx * Gx + Gy * Gy); // 0.5*Math.abs(Gx) + 0.5*Math.abs(Gy);//100*Math.atan(Gy,Gx); 467 | pixels[idx + 1] = 0; 468 | pixels[idx + 2] = 0; 469 | idx += 4; 470 | } 471 | } 472 | context.putImageData(imgData, 0, 0); 473 | callback(); 474 | }; 475 | } 476 | }; 477 | } 478 | }, { 479 | name: 'Brightness', 480 | help: 'Brightness must be between -255 (darker) and 255 (brighter).', 481 | generate: function(updateCallback) { 482 | var $html = $('
    '); 483 | var spinnerSlider = new SpinnerSlider({ 484 | $element: $html, 485 | init: 50, 486 | min: -255, 487 | max: 255, 488 | step: 1, 489 | updateCallback: updateCallback 490 | }); 491 | return { 492 | html: $html, 493 | getParams: function() { 494 | return spinnerSlider.getValue(); 495 | }, 496 | getFilter: function() { 497 | return OpenSeadragon.Filters.BRIGHTNESS( 498 | spinnerSlider.getValue()); 499 | }, 500 | sync: true 501 | }; 502 | } 503 | }, { 504 | name: 'Erosion', 505 | help: 'The erosion kernel size must be an odd number.', 506 | generate: function(updateCallback) { 507 | var $html = $('
    '); 508 | var spinner = new Spinner({ 509 | $element: $html, 510 | init: 3, 511 | min: 3, 512 | step: 2, 513 | updateCallback: updateCallback 514 | }); 515 | return { 516 | html: $html, 517 | getParams: function() { 518 | return spinner.getValue(); 519 | }, 520 | getFilter: function() { 521 | return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION( 522 | spinner.getValue(), Math.min); 523 | } 524 | }; 525 | } 526 | }, { 527 | name: 'Dilation', 528 | help: 'The dilation kernel size must be an odd number.', 529 | generate: function(updateCallback) { 530 | var $html = $('
    '); 531 | var spinner = new Spinner({ 532 | $element: $html, 533 | init: 3, 534 | min: 3, 535 | step: 2, 536 | updateCallback: updateCallback 537 | }); 538 | return { 539 | html: $html, 540 | getParams: function() { 541 | return spinner.getValue(); 542 | }, 543 | getFilter: function() { 544 | return OpenSeadragon.Filters.MORPHOLOGICAL_OPERATION( 545 | spinner.getValue(), Math.max); 546 | } 547 | }; 548 | } 549 | }, { 550 | name: 'Thresholding', 551 | help: 'The threshold must be between 0 and 255.', 552 | generate: function(updateCallback) { 553 | var $html = $('
    '); 554 | var spinnerSlider = new SpinnerSlider({ 555 | $element: $html, 556 | init: 127, 557 | min: 0, 558 | max: 255, 559 | step: 1, 560 | updateCallback: updateCallback 561 | }); 562 | return { 563 | html: $html, 564 | getParams: function() { 565 | return spinnerSlider.getValue(); 566 | }, 567 | getFilter: function() { 568 | return OpenSeadragon.Filters.THRESHOLDING( 569 | spinnerSlider.getValue()); 570 | }, 571 | sync: true 572 | }; 573 | } 574 | }]; 575 | availableFilters.sort(function(f1, f2) { 576 | return f1.name.localeCompare(f2.name); 577 | }); 578 | 579 | var idIncrement = 0; 580 | var hashTable = {}; 581 | 582 | availableFilters.forEach(function(filter) { 583 | var $li = $('
  • '); 584 | var $plus = $('+'); 585 | $li.append($plus); 586 | $li.append(filter.name); 587 | $li.appendTo($('#available')); 588 | $plus.click(function() { 589 | var id = 'selected_' + idIncrement++; 590 | var generatedFilter = filter.generate(updateFilters); 591 | hashTable[id] = { 592 | name: filter.name, 593 | generatedFilter: generatedFilter 594 | }; 595 | var $li = $('
  • '); 596 | var $minus = $('
    -
    '); 597 | $li.find('.wdzt-row-layout').append($minus); 598 | $li.find('.wdzt-row-layout').append('
    ' + filter.name + '
    '); 599 | if (filter.help) { 600 | var $help = $('
    help
    '); 602 | $help.tooltip(); 603 | $li.find('.wdzt-row-layout').append($help); 604 | } 605 | $li.find('.wdzt-row-layout').append( 606 | $('
    ') 607 | .append(generatedFilter.html)); 608 | $minus.click(function() { 609 | delete hashTable[id]; 610 | $li.remove(); 611 | updateFilters(); 612 | }); 613 | $li.appendTo($('#selected')); 614 | updateFilters(); 615 | }); 616 | }); 617 | 618 | $('#selected').sortable({ 619 | containment: 'parent', 620 | axis: 'y', 621 | tolerance: 'pointer', 622 | update: updateFilters 623 | }); 624 | 625 | function updateFilters() { 626 | var filters = []; 627 | var sync = true; 628 | $('#selected li').each(function() { 629 | var id = this.id; 630 | var filter = hashTable[id]; 631 | filters.push(filter.generatedFilter.getFilter()); 632 | sync &= filter.generatedFilter.sync; 633 | }); 634 | viewer.setFilterOptions({ 635 | filters: { 636 | processors: filters 637 | }, 638 | loadMode: sync ? 'sync' : 'async' 639 | }); 640 | } 641 | 642 | --------------------------------------------------------------------------------