Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
034472e
Added base BokehBlurProcessor class, and kernel parameters
Sergio0694 Feb 19, 2019
cdd004d
Added method to calculate the kernel parameters
Sergio0694 Feb 19, 2019
0bcd244
Switched to float, added method to create the 1D kernels
Sergio0694 Feb 19, 2019
3dbf5e5
Added complex kernels normalization
Sergio0694 Feb 19, 2019
c21d35b
Added BokehBlurExtensions class
Sergio0694 Feb 19, 2019
e0c60cc
Added the Complex64 struct type
Sergio0694 Feb 20, 2019
b42b9f7
Switched to Complex64 in the BokehBlurProcessor
Sergio0694 Feb 20, 2019
177a40c
Added caching system for the bokeh processor parameters
Sergio0694 Feb 20, 2019
0716ced
Added WeightedSum method to the Complex64 type
Sergio0694 Feb 20, 2019
47721ce
Added IEquatable<T> interface to the Complex64 type
Sergio0694 Feb 21, 2019
8468a2f
New complex types added
Sergio0694 Feb 21, 2019
2e7fb74
Added method to reshape a DenseMatrix<T> with no copies
Sergio0694 Feb 21, 2019
bd8c2e3
Added bokeh convolution first pass (WIP)
Sergio0694 Feb 21, 2019
ceebb7b
Added second bokeh convolution pass (WIP)
Sergio0694 Feb 21, 2019
732d229
Added image sum pass to the bokeh processor (WIP)
Sergio0694 Feb 21, 2019
5fe13ea
Minor bug fixes (WIP)
Sergio0694 Feb 21, 2019
df8c786
Switched to Vector4 processing in the bokeh computation
Sergio0694 Feb 21, 2019
9bd1c87
Minor tweaks
Sergio0694 Feb 21, 2019
15d0a98
Added Unit test for the bokeh kernel components
Sergio0694 Feb 22, 2019
6fc9c50
Minor performance improvements
Sergio0694 Feb 22, 2019
5985904
Minor code refactoring, added gamma parameter (WIP)
Sergio0694 Feb 22, 2019
3b7ad24
Removed unused temp buffers in the bokeh processing
Sergio0694 Feb 22, 2019
c654965
Gamma highlight processing implemented
Sergio0694 Feb 22, 2019
a261f93
Speed optimizations, fixed partials computations in target rectangle
Sergio0694 Feb 22, 2019
b36a907
Increased epsilon value in the unit tests
Sergio0694 Feb 23, 2019
5b8579b
Fixed for alpha transparency blur
Sergio0694 Feb 23, 2019
bf090ad
Fixed a bug when only blurring a target rectangle
Sergio0694 Feb 23, 2019
34308fe
Added bokeh blur image tests (WIP)
Sergio0694 Feb 23, 2019
c2486eb
Added IXunitSerializable interface to the test info class
Sergio0694 Feb 23, 2019
1fe7a43
culture independent parsing in BokehBlurTest.cs
antonfirsov Feb 23, 2019
fb4efe5
Performance optimizations in the bokeh processor
Sergio0694 Feb 23, 2019
9711d5f
Reduced number of memory allocations, fixed bug with multiple components
Sergio0694 Feb 23, 2019
697766e
Initialization and other speed improvements
Sergio0694 Feb 23, 2019
bd348a3
More initialization speed improvements
Sergio0694 Feb 23, 2019
ee5bbc3
Replaced LINQ with manual loop
Sergio0694 Feb 23, 2019
a64636e
Added BokehBlur overload to just specify the target bounds
Sergio0694 Feb 23, 2019
e18066e
Speed optimizations to the bokeh 1D convolutions
Sergio0694 Feb 23, 2019
1122563
More speed optimizations to the bokeh processor
Sergio0694 Feb 23, 2019
afcf78a
Fixed code style and Complex64.ToString method
Sergio0694 Feb 24, 2019
b56da13
Fixed processing buffer initialization
Sergio0694 Feb 26, 2019
32d062d
Minor performance improvements
Sergio0694 Feb 26, 2019
6e3e541
FIxed issue when applying bokeh blur to specific bounds
Sergio0694 Mar 9, 2019
39ce26c
Minor speed optimizaations
Sergio0694 Mar 9, 2019
4f77a38
Minor code refactoring
Sergio0694 Mar 9, 2019
93b6a82
Fixed convolution upper bound in second 1D pass
Sergio0694 Mar 15, 2019
d0b0b2b
improve BokehBlurTest coverage
antonfirsov Mar 31, 2019
3772e89
use Gray8 instead of Alpha8
antonfirsov Mar 31, 2019
2ed623a
Adjusted guard position in bokeh processor constructor
Sergio0694 Mar 31, 2019
c05d39a
Added BokehBlurParameters struct
Sergio0694 Mar 31, 2019
d511f06
Added BokehBlurKernelData struct
Sergio0694 Mar 31, 2019
73ee135
Minor code refactoring
Sergio0694 Mar 31, 2019
de72bfd
Fixed API change build errors
Sergio0694 Apr 7, 2019
93e8c1a
Bug fixes with the pixel premultiplication steps
Sergio0694 Apr 7, 2019
2e89d3d
Removed unwanted unpremultiplication pass
Sergio0694 Apr 7, 2019
7ec6905
Removed unused using directives
Sergio0694 Apr 7, 2019
2c5c85d
Fixed missing using directives in conditional branches
Sergio0694 Apr 7, 2019
343da66
Update from latest upstream master
JimBobSquarePants Jul 29, 2019
9304db2
Merge branch 'master' into feature_bokeh-blur-impl
JimBobSquarePants Jul 29, 2019
32d9077
Update Block8x8F.Generated.cs
JimBobSquarePants Jul 29, 2019
512586e
Update GenericBlock8x8.Generated.cs
JimBobSquarePants Jul 29, 2019
0e6259d
Manual checking for files with LF (see gitter)
Sergio0694 Jul 29, 2019
f36564c
Removed unused using directive
Sergio0694 Jul 29, 2019
9491262
Added IEquatable<ComplexVector4> interface
Sergio0694 Jul 29, 2019
9edf46b
Added IEquatable<BokehBlurParameters> interface
Sergio0694 Jul 29, 2019
db58163
Moved bokeh blur parameters types
Sergio0694 Jul 29, 2019
24cc75a
Added reference to original source code
Sergio0694 Jul 29, 2019
0a77f69
Complex convolution methods moved to another class
Sergio0694 Jul 29, 2019
04984cd
Switched to MathF in the bokeh blur processor
Sergio0694 Jul 29, 2019
a70518b
Switched to Vector4.Clamp
Sergio0694 Jul 29, 2019
8aec5e0
Added Buffer2D<T>.Slice API
Sergio0694 Aug 1, 2019
3f24cee
Added BokehBlurExecutionMode enum
Sergio0694 Aug 1, 2019
06dfb02
Added new bokeh blur processor constructors
Sergio0694 Aug 1, 2019
f1a0c37
Added new bokeh blur extension overloads with execution mode
Sergio0694 Aug 1, 2019
ebd64f6
Code refactoring in preparation for the execution mode switch
Sergio0694 Aug 1, 2019
5fc559a
Implemented execution mode switch in the bokeh processor
Sergio0694 Aug 1, 2019
7e7f163
Merge pull request #7 from Sergio0694/feature_bokeh-blur-impl-switch
Sergio0694 Aug 4, 2019
9aa75cc
Moved BokehBlurExecutionMode struct
Sergio0694 Aug 5, 2019
0869492
Removed unused using directives
Sergio0694 Aug 5, 2019
f663adf
Minor code refactoring
Sergio0694 Aug 5, 2019
8e9986e
More minor code refactoring
Sergio0694 Aug 5, 2019
5d28f6e
Update External
JimBobSquarePants Aug 6, 2019
c94948c
Fix undisposed buffers
JimBobSquarePants Aug 6, 2019
11fadaf
Bokeh blur processor cache switched to concurrent dictionary
Sergio0694 Aug 6, 2019
871ae09
Minor code refactoring
Sergio0694 Aug 6, 2019
efe5322
Merge branch 'master' into feature_bokeh-blur-impl
antonfirsov Aug 6, 2019
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
24 changes: 21 additions & 3 deletions src/ImageSharp/Memory/Buffer2D{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,31 @@ public Buffer2D(MemorySource<T> memorySource, int width, int height)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ImageSharp.DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
ImageSharp.DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y));

