Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = {
'minify-image': require('./modules/MinifyImage'),
// 'invert': require('image-sequencer-invert'),
'invert': require('./modules/Invert'),
'motion-blur': require('./modules/MotionBlur'),
'ndvi': require('./modules/Ndvi'),
'ndvi-colormap': require('./modules/NdviColormap'),
'noise-reduction': require('./modules/NoiseReduction'),
Expand Down
117 changes: 117 additions & 0 deletions src/modules/MotionBlur/Module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Rotates image
*/
module.exports = function Rotate(options, UI) {

let output;

function draw(input, callback, progressObj) {

const defaults = require('./../../util/getDefaults.js')(require('./info.json'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the require statement be pushed to the top?

options.rotate = options.rotate || defaults.rotate;

progressObj.stop(true);
progressObj.overrideFlag = true;

const step = this;

function changePixel(r, g, b, a) {
return [r, g, b, a];
}

function extraManipulation(pixels) {
const rotate_value = (options.rotate) % 360;
radians = (Math.PI) * rotate_value / 180,
width = pixels.shape[0],
height = pixels.shape[1],
cos = Math.cos(radians),
sin = Math.sin(radians);
// Final dimensions after rotation

const finalPixels = require('ndarray')(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

new Uint8Array(
4 *
(
Math.floor(
Math.abs(width * cos) +
Math.abs(height * sin) +
5
) *
(
Math.floor(
Math.abs(width * sin) +
Math.abs(height * cos)
) +
5
)
)
).fill(255),
[
Math.floor(Math.abs(width * cos) + Math.abs(height * sin)) + 5,
Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 4,
4
]
);

pixels = require('./MotionBlur')(pixels, finalPixels, rotate_value, width, height, cos, sin);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well.


const new_rotate_value = 360 - ((options.rotate) % 360);
new_radians = (Math.PI) * new_rotate_value / 180,
new_width = Math.floor(Math.abs(width * cos) + Math.abs(height * sin) + 5),
new_height = Math.floor(Math.abs(width * sin) + Math.abs(height * cos)) + 5,
new_cos = Math.cos(new_radians),
new_sin = Math.sin(new_radians);

const new_finalPixels = require('ndarray')(
new Uint8Array(
4 *
(
Math.floor(
Math.abs(new_width * new_cos) +
Math.abs(new_height * new_sin) +
5
) *
(
Math.floor(
Math.abs(new_width * new_sin) +
Math.abs(new_height * new_cos)
) +
5
)
)
).fill(255),
[
Math.floor(Math.abs(new_width * new_cos) + Math.abs(new_height * new_sin)) + 5,
Math.floor(Math.abs(new_width * new_sin) + Math.abs(new_height * new_cos)) + 4,
4
]
);

new_pixels = require('./MotionBlur')(finalPixels, new_finalPixels, new_rotate_value, new_width, new_height, new_cos, new_sin);
return new_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,
changePixel: changePixel,
extraManipulation: extraManipulation,
format: input.format,
image: options.image,
inBrowser: options.inBrowser,
callback: callback,
useWasm:options.useWasm
});
}

return {
options: options,
draw: draw,
output: output,
UI: UI
};
};
151 changes: 151 additions & 0 deletions src/modules/MotionBlur/MotionBlur.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Generates a 5x5 Gaussian kernel
function kernelGenerator(sigma = 1) {

let kernel = [],
sum = 0;

if (sigma == 0) sigma += 0.05;

const s = 2 * Math.pow(sigma, 2);

for (let y = -10; y <= 10; y++) {
kernel.push([]);
for (let x = -10; x <= 10; x++) {
let r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
if (y == 0) {
kernel[y + 10].push(Math.exp(-(r / s)));
}
else {
kernel[y + 10].push(0);
}
sum += kernel[y + 10][x + 10];
}
}

for (let x = 0; x < 21; x++){
for (let y = 0; y < 21; y++){
kernel[y][x] = (kernel[y][x] / sum);
}
}

return kernel;
}

const imagejs = require('imagejs'),
ndarray = require('ndarray');

module.exports = exports = function(pixels, finalPixels, rotate_value, width, height, cos, sin) {
const pixelSetter = require('../../util/pixelSetter.js');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file too

var defaults = require('./../../util/getDefaults.js')(require('./info.json'));
options.blur = options.blur || defaults.blur;

const height_half = Math.floor(height / 2),
width_half = Math.floor(width / 2);
dimension = width + height;

if (rotate_value % 360 == 0) return pixels;

function copyPixel(x1, y1, x2, y2, finalPix, initPix) {
finalPix.set(x1, y1, 0, initPix.get(x2, y2, 0));
finalPix.set(x1, y1, 1, initPix.get(x2, y2, 1));
finalPix.set(x1, y1, 2, initPix.get(x2, y2, 2));
finalPix.set(x1, y1, 3, initPix.get(x2, y2, 3));
}

const intermediatePixels = new ndarray(
new Uint8Array(4 * dimension * dimension).fill(255),
[dimension, dimension, 4]
); // Intermediate ndarray of pixels with a greater size to prevent clipping.

// Copying all the pixels from image to intermediatePixels
for (let x = 0; x < pixels.shape[0]; x++){
for (let y = 0; y < pixels.shape[1]; y++){
copyPixel(x + height_half, y + width_half, x, y, intermediatePixels, pixels);
}
}

// Rotating intermediatePixels
const bitmap = new imagejs.Bitmap({ width: intermediatePixels.shape[0], height: intermediatePixels.shape[1] });

for (let x = 0; x < intermediatePixels.shape[0]; x++) {
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
let r = intermediatePixels.get(x, y, 0),
g = intermediatePixels.get(x, y, 1),
b = intermediatePixels.get(x, y, 2),
a = intermediatePixels.get(x, y, 3);

bitmap.setPixel(x, y, r, g, b, a);
}
}

const rotated = bitmap.rotate({
degrees: rotate_value,
});

for (let x = 0; x < intermediatePixels.shape[0]; x++) {
for (let y = 0; y < intermediatePixels.shape[1]; y++) {
const {r, g, b, a} = rotated.getPixel(x, y);
pixelSetter(x, y, [r, g, b, a], intermediatePixels);
}
}

// Cropping extra whitespace
for (let x = 0; x < finalPixels.shape[0]; x++){
for (let y = 0; y < finalPixels.shape[1]; y++){
copyPixel(
x,
y,
x +
Math.floor(
dimension / 2 -
Math.abs(width * cos / 2) -
Math.abs(height * sin / 2)
) - 1,
y +
Math.floor(
dimension / 2 -
Math.abs(height * cos / 2) -
Math.abs(width * sin / 2)
) - 1,
finalPixels,
intermediatePixels
);
}
}


// blur pixels using GPU
let kernel = kernelGenerator(options.blur), // Generate the Gaussian kernel based on the sigma input.
pixs = { // Separates the rgb channel pixels to convolve on the GPU.
r: [],
g: [],
b: [],
};

for (let y = 0; y < finalPixels.shape[1]; y++){
pixs.r.push([]);
pixs.g.push([]);
pixs.b.push([]);

for (let x = 0; x < finalPixels.shape[0]; x++){
pixs.r[y].push(finalPixels.get(x, y, 0));
pixs.g[y].push(finalPixels.get(x, y, 1));
pixs.b[y].push(finalPixels.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 < finalPixels.shape[1]; y++){
for (let x = 0; x < finalPixels.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, finalPixels); // Sets the image pixels according to the blurred values.
}
}

return finalPixels;
};
4 changes: 4 additions & 0 deletions src/modules/MotionBlur/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = [
require('./Module'),
require('./info.json')
];
23 changes: 23 additions & 0 deletions src/modules/MotionBlur/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "motion-blur",
"description": "Applies a Motion blur given by the intensity and angle value",
"inputs": {
"blur": {
"type": "float",
"desc": "Amount of motion blur",
"default": 3.5,
"min": 0,
"max": 5,
"step": 0.05
},
"rotate": {
"type": "integer",
"desc": "Angular value for rotation in degrees",
"default": "90",
"min": "0",
"max": "360",
"step": "1"
}
},
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blur-module"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs the correct link.

}