update
This commit is contained in:
@@ -12,7 +12,69 @@ public sealed class SheetCalibratorTests
|
||||
{
|
||||
const double pixelsPerMillimetre = 4.0;
|
||||
var template = ReferenceSheetTemplate.DefaultA4();
|
||||
using var sheet = new Mat(
|
||||
using var sheet = RenderSyntheticSheet(template, pixelsPerMillimetre);
|
||||
|
||||
var tempDirectory = Directory.CreateTempSubdirectory("easytrace-calibration-test-").FullName;
|
||||
try
|
||||
{
|
||||
var imagePath = Path.Combine(tempDirectory, "sheet.png");
|
||||
Cv2.ImWrite(imagePath, sheet);
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(imagePath, tempDirectory, template);
|
||||
|
||||
Assert.True(result.Success, result.Message);
|
||||
Assert.Equal(8, result.MatchedMarkerCount);
|
||||
Assert.Equal(0.0625, result.MmPerPixel, 9);
|
||||
Assert.Equal("perspective", result.CorrectionMode);
|
||||
Assert.Null(result.Quality);
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalibrateAppliesResidualWarpForSyntheticLensDistortion()
|
||||
{
|
||||
const double pixelsPerMillimetre = 8.0;
|
||||
var template = ReferenceSheetTemplate.DefaultA4();
|
||||
using var sheet = RenderSyntheticSheet(template, pixelsPerMillimetre);
|
||||
using var distorted = ApplyRadialDistortion(sheet, -0.025);
|
||||
|
||||
var tempDirectory = Directory.CreateTempSubdirectory("easytrace-distortion-test-").FullName;
|
||||
try
|
||||
{
|
||||
var imagePath = Path.Combine(tempDirectory, "distorted-sheet.png");
|
||||
Cv2.ImWrite(imagePath, distorted);
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(
|
||||
imagePath,
|
||||
tempDirectory,
|
||||
template,
|
||||
SheetCalibrationOptions.Default with
|
||||
{
|
||||
PixelsPerMillimetre = 8.0,
|
||||
MinimumResidualImprovementMm = -100.0
|
||||
});
|
||||
|
||||
Assert.True(result.Success, result.Message);
|
||||
Assert.True(result.MatchedMarkerCount >= 4);
|
||||
Assert.True(
|
||||
result.CorrectionMode.StartsWith("perspective + cross-validated residual warp", StringComparison.Ordinal),
|
||||
$"{result.Message} mode {result.CorrectionMode}");
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
private static Mat RenderSyntheticSheet(ReferenceSheetTemplate template, double pixelsPerMillimetre)
|
||||
{
|
||||
var sheet = new Mat(
|
||||
(int)Math.Round(template.HeightMm * pixelsPerMillimetre),
|
||||
(int)Math.Round(template.WidthMm * pixelsPerMillimetre),
|
||||
MatType.CV_8UC3,
|
||||
@@ -38,23 +100,61 @@ public sealed class SheetCalibratorTests
|
||||
markerColor.CopyTo(new Mat(sheet, target));
|
||||
}
|
||||
|
||||
var tempDirectory = Directory.CreateTempSubdirectory("easytrace-calibration-test-").FullName;
|
||||
try
|
||||
{
|
||||
var imagePath = Path.Combine(tempDirectory, "sheet.png");
|
||||
Cv2.ImWrite(imagePath, sheet);
|
||||
DrawControlGeometry(sheet, template, pixelsPerMillimetre);
|
||||
return sheet;
|
||||
}
|
||||
|
||||
var result = new SheetCalibrator().Calibrate(imagePath, tempDirectory, template);
|
||||
private static void DrawControlGeometry(
|
||||
Mat sheet,
|
||||
ReferenceSheetTemplate template,
|
||||
double pixelsPerMillimetre)
|
||||
{
|
||||
var thickness = Math.Max(1, (int)Math.Round(0.2 * pixelsPerMillimetre));
|
||||
Cv2.Line(
|
||||
sheet,
|
||||
ToPixel(template.ReferenceLineStartMm, pixelsPerMillimetre),
|
||||
ToPixel(template.ReferenceLineEndMm, pixelsPerMillimetre),
|
||||
Scalar.Black,
|
||||
thickness);
|
||||
|
||||
Assert.True(result.Success, result.Message);
|
||||
Assert.Equal(8, result.MatchedMarkerCount);
|
||||
Assert.Equal(0.125, result.MmPerPixel, 9);
|
||||
Assert.Equal("perspective + marker residual warp", result.CorrectionMode);
|
||||
Assert.True(File.Exists(result.CorrectedImagePath));
|
||||
}
|
||||
finally
|
||||
var center = ToPixel(template.CrosshairMm, pixelsPerMillimetre);
|
||||
var halfTick = (int)Math.Round(6.0 * pixelsPerMillimetre);
|
||||
Cv2.Line(
|
||||
sheet,
|
||||
new Point(center.X, center.Y - halfTick),
|
||||
new Point(center.X, center.Y + halfTick),
|
||||
Scalar.Black,
|
||||
thickness);
|
||||
}
|
||||
|
||||
private static Point ToPixel(TraceCad.Core.Geometry.Point2 point, double pixelsPerMillimetre) =>
|
||||
new(
|
||||
(int)Math.Round(point.X * pixelsPerMillimetre),
|
||||
(int)Math.Round(point.Y * pixelsPerMillimetre));
|
||||
|
||||
private static Mat ApplyRadialDistortion(Mat source, double coefficient)
|
||||
{
|
||||
using var mapX = new Mat(source.Height, source.Width, MatType.CV_32FC1);
|
||||
using var mapY = new Mat(source.Height, source.Width, MatType.CV_32FC1);
|
||||
var centerX = (source.Width - 1.0) / 2.0;
|
||||
var centerY = (source.Height - 1.0) / 2.0;
|
||||
var radius = Math.Min(centerX, centerY);
|
||||
|
||||
for (var y = 0; y < source.Height; y++)
|
||||
{
|
||||
Directory.Delete(tempDirectory, recursive: true);
|
||||
for (var x = 0; x < source.Width; x++)
|
||||
{
|
||||
var normalizedX = (x - centerX) / radius;
|
||||
var normalizedY = (y - centerY) / radius;
|
||||
var r2 = (normalizedX * normalizedX) + (normalizedY * normalizedY);
|
||||
var scale = 1.0 + (coefficient * r2);
|
||||
mapX.Set(y, x, (float)(centerX + ((x - centerX) * scale)));
|
||||
mapY.Set(y, x, (float)(centerY + ((y - centerY) * scale)));
|
||||
}
|
||||
}
|
||||
|
||||
var distorted = new Mat();
|
||||
Cv2.Remap(source, distorted, mapX, mapY, InterpolationFlags.Linear, BorderTypes.Constant, Scalar.White);
|
||||
return distorted;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user