Span<T> span = this.Span;
return ref span[(this.Width * y) + x];
}
}

/// <summary>
/// Creates a new <see cref="Buffer2D{T}"/> instance that maps to a target rows interval from the current instance.
/// </summary>
/// <param name="y">The target vertical offset for the rows interval to retrieve.</param>
/// <param name="h">The desired number of rows to extract.</param>
/// <returns>The new <see cref="Buffer2D{T}"/> instance with the requested rows interval.</returns>
public Buffer2D<T> Slice(int y, int h)
{
DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y));
DebugGuard.MustBeGreaterThan(h, 0, nameof(h));
DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h));

Memory<T> slice = this.Memory.Slice(y * this.Width, h * this.Width);
var memory = new MemorySource<T>(slice);
return new Buffer2D<T>(memory, this.Width, h);
}

/// <summary>
/// Disposes the <see cref="Buffer2D{T}"/> instance
/// </summary>
Expand Down Expand Up @@ -98,4 +116,4 @@ private static void SwapDimensionData(Buffer2D<T> a, Buffer2D<T> b)
a.Height = bSize.Height;
}
}
}
}
49 changes: 49 additions & 0 deletions src/ImageSharp/Processing/Extensions/BokehBlurExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using SixLabors.ImageSharp.Processing.Processors.Convolution;
using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters;
using SixLabors.Primitives;

