Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions PhotoLocator/BitmapOperations/FloatBitmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ public BitmapSource ToBitmapSource(double dpiX, double dpiY, double gamma, Pixel

private byte[] ToPixels8(double gamma)
{
if (Elements is null)
throw new InvalidOperationException("Bitmap not initialized");
var pixels = ArrayPool<byte>.Shared.Rent(Height * Stride);
var gammaLut = CreateGammaLookupFloatToByte(gamma);
unsafe
Expand Down
144 changes: 114 additions & 30 deletions PhotoLocator/BitmapOperations/IIRMinMaxOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,59 @@ static public void MinFilter(FloatBitmap plane, float filterSize)
}
});
// Vertical
Parallel.For(0, plane.Stride, x =>
int quarterWidth = plane.Stride / 4;
Parallel.For(0, quarterWidth, x4 =>
{
int x = x4 * 4;
var width = plane.Stride;
var height = plane.Height;
fixed (float* pixels = plane.Elements)
{
var pix0 = &pixels[x];
var prev0 = *pix0;
var pix1 = pix0 + 1;
var prev1 = *pix1;
var pix2 = pix0 + 2;
var prev2 = *pix2;
var pix3 = pix0 + 3;
var prev3 = *pix3;
for (var y = 1; y < height; y++)
{
pix0 += width;
if (prev0 < *pix0) { prev0 = (prev0 * filterSize + *pix0) * scale; *pix0 = prev0; }
else prev0 = *pix0;
pix1 += width;
if (prev1 < *pix1) { prev1 = (prev1 * filterSize + *pix1) * scale; *pix1 = prev1; }
else prev1 = *pix1;
pix2 += width;
if (prev2 < *pix2) { prev2 = (prev2 * filterSize + *pix2) * scale; *pix2 = prev2; }
else prev2 = *pix2;
pix3 += width;
if (prev3 < *pix3) { prev3 = (prev3 * filterSize + *pix3) * scale; *pix3 = prev3; }
else prev3 = *pix3;
}
prev0 = *pix0;
prev1 = *pix1;
prev2 = *pix2;
prev3 = *pix3;
for (var y = 1; y < height; y++)
{
pix0 -= width;
if (prev0 < *pix0) { prev0 = (prev0 * filterSize + *pix0) * scale; *pix0 = prev0; }
else prev0 = *pix0;
pix1 -= width;
if (prev1 < *pix1) { prev1 = (prev1 * filterSize + *pix1) * scale; *pix1 = prev1; }
else prev1 = *pix1;
pix2 -= width;
if (prev2 < *pix2) { prev2 = (prev2 * filterSize + *pix2) * scale; *pix2 = prev2; }
else prev2 = *pix2;
pix3 -= width;
if (prev3 < *pix3) { prev3 = (prev3 * filterSize + *pix3) * scale; *pix3 = prev3; }
else prev3 = *pix3;
}
}
});
Parallel.For(quarterWidth * 4, plane.Stride, x =>
{
var width = plane.Stride;
var height = plane.Height;
Expand All @@ -59,25 +111,15 @@ static public void MinFilter(FloatBitmap plane, float filterSize)
for (var y = 1; y < height; y++)
{
pix += width;
if (prev < *pix)
{
prev = (prev * filterSize + *pix) * scale;
*pix = prev;
}
else
prev = *pix;
if (prev < *pix) { prev = (prev * filterSize + *pix) * scale; *pix = prev; }
else prev = *pix;
}
prev = *pix;
for (var y = 1; y < height; y++)
{
pix -= width;
if (prev < *pix)
{
prev = (prev * filterSize + *pix) * scale;
*pix = prev;
}
else
prev = *pix;
if (prev < *pix) { prev = (prev * filterSize + *pix) * scale; *pix = prev; }
else prev = *pix;
}
}
});
Expand Down Expand Up @@ -129,7 +171,59 @@ static public void MaxFilter(FloatBitmap plane, float filterSize)
}
});
// Vertical
Parallel.For(0, plane.Stride, x =>
int quarterWidth = plane.Stride / 4;
Parallel.For(0, quarterWidth, x4 =>
{
int x = x4 * 4;
var width = plane.Stride;
var height = plane.Height;
fixed (float* pixels = plane.Elements)
{
var pix0 = &pixels[x];
var prev0 = *pix0;
var pix1 = pix0 + 1;
var prev1 = *pix1;
var pix2 = pix0 + 2;
var prev2 = *pix2;
var pix3 = pix0 + 3;
var prev3 = *pix3;
for (var y = 1; y < height; y++)
{
pix0 += width;
if (prev0 > *pix0) { prev0 = (prev0 * filterSize + *pix0) * scale; *pix0 = prev0; }
else prev0 = *pix0;
pix1 += width;
if (prev1 > *pix1) { prev1 = (prev1 * filterSize + *pix1) * scale; *pix1 = prev1; }
else prev1 = *pix1;
pix2 += width;
if (prev2 > *pix2) { prev2 = (prev2 * filterSize + *pix2) * scale; *pix2 = prev2; }
else prev2 = *pix2;
pix3 += width;
if (prev3 > *pix3) { prev3 = (prev3 * filterSize + *pix3) * scale; *pix3 = prev3; }
else prev3 = *pix3;
}
prev0 = *pix0;
prev1 = *pix1;
prev2 = *pix2;
prev3 = *pix3;
for (var y = 1; y < height; y++)
{
pix0 -= width;
if (prev0 > *pix0) { prev0 = (prev0 * filterSize + *pix0) * scale; *pix0 = prev0; }
else prev0 = *pix0;
pix1 -= width;
if (prev1 > *pix1) { prev1 = (prev1 * filterSize + *pix1) * scale; *pix1 = prev1; }
else prev1 = *pix1;
pix2 -= width;
if (prev2 > *pix2) { prev2 = (prev2 * filterSize + *pix2) * scale; *pix2 = prev2; }
else prev2 = *pix2;
pix3 -= width;
if (prev3 > *pix3) { prev3 = (prev3 * filterSize + *pix3) * scale; *pix3 = prev3; }
else prev3 = *pix3;
}
}
});
Parallel.For(quarterWidth * 4, plane.Stride, x =>
{
var width = plane.Stride;
var height = plane.Height;
Expand All @@ -140,25 +234,15 @@ static public void MaxFilter(FloatBitmap plane, float filterSize)
for (var y = 1; y < height; y++)
{
pix += width;
if (prev > *pix)
{
prev = (prev * filterSize + *pix) * scale;
*pix = prev;
}
else
prev = *pix;
if (prev > *pix) { prev = (prev * filterSize + *pix) * scale; *pix = prev; }
else prev = *pix;
}
prev = *pix;
for (var y = 1; y < height; y++)
{
pix -= width;
if (prev > *pix)
{
prev = (prev * filterSize + *pix) * scale;
*pix = prev;
}
else
prev = *pix;
if (prev > *pix) { prev = (prev * filterSize + *pix) * scale; *pix = prev; }
else prev = *pix;
}
}
});
Expand Down
59 changes: 47 additions & 12 deletions PhotoLocator/BitmapOperations/IIRSmoothOperation.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Numerics;

