33
44using System ;
55using System . Runtime . CompilerServices ;
6- using System . Runtime . InteropServices ;
76using SixLabors . ImageSharp . Advanced ;
87using SixLabors . ImageSharp . Memory ;
98using SixLabors . ImageSharp . PixelFormats ;
@@ -27,133 +26,92 @@ internal class AdaptiveThresholdProcessor<TPixel> : ImageProcessor<TPixel>
2726 /// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
2827 public AdaptiveThresholdProcessor ( Configuration configuration , AdaptiveThresholdProcessor definition , Image < TPixel > source , Rectangle sourceRectangle )
2928 : base ( configuration , source , sourceRectangle )
30- {
31- this . definition = definition ;
32- }
29+ => this . definition = definition ;
3330
3431 /// <inheritdoc/>
3532 protected override void OnFrameApply ( ImageFrame < TPixel > source )
3633 {
37- var intersect = Rectangle . Intersect ( this . SourceRectangle , source . Bounds ( ) ) ;
34+ var interest = Rectangle . Intersect ( this . SourceRectangle , source . Bounds ( ) ) ;
3835
3936 Configuration configuration = this . Configuration ;
4037 TPixel upper = this . definition . Upper . ToPixel < TPixel > ( ) ;
4138 TPixel lower = this . definition . Lower . ToPixel < TPixel > ( ) ;
4239 float thresholdLimit = this . definition . ThresholdLimit ;
4340
44- int startY = intersect . Y ;
45- int endY = intersect . Bottom ;
46- int startX = intersect . X ;
47- int endX = intersect . Right ;
48-
49- int width = intersect . Width ;
50- int height = intersect . Height ;
51-
52- // 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'
53- byte clusterSize = ( byte ) Math . Truncate ( ( width / 16f ) - 1 ) ;
41+ // ClusterSize defines the size of cluster to used to check for average.
42+ // Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1'
43+ byte clusterSize = ( byte ) Math . Clamp ( interest . Width / 16F , 0 , 255 ) ;
5444
55- Buffer2D < TPixel > sourceBuffer = source . PixelBuffer ;
56-
57- // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data.
58- using ( Buffer2D < ulong > intImage = this . Configuration . MemoryAllocator . Allocate2D < ulong > ( width , height ) )
59- {
60- Rgba32 rgb = default ;
61- for ( int x = startX ; x < endX ; x ++ )
62- {
63- ulong sum = 0 ;
64- for ( int y = startY ; y < endY ; y ++ )
65- {
66- Span < TPixel > row = sourceBuffer . DangerousGetRowSpan ( y ) ;
67- ref TPixel rowRef = ref MemoryMarshal . GetReference ( row ) ;
68- ref TPixel color = ref Unsafe . Add ( ref rowRef , x ) ;
69- color . ToRgba32 ( ref rgb ) ;
70-
71- sum += ( ulong ) ( rgb . R + rgb . G + rgb . B ) ;
72-
73- if ( x - startX != 0 )
74- {
75- intImage [ x - startX , y - startY ] = intImage [ x - startX - 1 , y - startY ] + sum ;
76- }
77- else
78- {
79- intImage [ x - startX , y - startY ] = sum ;
80- }
81- }
82- }
83-
84- var operation = new RowOperation ( intersect , source . PixelBuffer , intImage , upper , lower , thresholdLimit , clusterSize , startX , endX , startY ) ;
85- ParallelRowIterator . IterateRows (
86- configuration ,
87- intersect ,
88- in operation ) ;
89- }
45+ using Buffer2D < ulong > intImage = source . CalculateIntegralImage ( interest ) ;
46+ RowOperation operation = new ( configuration , interest , source . PixelBuffer , intImage , upper , lower , thresholdLimit , clusterSize ) ;
47+ ParallelRowIterator . IterateRows < RowOperation , L8 > (
48+ configuration ,
49+ interest ,
50+ in operation ) ;
9051 }
9152
92- private readonly struct RowOperation : IRowOperation
53+ private readonly struct RowOperation : IRowOperation < L8 >
9354 {
55+ private readonly Configuration configuration ;
9456 private readonly Rectangle bounds ;
9557 private readonly Buffer2D < TPixel > source ;
9658 private readonly Buffer2D < ulong > intImage ;
9759 private readonly TPixel upper ;
9860 private readonly TPixel lower ;
9961 private readonly float thresholdLimit ;
10062 private readonly int startX ;
101- private readonly int endX ;
10263 private readonly int startY ;
10364 private readonly byte clusterSize ;
10465
10566 [ MethodImpl ( InliningOptions . ShortMethod ) ]
10667 public RowOperation (
68+ Configuration configuration ,
10769 Rectangle bounds ,
10870 Buffer2D < TPixel > source ,
10971 Buffer2D < ulong > intImage ,
11072 TPixel upper ,
11173 TPixel lower ,
11274 float thresholdLimit ,
113- byte clusterSize ,
114- int startX ,
115- int endX ,
116- int startY )
75+ byte clusterSize )
11776 {
77+ this . configuration = configuration ;
11878 this . bounds = bounds ;
79+ this . startX = bounds . X ;
80+ this . startY = bounds . Y ;
11981 this . source = source ;
12082 this . intImage = intImage ;
12183 this . upper = upper ;
12284 this . lower = lower ;
12385 this . thresholdLimit = thresholdLimit ;
124- this . startX = startX ;
125- this . endX = endX ;
126- this . startY = startY ;
12786 this . clusterSize = clusterSize ;
12887 }
12988
13089 /// <inheritdoc/>
13190 [ MethodImpl ( InliningOptions . ShortMethod ) ]
132- public void Invoke ( int y )
91+ public void Invoke ( int y , Span < L8 > span )
13392 {
134- Rgba32 rgb = default ;
135- Span < TPixel > pixelRow = this . source . DangerousGetRowSpan ( y ) ;
93+ Span < TPixel > rowSpan = this . source . DangerousGetRowSpan ( y ) . Slice ( this . startX , span . Length ) ;
94+ PixelOperations < TPixel > . Instance . ToL8 ( this . configuration , rowSpan , span ) ;
13695
137- for ( int x = this . startX ; x < this . endX ; x ++ )
96+ int maxX = this . bounds . Width - 1 ;
97+ int maxY = this . bounds . Height - 1 ;
98+ for ( int x = 0 ; x < rowSpan . Length ; x ++ )
13899 {
139- TPixel pixel = pixelRow [ x ] ;
140- pixel . ToRgba32 ( ref rgb ) ;
141-
142- var x1 = Math . Max ( x - this . startX - this . clusterSize + 1 , 0 ) ;
143- var x2 = Math . Min ( x - this . startX + this . clusterSize + 1 , this . bounds . Width - 1 ) ;
144- var y1 = Math . Max ( y - this . startY - this . clusterSize + 1 , 0 ) ;
145- var y2 = Math . Min ( y - this . startY + this . clusterSize + 1 , this . bounds . Height - 1 ) ;
100+ int x1 = Math . Clamp ( x - this . clusterSize + 1 , 0 , maxX ) ;
101+ int x2 = Math . Min ( x + this . clusterSize + 1 , maxX ) ;
102+ int y1 = Math . Clamp ( y - this . startY - this . clusterSize + 1 , 0 , maxY ) ;
103+ int y2 = Math . Min ( y - this . startY + this . clusterSize + 1 , maxY ) ;
146104
147- var count = ( uint ) ( ( x2 - x1 ) * ( y2 - y1 ) ) ;
148- var sum = ( long ) Math . Min ( this . intImage [ x2 , y2 ] - this . intImage [ x1 , y2 ] - this . intImage [ x2 , y1 ] + this . intImage [ x1 , y1 ] , long . MaxValue ) ;
105+ uint count = ( uint ) ( ( x2 - x1 ) * ( y2 - y1 ) ) ;
106+ ulong sum = Math . Min ( this . intImage [ x2 , y2 ] - this . intImage [ x1 , y2 ] - this . intImage [ x2 , y1 ] + this . intImage [ x1 , y1 ] , ulong . MaxValue ) ;
149107
150- if ( ( rgb . R + rgb . G + rgb . B ) * count <= sum * this . thresholdLimit )
108+ if ( span [ x ] . PackedValue * count <= sum * this . thresholdLimit )
151109 {
152- this . source [ x , y ] = this . lower ;
110+ rowSpan [ x ] = this . lower ;
153111 }
154112 else
155113 {
156- this . source [ x , y ] = this . upper ;
114+ rowSpan [ x ] = this . upper ;
157115 }
158116 }
159117 }
0 commit comments