private static CoordinateSystemGrid FindForPlaneCalibration(CalibrationHelper calibrationHelper) { CoordinateSystemGrid grid = new CoordinateSystemGrid(); RectangleF imageBounds = new RectangleF(PointF.Empty, calibrationHelper.ImageSize); RectangleF clipWindow = imageBounds; CalibrationPlane calibrator = calibrationHelper.CalibrationByPlane_GetCalibrator(); RectangleF plane = new RectangleF(PointF.Empty, calibrator.Size); int targetSteps = 15; float stepVertical = 1.0f; float stepHorizontal = 1.0f; // The extended plane is used for vanishing point replacement and iteration stop condition. QuadrilateralF extendedPlane; bool orthogonal; if (!calibrator.QuadImage.IsRectangle) { // If perspective plane, define as 2n times the nominal plane centered on origin. orthogonal = false; float n = 4; PointF a = new PointF(-calibrator.Size.Width * n, calibrator.Size.Height * n); PointF b = new PointF(calibrator.Size.Width * n, calibrator.Size.Height * n); PointF c = new PointF(calibrator.Size.Width * n, -calibrator.Size.Height * n); PointF d = new PointF(-calibrator.Size.Width * n, -calibrator.Size.Height * n); extendedPlane = new QuadrilateralF(a, b, c, d); QuadrilateralF quadImage = calibrator.QuadImage; float projectedWidthLength = GeometryHelper.GetDistance(quadImage.A, quadImage.B); float scaledTargetHorizontal = targetSteps / (calibrationHelper.ImageSize.Width / projectedWidthLength); stepHorizontal = RangeHelper.FindUsableStepSize(plane.Width, scaledTargetHorizontal); float projectedHeightLength = GeometryHelper.GetDistance(quadImage.A, quadImage.D); float scaledTargetVertical = targetSteps / (calibrationHelper.ImageSize.Height / projectedHeightLength); stepVertical = RangeHelper.FindUsableStepSize(plane.Height, scaledTargetVertical); } else { // If flat plane (and no distortion) we know there is no way to get any vanishing point inside the image, // so we can safely use the whole image reprojection as an extended plane. orthogonal = true; QuadrilateralF quadImageBounds = new QuadrilateralF(imageBounds); PointF a = calibrationHelper.GetPointFromRectified(quadImageBounds.A); PointF b = calibrationHelper.GetPointFromRectified(quadImageBounds.B); PointF c = calibrationHelper.GetPointFromRectified(quadImageBounds.C); PointF d = calibrationHelper.GetPointFromRectified(quadImageBounds.D); extendedPlane = new QuadrilateralF(a, b, c, d); float width = extendedPlane.B.X - extendedPlane.A.X; stepHorizontal = RangeHelper.FindUsableStepSize(width, targetSteps); float height = extendedPlane.A.Y - extendedPlane.D.Y; stepVertical = RangeHelper.FindUsableStepSize(height, targetSteps); } //------------------------------------------------------------------------------------------------- // There is a complication with points behind the camera, as they projects above the vanishing line. // The general strategy is the following: // Find out if the vanishing point is inside the image. Reminder: parallel lines share the same vanishing point. // If it is not inside the image, there is no risk, so we take two points on the line and draw an infinite line that we clip against the image bounds. // If it is inside the image: // Take two points on the line and project them in image space. They are colinear with the vanishing point in image space. // Find on which side of the quadrilateral the vanishing point is. // If the vanishing point is above the quad, draw a ray from the top of the extended quad, down to infinity. // If the vanishing point is below the quad, draw a ray from the bottom of the extended quad, up to infinity. // // Stepping strategy: // We start at origin and progress horizontally and vertically until we find a gridline that is completely clipped out. //------------------------------------------------------------------------------------------------- // Vertical lines. PointF yVanish = new PointF(0, float.MinValue); bool yVanishVisible = false; Vector3 yv = calibrator.Project(new Vector3(0, 1, 0)); if (yv.Z != 0) { yVanish = new PointF(yv.X / yv.Z, yv.Y / yv.Z); yVanishVisible = clipWindow.Contains(yVanish); } CreateVerticalGridLines(grid, 0, -stepHorizontal, calibrator, clipWindow, plane, extendedPlane, orthogonal, yVanishVisible, yVanish); CreateVerticalGridLines(grid, stepHorizontal, stepHorizontal, calibrator, clipWindow, plane, extendedPlane, orthogonal, yVanishVisible, yVanish); // Horizontal lines PointF xVanish = new PointF(float.MinValue, 0); bool xVanishVisible = false; Vector3 xv = calibrator.Project(new Vector3(1, 0, 0)); if (xv.Z != 0) { xVanish = new PointF(xv.X / xv.Z, xv.Y / xv.Z); xVanishVisible = clipWindow.Contains(xVanish); } CreateHorizontalGridLines(grid, 0, -stepVertical, calibrator, clipWindow, plane, extendedPlane, orthogonal, xVanishVisible, xVanish); CreateHorizontalGridLines(grid, stepVertical, stepVertical, calibrator, clipWindow, plane, extendedPlane, orthogonal, xVanishVisible, xVanish); return(grid); }
private static void CreateHorizontalGridLines(CoordinateSystemGrid grid, float start, float step, CalibrationPlane calibrator, RectangleF clipWindow, RectangleF plane, QuadrilateralF extendedPlane, bool orthogonal, bool vanishVisible, PointF vanish) { // Progress from origin to the side until grid lines are no longer visible when projected on image. float y = start; bool partlyVisible = true; while (partlyVisible && y >= extendedPlane.D.Y && y <= extendedPlane.A.Y) { Vector3 pa = calibrator.Project(new PointF(0, y)); Vector3 pb = calibrator.Project(new PointF(plane.Width, y)); // Discard line if one of the points is behind the camera. if (pa.Z < 0 || pb.Z < 0) { y += step; continue; } PointF a = new PointF(pa.X / pa.Z, pa.Y / pa.Z); PointF b = new PointF(pb.X / pb.Z, pb.Y / pb.Z); ClipResult result; if (vanishVisible) { float scale = (b.X - a.X) / (vanish.X - a.X); if (scale > 0) { // Vanishing point is after b. PointF vb = calibrator.Untransform(new PointF(extendedPlane.B.X, y)); result = LiangBarsky.ClipLine(clipWindow, a, vb, double.MinValue, 1); } else { // Vanishing point is before a. PointF va = calibrator.Untransform(new PointF(extendedPlane.A.X, y)); result = LiangBarsky.ClipLine(clipWindow, va, b, 0, double.MaxValue); } } else { result = LiangBarsky.ClipLine(clipWindow, a, b); } partlyVisible = result.Visible; if (partlyVisible) { if (y == 0) { grid.HorizontalAxis = new GridAxis(result.A, result.B); } else { grid.GridLines.Add(new GridLine(result.A, result.B)); if (clipWindow.Contains(a) && (orthogonal || TickMarkVisible((int)(y / step)))) { grid.TickMarks.Add(new TickMark(y, a, TextAlignment.Left)); } } } y += step; } }