diff --git a/docs/MODULES.md b/docs/MODULES.md index d1a9137e..da51fb74 100644 --- a/docs/MODULES.md +++ b/docs/MODULES.md @@ -41,11 +41,12 @@ List of Module Documentations 36. [Rotate](#rotate-module) 37. [Saturation](#saturation-module) 38. [Segmented-Colormap](#segmented-colormap-module) -39. [Text-Overlay](#text-overlay) -40. [Threshold](#threshold) -41. [Tint](#tint) -42. [WebGL-Distort](#webgl-distort-module) -43. [White-Balance](#white-balance-module) +39. [Sharpen](#sharpening-module) +40. [Text-Overlay](#text-overlay) +41. [Threshold](#threshold) +42. [Tint](#tint) +43. [WebGL-Distort](#webgl-distort-module) +44. [White-Balance](#white-balance-module) ## add-qr-module @@ -667,6 +668,20 @@ where `options` is an object with the property `colormap`. `options.colormap` ca * A custom array. +## sharpen-module + +This module is used to sharpen the pixels of the image using a 3x3 convolution filter. +#### Usage + +```js + sequencer.loadImage('PATH') + .addSteps('sharpen',options) + .run() +``` + +where `options` is an object with the property `sharpenStrength`, which can be set to achieve the desired level of sharpening on the image. + + ## Text Overlay The modules allows to add text to image in both browser and node environment. We have the options to modify the font-size and also support few font-styles. The text color can also be modified. diff --git a/src/Modules.js b/src/Modules.js index 20f0354a..6dc0bdc5 100644 --- a/src/Modules.js +++ b/src/Modules.js @@ -46,6 +46,7 @@ module.exports = { 'rotate': require('./modules/Rotate'), 'saturation': require('./modules/Saturation'), 'shadow': require('./modules/Shadow'), + 'sharpen': require('./modules/Sharpen'), 'text-overlay': require('./modules/TextOverlay'), 'threshold': require('./modules/Threshold'), 'tint': require('./modules/Tint'), diff --git a/src/modules/Sharpen/Module.js b/src/modules/Sharpen/Module.js new file mode 100644 index 00000000..3e7d6406 --- /dev/null +++ b/src/modules/Sharpen/Module.js @@ -0,0 +1,46 @@ +/* + Sharpen an image +*/ +module.exports = function Sharpen(options, UI) { + + let defaults = require('./../../util/getDefaults.js')(require('./info.json')); + options.sharpenStrength = options.sharpenStrength || defaults.sharpenStrength; + options.sharpenStrength = parseFloat(options.sharpenStrength); //returns a float + let output; + + function draw(input, callback, progressObj) { + + progressObj.stop(true); + progressObj.overrideFlag = true; + + let step = this; + + function extraManipulation(pixels) { + pixels = require('./Sharpen')(pixels, options.sharpenStrength); + return (pixels); + } + + function output(image, datauri, mimetype, wasmSuccess) { + step.output = { src: datauri, format: mimetype, wasmSuccess, useWasm: options.useWasm }; + } + + return require('../_nomodule/PixelManipulation.js')(input, { + output: output, + ui: options.step.ui, + inBrowser: options.inBrowser, + extraManipulation: extraManipulation, + format: input.format, + image: options.image, + callback: callback, + useWasm:options.useWasm + }); + + } + return { + options: options, + draw: draw, + output: output, + UI: UI + }; +}; + \ No newline at end of file diff --git a/src/modules/Sharpen/Sharpen.js b/src/modules/Sharpen/Sharpen.js new file mode 100644 index 00000000..c7de5b61 --- /dev/null +++ b/src/modules/Sharpen/Sharpen.js @@ -0,0 +1,48 @@ +// Generates a 3x3 convolution sharpening kernel +function kernelGenerator(strength = 1) { //default value of sharpeningStrength set to 1 + + let kernel = [ + [0, -1 * strength, 0], + [-1 * strength, 5 * strength, -1 * strength], + [0, -1 * strength, 0] + ]; + return kernel; +} + +module.exports = exports = function(pixels, sharpen) { + const pixelSetter = require('../../util/pixelSetter.js'); + + let kernel = kernelGenerator(sharpen), // Generate the kernel based on the strength input. + pixs = { // Separates the rgb channel pixels to convolve on the GPU. + r: [], + g: [], + b: [], + }; + for (let y = 0; y < pixels.shape[1]; y++){ + pixs.r.push([]); + pixs.g.push([]); + pixs.b.push([]); + + for (let x = 0; x < pixels.shape[0]; x++){ + pixs.r[y].push(pixels.get(x, y, 0)); + pixs.g[y].push(pixels.get(x, y, 1)); + pixs.b[y].push(pixels.get(x, y, 2)); + } + } + + const convolve = require('../_nomodule/gpuUtils').convolve; // GPU convolution function. + + const conPix = convolve([pixs.r, pixs.g, pixs.b], kernel); // Convolves the pixels (all channels separately) on the GPU. + + for (let y = 0; y < pixels.shape[1]; y++){ + for (let x = 0; x < pixels.shape[0]; x++){ + var pixelvalue = [Math.max(0, Math.min(conPix[0][y][x], 255)), + Math.max(0, Math.min(conPix[1][y][x], 255)), + Math.max(0, Math.min(conPix[2][y][x], 255))]; + + pixelSetter(x, y, pixelvalue, pixels); // Sets the image pixels according to the sharpened values. + + } + } + return (pixels); +}; \ No newline at end of file diff --git a/src/modules/Sharpen/index.js b/src/modules/Sharpen/index.js new file mode 100644 index 00000000..71549002 --- /dev/null +++ b/src/modules/Sharpen/index.js @@ -0,0 +1,4 @@ +module.exports = [ + require('./Module'), + require('./info.json') +]; \ No newline at end of file diff --git a/src/modules/Sharpen/info.json b/src/modules/Sharpen/info.json new file mode 100644 index 00000000..7d9fcb05 --- /dev/null +++ b/src/modules/Sharpen/info.json @@ -0,0 +1,15 @@ +{ + "name": "sharpen", + "description": "Applies a sharpening filter given by the intensity value", + "inputs": { + "sharpenStrength": { + "type": "float", + "desc": "Amount of sharpening (More sharpening gives more detail, but may lead to overexposure)", + "default": 1, + "min": 1, + "max": 1.5, + "step": 0.05 + } + }, + "docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#sharpen-module" +} diff --git a/test/core/images/moon.js b/test/core/images/moon.js new file mode 100644 index 00000000..f797ecb0 --- /dev/null +++ b/test/core/images/moon.js @@ -0,0 +1,2 @@ +module.exports = 'data:image/png;base64,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D5sCSQAAACd0RVh0ZXhpZjpDb21wb25lbnRzQ29uZmlndXJhdGlvbgAxLCAyLCAzLCAwVaQjvwAAABN0RVh0ZXhpZjpFeGlmT2Zmc2V0ADEwMnNCKacAAAAfdEVYdGV4aWY6RXhpZlZlcnNpb24ANDgsIDUwLCA1MCwgNDlj1An4AAAAI3RFWHRleGlmOkZsYXNoUGl4VmVyc2lvbgA0OCwgNDksIDQ4LCA0OO/ZB2sAAAAYdEVYdGV4aWY6UGl4ZWxYRGltZW5zaW9uADY2MBUn8W4AAAAYdEVYdGV4aWY6UGl4ZWxZRGltZW5zaW9uADY4MBarPZYAAAAXdEVYdGV4aWY6U2NlbmVDYXB0dXJlVHlwZQAwIrQxYwAAAFx0RVh0ZXhpZjpVc2VyQ29tbWVudAA2NSwgODMsIDY3LCA3MywgNzMsIDAsIDAsIDAsIDgzLCA5OSwgMTE0LCAxMDEsIDEwMSwgMTEwLCAxMTUsIDEwNCwgMTExLCAxMTZAuB9yAAAAF3RFWHRleGlmOllDYkNyUG9zaXRpb25pbmcAMawPgGMAAAAodEVYdGljYzpjb3B5cmlnaHQAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMTe/8xjQAAAAGnRFWHRpY2M6ZGVzY3JpcHRpb24ARGlzcGxheSBQM495u7wAAAAASUVORK5CYII='; +//base64 of original unmodified image \ No newline at end of file diff --git a/test/core/modules/sharpen.js b/test/core/modules/sharpen.js new file mode 100644 index 00000000..4bd73fce --- /dev/null +++ b/test/core/modules/sharpen.js @@ -0,0 +1,14 @@ +const testModule = require('../templates/module-test'), + image = require('../images/moon'), + benchmark = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAklEQVR4AewaftIAAAJZSURBVH3BPUvrUACA4bfnJETsoIIV6qBFCtKiQoc6SPD7I3/ACw6KoJv+Bkm4Sy/iD3DQzU2oOLu5iCCilKJQ7CjtFbcEY3Ka24AFEa/PkwAiPmiaRhiGxIQQSCkJgoCfCD7ouk4YhnS0Wi2iKCKRSPATQZuu6wRBQGz11yqFQgHDMAjDkCiKMAyD/xFSSoIgIOZ5HoPpQYaHh9nZ2aG3t5eY7/toaHxH8OHw8JDj42NmZ2eRUiKlZG1tjYQQxEJCviOUUsS2t7dxHIerqyvq9Trr6+tsbm6yvLREhyElX2m0LSwu8Pb2RjKZJJ/PYxgG19fXPD4+0tfXRzabpVar4SuFlBKlFB2CtpW5FZp/m0xPT2NZFvV6nUajgeu6jI6Osru7S7FYJKaU4jNB2+8/v8kMZzg4OGBgYIBCoYBt29i2jWmaPD09YZomXV1dxIQQdEjAfvffsW2bXC5HKpViaGgIy7LwfZ9KpcL5+Tmu61IsFrm/vyeKIqSURFGE4BPTNCmXyxwdHdHT08Pd3R03NzdsbW0xMjJCrVZjbm6OmFKKmEgmk3ScnJxwdnbG6ekpY2NjWJbF/Pw8Dw8PpNNp8vk8ruvymQzD0KbNcRxs26ZareJ5HjMzMzw/PzMxMcHr6ytTU1PkcjnK5TIvLy90SMDWdZ1Wq4XjOFQqFRqNBtlslkwmQ6lUolQqcXFxwd7eHsvLy4yPj3N7e0ssAUS0dXd343kesWq1Sn+qn1R/ipjv+0gp2d/fZ3FxkcnJSWKappEAIj5IKVFK8dnGxgbNZhPXdbm8vOSrf2/T7iEkhMBLAAAAAElFTkSuQmCC', + benchmark1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAklEQVR4AewaftIAAAI8SURBVH3BPUvjYADA8X+Sh6RUqS9jdx0cilMEl1oQQYfTVUHwtJycIoIuTpoigoubn0D0E+jsC+pSqDoXpCB4DkVMsR48aeJzeaABEc/fzwAUbbZtEwQBmmVZCCGQUvIdkzbHcQiCgEQURSilMAyD75jEHMdBSom2VdpifHycdDpNEAQopUin0/yPJYTwWq0WmlKKpz9PGIbB0tISl5eXNJtNWq0WNjYREZ+ZtD0+PlKr1ZienkYIgRCCnZ0dDMtCCwj4ihmGIVo2m6VQKHB8fMzt7S1TU1PMz8/ze3GRRFoIPhPEir+KaD09PeTzeTo6OqjX61xfX5PNZnFdl3K5zN8wRAhBGIYkTGLLP5fRZmdnyWQy3NzccH9/j+/7DA8Pc3BwwOTkJFoYhnxkEhv9MYq2traGNjExwcjICHNzcxQKBSqVCjMzM3R2dqJZlkXCJPZcf0bL5XK8vb0xMDCA7/to1WqVw8NDzs/P2dzcRIuiCCEEmgV4xDzPo9FocHp6ytnZGWNjY1SrVU5OTlhfX0dKycXFBUNDQ9zd3fH+/o5mdnd3k9jY2GB3d5ft7W20/v5+FhYWuLq6oq+vj3w+j+/7fGRJKT1ipVIJz/PQBgcHSaVSaKlUit7eXnK5HK7rsr+/z8PDAwkDUI7jIKVEU0qRyGQyvL6+opSiUqlQLBZZXV2l2WyysrKCZgCKWFdXF41GA00pxVfK5TKu62IYBppt2xiAok0IQRiGfLS3t0etVuPl5YWjoyM++wcQzOdOsGwgXgAAAABJRU5ErkJggg==', + option = { + sharpenStrength: 1.0 + }, + option1 = { + sharpenStrength: 1.5 + }, + optionsTest = require('../templates/options-test'); + +optionsTest('sharpen', [option, option1], [benchmark, benchmark1], image); +testModule('sharpen', option, benchmark, image); \ No newline at end of file