@@ -665,30 +665,19 @@ export class RawImage {
665
665
666
666
const kernel = [ ] ;
667
667
const center = Math . floor ( kernelSize / 2 ) ;
668
+ const sigma2 = sigma * sigma ;
668
669
let sum = 0 ;
669
670
670
671
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
- }
672
+ const x = i - center ;
673
+ const value = Math . exp ( - ( x * x ) / ( 2 * sigma2 ) ) ;
674
+ kernel [ i ] = value ;
675
+ sum += value ;
685
676
}
686
677
687
- // Normalise the kernel.
678
+ // Normalize the kernel
688
679
for ( let i = 0 ; i < kernelSize ; i ++ ) {
689
- for ( let j = 0 ; j < kernelSize ; j ++ ) {
690
- kernel [ i ] [ j ] /= sum ;
691
- }
680
+ kernel [ i ] /= sum ;
692
681
}
693
682
694
683
return kernel ;
@@ -702,38 +691,57 @@ export class RawImage {
702
691
*/
703
692
async gaussianBlur ( kernelSize = 3 , sigma = 1 ) {
704
693
const kernel = this . generateGaussianKernel ( kernelSize , sigma ) ;
705
- const output = new Uint8ClampedArray ( this . data . length ) ;
706
694
const halfSize = Math . floor ( kernelSize / 2 ) ;
707
-
708
- const height = this . height ;
709
695
const width = this . width ;
696
+ const height = this . height ;
710
697
const channels = this . channels ;
711
698
699
+ // Rather than checking an entire grid of elements, we can instead do
700
+ // two separate passes with a 1d array (rather than 2d).
701
+ // Consider a 3x3 kernel, instead of calculating each pixel 9 times, we
702
+ // can instead calculate two sets of 3 values and then combine them.
703
+ const horizontalPass = new Float32Array ( this . data . length ) ;
704
+ const verticalPass = new Uint8ClampedArray ( this . data . length ) ;
705
+
706
+ // Horizontal pass.
712
707
for ( let y = 0 ; y < height ; y ++ ) {
713
708
for ( let x = 0 ; x < width ; x ++ ) {
714
709
for ( let c = 0 ; c < channels ; c ++ ) {
715
710
let sum = 0 ;
716
711
717
- for ( let ky = - halfSize ; ky <= halfSize ; ky ++ ) {
718
- for ( let kx = - halfSize ; kx <= halfSize ; kx ++ ) {
719
- const pixelX = Math . min ( Math . max ( x + kx , 0 ) , width - 1 ) ;
720
- const pixelY = Math . min ( Math . max ( y + ky , 0 ) , height - 1 ) ;
712
+ for ( let kx = - halfSize ; kx <= halfSize ; kx ++ ) {
713
+ const pixelX = Math . min ( Math . max ( x + kx , 0 ) , width - 1 ) ;
714
+ const dataIndex = ( ( y * width ) + pixelX ) * channels + c ;
715
+ const kernelValue = kernel [ kx + halfSize ] ;
716
+ sum += this . data [ dataIndex ] * kernelValue ;
717
+ }
718
+
719
+ const outputIndex = ( ( y * width ) + x ) * channels + c ;
720
+ horizontalPass [ outputIndex ] = sum ;
721
+ }
722
+ }
723
+ }
721
724
722
- const kernelValue = kernel [ ky + halfSize ] [ kx + halfSize ] ;
723
- const dataIndex = ( ( ( pixelY * width ) + pixelX ) * channels ) + c ;
725
+ // Vertical pass.
726
+ for ( let y = 0 ; y < height ; y ++ ) {
727
+ for ( let x = 0 ; x < width ; x ++ ) {
728
+ for ( let c = 0 ; c < channels ; c ++ ) {
729
+ let sum = 0 ;
724
730
725
- sum += this . data [ dataIndex ] * kernelValue ;
726
- }
731
+ for ( let ky = - halfSize ; ky <= halfSize ; ky ++ ) {
732
+ const pixelY = Math . min ( Math . max ( y + ky , 0 ) , height - 1 ) ;
733
+ const dataIndex = ( ( pixelY * width ) + x ) * channels + c ;
734
+ const kernelValue = kernel [ ky + halfSize ] ;
735
+ sum += horizontalPass [ dataIndex ] * kernelValue ;
727
736
}
728
737
729
- const outputIndex = ( ( ( y * width ) + x ) * channels ) + c ;
730
- output [ outputIndex ] = sum ;
738
+ const outputIndex = ( ( y * width ) + x ) * channels + c ;
739
+ verticalPass [ outputIndex ] = sum ;
731
740
}
732
741
}
733
742
}
734
743
735
- // Update the image data with the blurred result.
736
- this . data = output ;
744
+ this . data = verticalPass ;
737
745
return this ;
738
746
}
739
747
@@ -785,7 +793,7 @@ export class RawImage {
785
793
/**
786
794
* Split this image into individual bands. This method returns an array of individual image bands from an image.
787
795
* For example, splitting an "RGB" image creates three new images each containing a copy of one of the original bands (red, green, blue).
788
- *
796
+ *
789
797
* Inspired by PIL's `Image.split()` [function](https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.split).
790
798
* @returns {RawImage[] } An array containing bands.
791
799
*/
0 commit comments