Skip to content

Commit 3af73d5

Browse files
WIP. Create first draft of Gaussian BLur algorithm.
1 parent 610391d commit 3af73d5

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

src/utils/image.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,88 @@ export class RawImage {
651651
}
652652
}
653653

654+
/**
655+
* Generates a 1D Gaussian kernel.
656+
* @param {number} kernelSize - Kernel size (must be odd).
657+
* @param {number} sigma - Standard deviation of the Gaussian.
658+
* @returns {Array<number>} - The 1D Gaussian kernel.
659+
*/
660+
generateGaussianKernel(kernelSize, sigma) {
661+
// Kernel must be odd because each pixel must sit evenly in the middle.
662+
if (kernelSize % 2 === 0) {
663+
throw new Error('Kernel size must be odd.');
664+
}
665+
666+
const kernel = [];
667+
const center = Math.floor(kernelSize / 2);
668+
let sum = 0;
669+
670+
for (let i = 0; i < kernelSize; i++) {
671+
kernel[i] = [];
672+
for (let j = 0; j < kernelSize; j++) {
673+
const x = i - center;
674+
const y = j - center;
675+
676+
// Square the numbers.
677+
const x2 = x * x;
678+
const y2 = y * y;
679+
const sigma2 = sigma * sigma;
680+
681+
const value = Math.exp(-(x2 + y2) / (2 * sigma2));
682+
kernel[i][j] = value;
683+
sum += value;
684+
}
685+
}
686+
687+
// Normalise the kernel.
688+
for (let i = 0; i < kernelSize; i++) {
689+
for (let j = 0; j < kernelSize; j++) {
690+
kernel[i][j] /= sum;
691+
}
692+
}
693+
694+
return kernel;
695+
}
696+
697+
/**
698+
* Performs a Gaussian blur on the image.
699+
* @param {number} kernelSize - Kernel size (must be odd).
700+
* @param {number} sigma - Standard deviation of the Gaussian.
701+
* @returns {Promise<RawImage>} - The blurred image.
702+
*/
703+
async gaussianBlur(kernelSize = 3, sigma = 1) {
704+
const kernel = this.generateGaussianKernel(kernelSize, sigma);
705+
const output = new Uint8ClampedArray(this.data.length);
706+
const halfSize = Math.floor(kernelSize / 2);
707+
708+
for (let c = 0; c < this.channels; c++) {
709+
for (let y = 0; y < this.height; y++) {
710+
for (let x = 0; x < this.width; x++) {
711+
let sum = 0;
712+
713+
for (let ky = -halfSize; ky < halfSize; ky++) {
714+
for (let kx = -halfSize; kx < halfSize; kx++) {
715+
const pixelX = Math.min(Math.max(x + kx, 0), this.width - 1);
716+
const pixelY = Math.min(Math.max(y + ky, 0), this.height - 1);
717+
718+
const kernelValue = kernel[ky + halfSize][kx + halfSize];
719+
const dataIndex = (pixelY * this.width + pixelX) * this.channels + c;
720+
721+
sum += this.data[dataIndex] * kernelValue;
722+
}
723+
}
724+
725+
const outputIndex = (y * this.width + x) * this.channels + c;
726+
output[outputIndex] = sum;
727+
}
728+
}
729+
}
730+
731+
// Update the image data with the blurred result.
732+
this.data = output;
733+
return this;
734+
}
735+
654736
async toBlob(type = 'image/png', quality = 1) {
655737
if (!IS_BROWSER_OR_WEBWORKER) {
656738
throw new Error('toBlob() is only supported in browser environments.')

0 commit comments

Comments
 (0)