namespace SixLabors.ImageSharp.Processing
Expand All @@ -19,6 +20,15 @@ public static class BokehBlurExtensions
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source)
=> source.ApplyProcessor(new BokehBlurProcessor());

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="executionMode">The execution mode to use when applying the processor.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, BokehBlurExecutionMode executionMode)
=> source.ApplyProcessor(new BokehBlurProcessor(executionMode));

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
Expand All @@ -30,6 +40,18 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma));

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="components">The 'components' value representing the number of kernels to use to approximate the bokeh effect.</param>
/// <param name="gamma">The gamma highlight factor to use to emphasize bright spots in the source image</param>
/// <param name="executionMode">The execution mode to use when applying the processor.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode));

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
Expand All @@ -41,6 +63,18 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(), rectangle);

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="executionMode">The execution mode to use when applying the processor.</param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, Rectangle rectangle, BokehBlurExecutionMode executionMode)
=> source.ApplyProcessor(new BokehBlurProcessor(executionMode), rectangle);

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
Expand All @@ -54,5 +88,20 @@ public static IImageProcessingContext BokehBlur(this IImageProcessingContext sou
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma), rectangle);

/// <summary>
/// Applies a bokeh blur to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="components">The 'components' value representing the number of kernels to use to approximate the bokeh effect.</param>
/// <param name="gamma">The gamma highlight factor to use to emphasize bright spots in the source image</param>
/// <param name="executionMode">The execution mode to use when applying the processor.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext BokehBlur(this IImageProcessingContext source, int radius, int components, float gamma, BokehBlurExecutionMode executionMode, Rectangle rectangle)
=> source.ApplyProcessor(new BokehBlurProcessor(radius, components, gamma, executionMode), rectangle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.

using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters;

namespace SixLabors.ImageSharp.Processing.Processors.Convolution
{
Expand All @@ -25,11 +26,27 @@ public sealed class BokehBlurProcessor : IImageProcessor
/// </summary>
public const float DefaultGamma = 3F;

/// <summary>
/// The default execution mode used by the parameterless constructor.
/// </summary>
public const BokehBlurExecutionMode DefaultExecutionMode = BokehBlurExecutionMode.PreferLowMemoryUsage;

/// <summary>
/// Initializes a new instance of the <see cref="BokehBlurProcessor"/> class.
/// </summary>
public BokehBlurProcessor()
: this(DefaultRadius, DefaultComponents, DefaultGamma)
: this(DefaultRadius, DefaultComponents, DefaultGamma, DefaultExecutionMode)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="BokehBlurProcessor"/> class.
/// </summary>
/// <param name="executionMode">
/// The execution mode to use when applying the processor.
/// </param>
public BokehBlurProcessor(BokehBlurExecutionMode executionMode)
: this(DefaultRadius, DefaultComponents, DefaultGamma, executionMode)
{
}

Expand All @@ -46,12 +63,33 @@ public BokehBlurProcessor()
/// The gamma highlight factor to use to further process the image.
/// </param>
public BokehBlurProcessor(int radius, int components, float gamma)
: this(radius, components, gamma, DefaultExecutionMode)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="BokehBlurProcessor"/> class.
/// </summary>
/// <param name="radius">
/// The 'radius' value representing the size of the area to sample.
/// </param>
/// <param name="components">
/// The number of components to use to approximate the original 2D bokeh blur convolution kernel.
/// </param>
/// <param name="gamma">
/// The gamma highlight factor to use to further process the image.
/// </param>
/// <param name="executionMode">
/// The execution mode to use when applying the processor.
/// </param>
public BokehBlurProcessor(int radius, int components, float gamma, BokehBlurExecutionMode executionMode)
{
Guard.MustBeGreaterThanOrEqualTo(gamma, 1, nameof(gamma));

this.Radius = radius;
this.Components = components;
this.Gamma = gamma;
this.ExecutionMode = executionMode;
}

/// <summary>
Expand All @@ -69,6 +107,11 @@ public BokehBlurProcessor(int radius, int components, float gamma)
/// </summary>
public float Gamma { get; }

/// <summary>
/// Gets the exection mode to use when applying the effect.
/// </summary>
public BokehBlurExecutionMode ExecutionMode { get; }

/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
private readonly float gamma;

/// <summary>
/// The maximum size of the kernel in either direction.
/// The execution mode to use when applying the effect
/// </summary>
private readonly BokehBlurExecutionMode executionMode;

/// <summary>
/// The maximum size of the kernel in either direction
/// </summary>
private readonly int kernelSize;

Expand Down Expand Up @@ -75,6 +80,7 @@ public BokehBlurProcessor(BokehBlurProcessor definition)
this.kernelSize = (this.radius * 2) + 1;
this.componentsCount = definition.Components;
this.gamma = definition.Gamma;
this.executionMode = definition.ExecutionMode;

// Reuse the initialized values from the cache, if possible
var parameters = new BokehBlurParameters(this.radius, this.componentsCount);
Expand Down Expand Up @@ -271,30 +277,67 @@ protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle source

// Create a 0-filled buffer to use to store the result of the component convolutions
using (Buffer2D<Vector4> processing = configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size(), AllocationOptions.Clean))
using (Buffer2D<ComplexVector4>
partialValues = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()),
firstPassValues = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
{
// Perform two 1D convolutions for each component in the current instance
ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
for (int i = 0; i < this.kernels.Length; i++)
if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
{
// Compute the resulting complex buffer for the current component
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
Complex64[] kernel = Unsafe.Add(ref baseRef, i);
this.ApplyConvolution(firstPassValues, source.PixelBuffer, interest, kernel, configuration);
this.ApplyConvolution(partialValues, firstPassValues, interest, kernel, configuration);

// Add the results of the convolution with the current kernel
Vector4 parameters = this.kernelParameters[i];
this.SumProcessingPartials(processing, partialValues, sourceRectangle, configuration, parameters.Z, parameters.W);
// Memory usage priority: allocate a shared buffer and execute the second convolution in sequential mode
using (Buffer2D<ComplexVector4> buffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Width, source.Height + this.radius))
{
Buffer2D<ComplexVector4> firstPassBuffer = buffer.Slice(this.radius, source.Height);
Buffer2D<ComplexVector4> secondPassBuffer = buffer.Slice(0, source.Height);

this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassBuffer, secondPassBuffer);
}
}
else
{
// Performance priority: allocate two independent buffers and execute both convolutions in parallel mode
using (Buffer2D<ComplexVector4>
firstPassValues = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()),
secondPassBuffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()))
{
this.OnFrameApplyCore(source, sourceRectangle, configuration, processing, firstPassValues, secondPassBuffer);
}
}

