@ -57,11 +57,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
private readonly float kernelsScale ;
/// <summary>
/// The mapping of initialized complex kernels and parameters, to speed up the initialization of new <see cref="BokehBlurProcessor{TPixel}"/> instances
/// </summary>
private static readonly ConcurrentDictionary < BokehBlurParameters , BokehBlurKernelData > Cache = new ConcurrentDictionary < BokehBlurParameters , BokehBlurKernelData > ( ) ;
/// <summary>
/// Initializes a new instance of the <see cref="BokehBlurProcessor{TPixel}"/> class.
/// </summary>
@ -77,24 +72,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
this . componentsCount = definition . Components ;
this . gamma = definition . Gamma ;
// Reuse the initialized values from the cache, if possible
var parameters = new BokehBlurParameters ( this . radius , this . componentsCount ) ;
if ( Cache . TryGetValue ( parameters , out BokehBlurKernelData info ) )
{
this . kernelParameters = info . Parameters ;
this . kernelsScale = info . Scale ;
this . kernels = info . Kernels ;
}
else
{
// Initialize the complex kernels and parameters with the current arguments
( this . kernelParameters , this . kernelsScale ) = this . GetParameters ( ) ;
this . kernels = this . CreateComplexKernels ( ) ;
this . NormalizeKernels ( ) ;
// Get the bokeh blur data
BokehBlurKernelData data = BokehBlurKernelDataProvider . GetBokehBlurKernelData ( this . radius , this . kernelSize , this . componentsCount ) ;
// Store them in the cache for future use
Cache . TryAdd ( parameters , new BokehBlurKernelData ( this . kernelParameters , this . kernelsScale , this . kernels ) ) ;
}
this . kernelParameters = data . Parameters ;
this . kernelsScale = data . Scale ;
this . kernels = data . Kernels ;
}
/// <summary>
@ -107,163 +90,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
/// </summary>
public IReadOnlyList < Vector4 > KernelParameters = > this . kernelParameters ;
/// <summary>
/// Gets the kernel scales to adjust the component values in each kernel
/// </summary>
private static IReadOnlyList < float > KernelScales { get ; } = new [ ] { 1.4f , 1.2f , 1.2f , 1.2f , 1.2f , 1.2f } ;
/// <summary>
/// Gets the available bokeh blur kernel parameters
/// </summary>
private static IReadOnlyList < Vector4 [ ] > KernelComponents { get ; } = new [ ]
{
// 1 component
new [ ] { new Vector4 ( 0.862325f , 1.624835f , 0.767583f , 1.862321f ) } ,
// 2 components
new [ ]
{
new Vector4 ( 0.886528f , 5.268909f , 0.411259f , - 0.548794f ) ,
new Vector4 ( 1.960518f , 1.558213f , 0.513282f , 4.56111f )
} ,
// 3 components
new [ ]
{
new Vector4 ( 2.17649f , 5.043495f , 1.621035f , - 2.105439f ) ,
new Vector4 ( 1.019306f , 9.027613f , - 0.28086f , - 0.162882f ) ,
new Vector4 ( 2.81511f , 1.597273f , - 0.366471f , 1 0.300301f )
} ,
// 4 components
new [ ]
{
new Vector4 ( 4.338459f , 1.553635f , - 5.767909f , 4 6.164397f ) ,
new Vector4 ( 3.839993f , 4.693183f , 9.795391f , - 1 5.227561f ) ,
new Vector4 ( 2.791880f , 8.178137f , - 3.048324f , 0.302959f ) ,
new Vector4 ( 1.342190f , 1 2.328289f , 0.010001f , 0.244650f )
} ,
// 5 components
new [ ]
{
new Vector4 ( 4.892608f , 1.685979f , - 2 2.356787f , 8 5.91246f ) ,
new Vector4 ( 4.71187f , 4.998496f , 3 5.918936f , - 2 8.875618f ) ,
new Vector4 ( 4.052795f , 8.244168f , - 1 3.212253f , - 1.578428f ) ,
new Vector4 ( 2.929212f , 1 1.900859f , 0.507991f , 1.816328f ) ,
new Vector4 ( 1.512961f , 1 6.116382f , 0.138051f , - 0.01f )
} ,
// 6 components
new [ ]
{
new Vector4 ( 5.143778f , 2.079813f , - 8 2.326596f , 1 1 1.231024f ) ,
new Vector4 ( 5.612426f , 6.153387f , 1 1 3.878661f , 5 8.004879f ) ,
new Vector4 ( 5.982921f , 9.802895f , 3 9.479083f , - 1 6 2.028887f ) ,
new Vector4 ( 6.505167f , 1 1.059237f , - 7 1.286026f , 9 5.027069f ) ,
new Vector4 ( 3.869579f , 1 4.81052f , 1.405746f , - 3.704914f ) ,
new Vector4 ( 2.201904f , 1 9.032909f , - 0.152784f , - 0.107988f )
}
} ;
/// <summary>
/// Gets the kernel parameters and scaling factor for the current count value in the current instance
/// </summary>
private ( Vector4 [ ] Parameters , float Scale ) GetParameters ( )
{
// Prepare the kernel components
int index = Math . Max ( 0 , Math . Min ( this . componentsCount - 1 , KernelComponents . Count ) ) ;
return ( KernelComponents [ index ] , KernelScales [ index ] ) ;
}
/// <summary>
/// Creates the collection of complex 1D kernels with the specified parameters
/// </summary>
private Complex64 [ ] [ ] CreateComplexKernels ( )
{
var kernels = new Complex64 [ this . kernelParameters . Length ] [ ] ;
ref Vector4 baseRef = ref MemoryMarshal . GetReference ( this . kernelParameters . AsSpan ( ) ) ;
for ( int i = 0 ; i < this . kernelParameters . Length ; i + + )
{
ref Vector4 paramsRef = ref Unsafe . Add ( ref baseRef , i ) ;
kernels [ i ] = this . CreateComplex1DKernel ( paramsRef . X , paramsRef . Y ) ;
}
return kernels ;
}
/// <summary>
/// Creates a complex 1D kernel with the specified parameters
/// </summary>
/// <param name="a">The exponential parameter for each complex component</param>
/// <param name="b">The angle component for each complex component</param>
private Complex64 [ ] CreateComplex1DKernel ( float a , float b )
{
var kernel = new Complex64 [ this . kernelSize ] ;
ref Complex64 baseRef = ref MemoryMarshal . GetReference ( kernel . AsSpan ( ) ) ;
int r = this . radius , n = - r ;
for ( int i = 0 ; i < this . kernelSize ; i + + , n + + )
{
// Incrementally compute the range values
float value = n * this . kernelsScale * ( 1f / r ) ;
value * = value ;
// Fill in the complex kernel values
Unsafe . Add ( ref baseRef , i ) = new Complex64 (
MathF . Exp ( - a * value ) * MathF . Cos ( b * value ) ,
MathF . Exp ( - a * value ) * MathF . Sin ( b * value ) ) ;
}
return kernel ;
}
/// <summary>
/// Normalizes the kernels with respect to A * real + B * imaginary
/// </summary>
private void NormalizeKernels ( )
{
// Calculate the complex weighted sum
float total = 0 ;
Span < Complex64 [ ] > kernelsSpan = this . kernels . AsSpan ( ) ;
ref Complex64 [ ] baseKernelsRef = ref MemoryMarshal . GetReference ( kernelsSpan ) ;
ref Vector4 baseParamsRef = ref MemoryMarshal . GetReference ( this . kernelParameters . AsSpan ( ) ) ;
for ( int i = 0 ; i < this . kernelParameters . Length ; i + + )
{
ref Complex64 [ ] kernelRef = ref Unsafe . Add ( ref baseKernelsRef , i ) ;
int length = kernelRef . Length ;
ref Complex64 valueRef = ref kernelRef [ 0 ] ;
ref Vector4 paramsRef = ref Unsafe . Add ( ref baseParamsRef , i ) ;
for ( int j = 0 ; j < length ; j + + )
{
for ( int k = 0 ; k < length ; k + + )
{
ref Complex64 jRef = ref Unsafe . Add ( ref valueRef , j ) ;
ref Complex64 kRef = ref Unsafe . Add ( ref valueRef , k ) ;
total + =
( paramsRef . Z * ( ( jRef . Real * kRef . Real ) - ( jRef . Imaginary * kRef . Imaginary ) ) )
+ ( paramsRef . W * ( ( jRef . Real * kRef . Imaginary ) + ( jRef . Imaginary * kRef . Real ) ) ) ;
}
}
}
// Normalize the kernels
float scalar = 1f / MathF . Sqrt ( total ) ;
for ( int i = 0 ; i < kernelsSpan . Length ; i + + )
{
ref Complex64 [ ] kernelsRef = ref Unsafe . Add ( ref baseKernelsRef , i ) ;
int length = kernelsRef . Length ;
ref Complex64 valueRef = ref kernelsRef [ 0 ] ;
for ( int j = 0 ; j < length ; j + + )
{
Unsafe . Add ( ref valueRef , j ) * = scalar ;
}
}
}
/// <inheritdoc/>
protected override void OnFrameApply ( ImageFrame < TPixel > source )
{