namespace PhotoLocator.BitmapOperations
{
Expand All @@ -12,47 +15,79 @@ public static void Apply(FloatBitmap plane, float filterSize)
Debug.Assert(plane.PlaneCount == 1);
filterSize /= 4f;
var scale = 1f / (1f + filterSize);
var height = plane.Height;
unsafe
{
// Horizontal smooth
Parallel.For(0, plane.Height, y =>
Parallel.For(0, height, y =>
{
var width = plane.Stride;
var stride = plane.Stride;
fixed (float* pixels = plane.Elements)
{
var pix = &pixels[y * width];
var pix = &pixels[y * stride];
var value = *pix;
for (var x = width; x > 1; x--)
for (var x = stride; x > 1; x--)
{
pix++;
value = (value * filterSize + *pix) * scale;
*pix = value;
}
for (var x = width; x > 1; x--)
for (var x = stride; x > 1; x--)
{
pix--;
value = (value * filterSize + *pix) * scale;
*pix = value;
}
}
});
// Vertical smooth
Parallel.For(0, plane.Stride, x =>

// Vertical smooth - vectorized over columns when possible
int vectorSize = Vector<float>.Count;
int vectorizedSegments = plane.Stride / vectorSize;
var filterSizeV = Vector.Create(filterSize);
var scaleV = Vector.Create(scale);
Parallel.For(0, vectorizedSegments, vi =>
{
int x = vi * vectorSize;
var stride = plane.Stride;
fixed (float* pixels = plane.Elements)
{
float* colPtr = &pixels[x];
var v = Vector.Load(colPtr);
for (var y = height; y > 1; y--)
{
colPtr += stride;
var inV = Vector.Load(colPtr);
v = Vector.Multiply(Vector.Add(Vector.Multiply(v, filterSizeV), inV), scaleV);
v.Store(colPtr);
}
for (var y = height; y > 1; y--)
{
colPtr -= stride;
var inV = Vector.Load(colPtr);
v = Vector.Multiply(Vector.Add(Vector.Multiply(v, filterSizeV), inV), scaleV);
v.Store(colPtr);
}
}
});

// Remaining columns - columns not divisible by vectorSize
Parallel.For(vectorizedSegments * vectorSize, plane.Stride, x =>
{
var width = plane.Stride;
var stride = plane.Stride;
fixed (float* pixels = plane.Elements)
{
var pix = &pixels[x];
var value = *pix;
for (var y = plane.Height; y > 1; y--)
for (var y = height; y > 1; y--)
{
pix += width;
pix += stride;
value = (value * filterSize + *pix) * scale;
*pix = value;
}
for (var y = plane.Height; y > 1; y--)
for (var y = height; y > 1; y--)
{
pix -= width;
pix -= stride;
value = (value * filterSize + *pix) * scale;
*pix = value;
}
Expand Down
33 changes: 33 additions & 0 deletions PhotoLocatorTest/BenchmarkHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Diagnostics;
using System.Linq;

namespace PhotoLocator
{
public static class BenchmarkHelper
{
public static void Run(Action action, int innerLoops = 1, int outerIterations = 5)
{
#if DEBUG
Console.WriteLine("WARNING: Running benchmark in DEBUG mode. Results may not reflect release performance.");
#endif
var iterationTimes = new long[outerIterations];
for (int i = 0; i < outerIterations; i++)
{
GC.Collect();
GC.TryStartNoGCRegion(1024 * 1024 * 100);
var sw = Stopwatch.StartNew();
for (int j = 0; j < innerLoops; j++)
action();
sw.Stop();
Console.WriteLine($"Iteration {i + 1}: {sw.ElapsedMilliseconds} ms");
GC.EndNoGCRegion();
iterationTimes[i] = sw.ElapsedMilliseconds;
}
Comment thread
meesoft marked this conversation as resolved.
var median = iterationTimes.Order().Skip(outerIterations / 2).First();
var min = iterationTimes.Min();
Console.WriteLine($"Median time: {median} ms");
Console.WriteLine($"Minimum time: {min} ms");
throw new AssertInconclusiveException($"Min={min} ms, median={median} ms");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void Apply_IncreaseLocalContrast()
#if DEBUG
GeneralFileFormatHandler.SaveToFile(result, "localContrast.png");
#endif
Assert.AreEqual(0.23394798570186837, op.DstBitmap.Mean(), 1e-5);
}
}
}
Loading