// Apply the inverse gamma exposure pass, and write the final pixel data
this.ApplyInverseGammaExposure(source.PixelBuffer, processing, sourceRectangle, configuration);
}
}

/// <summary>
/// Computes and aggregates the convolution for each complex kernel component in the processor.
/// </summary>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">The <see cref="Rectangle" /> structure that specifies the portion of the image object to draw.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="processingBuffer">The buffer with the raw pixel data to use to aggregate the results of each convolution.</param>
/// <param name="firstPassBuffer">The complex buffer to use for the first 1D convolution pass for each kernel.</param>
/// <param name="secondPassBuffer">The complex buffer to use for the second 1D convolution pass for each kernel.</param>
private void OnFrameApplyCore(
ImageFrame<TPixel> source,
Rectangle sourceRectangle,
Configuration configuration,
Buffer2D<Vector4> processingBuffer,
Buffer2D<ComplexVector4> firstPassBuffer,
Buffer2D<ComplexVector4> secondPassBuffer)
{
// Perform two 1D convolutions for each component in the current instance
ref Complex64[] baseRef = ref MemoryMarshal.GetReference(this.kernels.AsSpan());
for (int i = 0; i < this.kernels.Length; i++)
{
// Compute the resulting complex buffer for the current component
var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
Complex64[] kernel = Unsafe.Add(ref baseRef, i);
this.ApplyConvolution(firstPassBuffer, source.PixelBuffer, interest, kernel, configuration);
this.ApplyConvolution(secondPassBuffer, firstPassBuffer, interest, kernel, configuration);

// Add the results of the convolution with the current kernel
Vector4 parameters = this.kernelParameters[i];
this.SumProcessingPartials(processingBuffer, secondPassBuffer, sourceRectangle, configuration, parameters.Z, parameters.W);
}
}

/// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageFrame{TPixel}"/> at the specified location
/// and with the specified size.
Expand Down Expand Up @@ -368,6 +411,12 @@ private void ApplyConvolution(
var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY);
int width = workingRectangle.Width;

if (this.executionMode == BokehBlurExecutionMode.PreferLowMemoryUsage)
{
configuration = configuration.Clone();
configuration.MaxDegreeOfParallelism = 1;
}

ParallelHelper.IterateRows(
workingRectangle,
configuration,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

namespace SixLabors.ImageSharp.Processing.Processors.Convolution.Parameters
{
/// <summary>
/// An <see langword="enum"/> that indicates execution options for the <see cref="BokehBlurProcessor"/>.
/// </summary>
public enum BokehBlurExecutionMode
{
/// <summary>
/// Indicates that the maximum performance should be prioritized over memory usage.
/// </summary>
PreferMaximumPerformance,

/// <summary>
/// Indicates that the memory usage should be prioritized over raw performance.
/// </summary>
PreferLowMemoryUsage
}
}