-
-
Notifications
You must be signed in to change notification settings - Fork 886
Addition of Breadley AdaptiveThreshold #725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+442
−1
Merged
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
e1d39de
Addition of Breadley AdaptiveThreshold
SimantoR 152b8e6
# Added Rect.Intersect
SimantoR 7951548
Merge branch 'master' into master
SimantoR 170a65d
Merge branch 'master' into master
SimantoR 5d61a3f
Merge branch 'master' into master
SimantoR 95a7b0d
Added parallelism to loops
SimantoR 1e7dc83
Merge branch 'master' into master
SimantoR 2b6d93a
Temporary fix to accomodate #744
SimantoR bb5cc29
Few general changes without effecting the algorithm implementation
SimantoR ba8929c
Merge branch 'master' into master
SimantoR a501113
Merge branch 'master' of https://github.com/SimantoR/ImageSharp
SimantoR d8f3b39
Fixed few breaking changes
SimantoR 8978bc3
Missed an end of line by accident :p
SimantoR 6805f6d
Used TempBuffer and fixed few logical errors
SimantoR b054102
Added contructor to control threshold limit
SimantoR 319fc95
Fixed several bugs produced during parallelism implementations
SimantoR a239e60
Algorithm behaves abnormally when applied with ParallelHelpers
SimantoR 1f52c9d
Fully working implementation
SimantoR 31e5d8d
Changed naming convention for threshold limit param
SimantoR 8610f5c
Fixed a minor bug
SimantoR be3718d
update to most recent version
SimantoR a5a0ecd
Re-add external test images
brianpopow 019d973
Merge remote-tracking branch 'upstream/master'
brianpopow 39d5a93
Adjustments to changes from the upstream
brianpopow 6963945
Add tests for the AdaptiveThreshold processor
brianpopow 75ac0ee
Remove not needed tmp buffer
brianpopow a468883
Changed startX and endX from ushort to int, Add test with rectangle
brianpopow 1c92670
Using pixel row span to access the pixels
brianpopow 50aa77e
Review changes
brianpopow ee016f6
Minor formatting change
brianpopow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| // Copyright (c) Six Labors and contributors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using SixLabors.ImageSharp.Processing.Processors.Binarization; | ||
|
|
||
| namespace SixLabors.ImageSharp.Processing | ||
| { | ||
| /// <summary> | ||
| /// Extensions to perform AdaptiveThreshold through Mutator. | ||
| /// </summary> | ||
| public static class AdaptiveThresholdExtensions | ||
| { | ||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor()); | ||
|
|
||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, float thresholdLimit) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor(thresholdLimit)); | ||
|
|
||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <param name="upper">Upper (white) color for thresholding.</param> | ||
| /// <param name="lower">Lower (black) color for thresholding.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower)); | ||
|
|
||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <param name="upper">Upper (white) color for thresholding.</param> | ||
| /// <param name="lower">Lower (black) color for thresholding.</param> | ||
| /// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit)); | ||
|
|
||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <param name="upper">Upper (white) color for thresholding.</param> | ||
| /// <param name="lower">Lower (black) color for thresholding</param> | ||
| /// <param name="rectangle">Rectangle region to apply the processor on.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, Rectangle rectangle) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower), rectangle); | ||
|
|
||
| /// <summary> | ||
| /// Applies Bradley Adaptive Threshold to the image. | ||
| /// </summary> | ||
| /// <param name="source">The image this method extends.</param> | ||
| /// <param name="upper">Upper (white) color for thresholding.</param> | ||
| /// <param name="lower">Lower (black) color for thresholding</param> | ||
| /// <param name="thresholdLimit">Threshold limit (0.0-1.0) to consider for binarization.</param> | ||
| /// <param name="rectangle">Rectangle region to apply the processor on.</param> | ||
| /// <returns>The <see cref="Image{TPixel}"/>.</returns> | ||
| public static IImageProcessingContext AdaptiveThreshold(this IImageProcessingContext source, Color upper, Color lower, float thresholdLimit, Rectangle rectangle) | ||
| => source.ApplyProcessor(new AdaptiveThresholdProcessor(upper, lower, thresholdLimit), rectangle); | ||
| } | ||
| } |
77 changes: 77 additions & 0 deletions
77
src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // Copyright (c) Six Labors and contributors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using SixLabors.ImageSharp.PixelFormats; | ||
|
|
||
| namespace SixLabors.ImageSharp.Processing.Processors.Binarization | ||
| { | ||
| /// <summary> | ||
| /// Performs Bradley Adaptive Threshold filter against an image. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Implements "Adaptive Thresholding Using the Integral Image", | ||
| /// see paper: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.7883&rep=rep1&type=pdf | ||
| /// </remarks> | ||
| public class AdaptiveThresholdProcessor : IImageProcessor | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AdaptiveThresholdProcessor"/> class. | ||
| /// </summary> | ||
| public AdaptiveThresholdProcessor() | ||
| : this(Color.White, Color.Black, 0.85f) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AdaptiveThresholdProcessor"/> class. | ||
| /// </summary> | ||
| /// <param name="thresholdLimit">Threshold limit.</param> | ||
| public AdaptiveThresholdProcessor(float thresholdLimit) | ||
| : this(Color.White, Color.Black, thresholdLimit) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AdaptiveThresholdProcessor"/> class. | ||
| /// </summary> | ||
| /// <param name="upper">Color for upper threshold.</param> | ||
| /// <param name="lower">Color for lower threshold.</param> | ||
| public AdaptiveThresholdProcessor(Color upper, Color lower) | ||
| : this(upper, lower, 0.85f) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AdaptiveThresholdProcessor"/> class. | ||
| /// </summary> | ||
| /// <param name="upper">Color for upper threshold.</param> | ||
| /// <param name="lower">Color for lower threshold.</param> | ||
| /// <param name="thresholdLimit">Threshold limit.</param> | ||
| public AdaptiveThresholdProcessor(Color upper, Color lower, float thresholdLimit) | ||
| { | ||
| this.Upper = upper; | ||
| this.Lower = lower; | ||
| this.ThresholdLimit = thresholdLimit; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets upper color limit for thresholding. | ||
| /// </summary> | ||
| public Color Upper { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets lower color limit for threshold. | ||
| /// </summary> | ||
| public Color Lower { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the value for threshold limit. | ||
| /// </summary> | ||
| public float ThresholdLimit { get; set; } | ||
|
|
||
| /// <inheritdoc /> | ||
| public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle) | ||
| where TPixel : unmanaged, IPixel<TPixel> | ||
| => new AdaptiveThresholdProcessor<TPixel>(configuration, this, source, sourceRectangle); | ||
| } | ||
| } |
165 changes: 165 additions & 0 deletions
165
src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| // Copyright (c) Six Labors and contributors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| using SixLabors.ImageSharp.Advanced; | ||
| using SixLabors.ImageSharp.Memory; | ||
| using SixLabors.ImageSharp.PixelFormats; | ||
|
|
||
| namespace SixLabors.ImageSharp.Processing.Processors.Binarization | ||
| { | ||
| /// <summary> | ||
| /// Performs Bradley Adaptive Threshold filter against an image. | ||
| /// </summary> | ||
| internal class AdaptiveThresholdProcessor<TPixel> : ImageProcessor<TPixel> | ||
| where TPixel : unmanaged, IPixel<TPixel> | ||
| { | ||
| private readonly AdaptiveThresholdProcessor definition; | ||
| private readonly PixelOperations<TPixel> pixelOpInstance; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="AdaptiveThresholdProcessor{TPixel}"/> class. | ||
| /// </summary> | ||
| /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> | ||
| /// <param name="definition">The <see cref="AdaptiveThresholdProcessor"/> defining the processor parameters.</param> | ||
| /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> | ||
| /// <param name="sourceRectangle">The source area to process for the current processor instance.</param> | ||
| public AdaptiveThresholdProcessor(Configuration configuration, AdaptiveThresholdProcessor definition, Image<TPixel> source, Rectangle sourceRectangle) | ||
| : base(configuration, source, sourceRectangle) | ||
| { | ||
| this.pixelOpInstance = PixelOperations<TPixel>.Instance; | ||
| this.definition = definition; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| protected override void OnFrameApply(ImageFrame<TPixel> source) | ||
| { | ||
| var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); | ||
|
|
||
| Configuration configuration = this.Configuration; | ||
| TPixel upper = this.definition.Upper.ToPixel<TPixel>(); | ||
| TPixel lower = this.definition.Lower.ToPixel<TPixel>(); | ||
| float thresholdLimit = this.definition.ThresholdLimit; | ||
|
|
||
| int startY = intersect.Y; | ||
| int endY = intersect.Bottom; | ||
| int startX = intersect.X; | ||
| int endX = intersect.Right; | ||
|
|
||
| int width = intersect.Width; | ||
| int height = intersect.Height; | ||
|
|
||
| // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' | ||
| byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); | ||
|
|
||
| // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. | ||
| using (Buffer2D<ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D<ulong>(width, height)) | ||
| { | ||
| // Defines the rectangle section of the image to work on. | ||
| var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); | ||
|
|
||
| Rgba32 rgb = default; | ||
| for (int x = startX; x < endX; x++) | ||
| { | ||
| ulong sum = 0; | ||
| for (int y = startY; y < endY; y++) | ||
| { | ||
| Span<TPixel> row = source.GetPixelRowSpan(y); | ||
| ref TPixel rowRef = ref MemoryMarshal.GetReference(row); | ||
| ref TPixel color = ref Unsafe.Add(ref rowRef, x); | ||
| color.ToRgba32(ref rgb); | ||
|
|
||
| sum += (ulong)(rgb.R + rgb.G + rgb.G); | ||
| if (x - startX != 0) | ||
| { | ||
| intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; | ||
| } | ||
| else | ||
| { | ||
| intImage[x - startX, y - startY] = sum; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var operation = new RowOperation(workingRectangle, source, intImage, upper, lower, thresholdLimit, clusterSize, startX, endX, startY); | ||
| ParallelRowIterator.IterateRows( | ||
| configuration, | ||
| workingRectangle, | ||
| in operation); | ||
| } | ||
| } | ||
|
|
||
| private readonly struct RowOperation : IRowOperation | ||
| { | ||
| private readonly Rectangle bounds; | ||
| private readonly ImageFrame<TPixel> source; | ||
| private readonly Buffer2D<ulong> intImage; | ||
| private readonly TPixel upper; | ||
| private readonly TPixel lower; | ||
| private readonly float thresholdLimit; | ||
| private readonly int startX; | ||
| private readonly int endX; | ||
| private readonly int startY; | ||
| private readonly byte clusterSize; | ||
|
|
||
| [MethodImpl(InliningOptions.ShortMethod)] | ||
| public RowOperation( | ||
| Rectangle bounds, | ||
| ImageFrame<TPixel> source, | ||
| Buffer2D<ulong> intImage, | ||
| TPixel upper, | ||
| TPixel lower, | ||
| float thresholdLimit, | ||
| byte clusterSize, | ||
| int startX, | ||
| int endX, | ||
| int startY) | ||
| { | ||
| this.bounds = bounds; | ||
| this.source = source; | ||
| this.intImage = intImage; | ||
| this.upper = upper; | ||
| this.lower = lower; | ||
| this.thresholdLimit = thresholdLimit; | ||
| this.startX = startX; | ||
| this.endX = endX; | ||
| this.startY = startY; | ||
| this.clusterSize = clusterSize; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| [MethodImpl(InliningOptions.ShortMethod)] | ||
| public void Invoke(int y) | ||
| { | ||
| Rgba32 rgb = default; | ||
| Span<TPixel> pixelRow = this.source.GetPixelRowSpan(y); | ||
|
|
||
| for (int x = this.startX; x < this.endX; x++) | ||
| { | ||
| TPixel pixel = pixelRow[x]; | ||
| pixel.ToRgba32(ref rgb); | ||
|
|
||
| var x1 = (ushort)Math.Max(x - this.startX - this.clusterSize + 1, 0); | ||
|
||
| var x2 = (ushort)Math.Min(x - this.startX + this.clusterSize + 1, this.bounds.Width - 1); | ||
| var y1 = (ushort)Math.Max(y - this.startY - this.clusterSize + 1, 0); | ||
| var y2 = (ushort)Math.Min(y - this.startY + this.clusterSize + 1, this.bounds.Height - 1); | ||
|
|
||
| var count = (uint)((x2 - x1) * (y2 - y1)); | ||
| var sum = (long)Math.Min(this.intImage[x2, y2] - this.intImage[x1, y2] - this.intImage[x2, y1] + this.intImage[x1, y1], long.MaxValue); | ||
|
|
||
| if ((rgb.R + rgb.G + rgb.B) * count <= sum * this.thresholdLimit) | ||
| { | ||
| this.source[x, y] = this.lower; | ||
| } | ||
| else | ||
| { | ||
| this.source[x, y] = this.upper; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.