public override int HitTest(PointF point, long currentTimestamp, DistortionHelper distorter, IImageToViewportTransformer transformer, bool zooming) { // Convention: miss = -1, object = 0, handle = n. if (!Visible) { return(-1); } int result = -1; if (HitTester.HitTest(points["0"], point, transformer)) { return(1); } if (showGrid || showGraduations || showAxis) { CoordinateSystemGrid grid = CalibrationHelper.GetCoordinateSystemGrid(); if (grid == null) { return(-1); } if (grid.HorizontalAxis != null && IsPointOnRectifiedLine(point, grid.HorizontalAxis.Start, grid.HorizontalAxis.End, distorter, transformer)) { result = 2; } else if (grid.VerticalAxis != null && IsPointOnRectifiedLine(point, grid.VerticalAxis.Start, grid.VerticalAxis.End, distorter, transformer)) { result = 3; } } return(result); }
private static void ExportImage(CalibrationHelper calibrationHelper, CoordinateSystemGrid grid) { //Bitmap bitmap = new Bitmap((int)(calibrationHelper.ImageSize.Width + 100), (int)(calibrationHelper.ImageSize.Height + 100), PixelFormat.Format24bppRgb); Bitmap bitmap = new Bitmap((int)(calibrationHelper.ImageSize.Width), (int)(calibrationHelper.ImageSize.Height), PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(bitmap); g.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height); if (grid.VerticalAxis != null) { g.DrawLine(Pens.Blue, grid.VerticalAxis.Start, grid.VerticalAxis.End); } if (grid.HorizontalAxis != null) { g.DrawLine(Pens.Blue, grid.HorizontalAxis.Start, grid.HorizontalAxis.End); } foreach (GridLine line in grid.GridLines) { g.DrawLine(Pens.Red, line.Start, line.End); } string filename = GetRandomString(10); bitmap.Save(Path.Combine(@"C:\Users\Joan\Videos\Kinovea\Video Testing\Projective\infinite plane\rnd", string.Format("{0}.png", filename))); }
private void ComputeCoordinateSystemGrid() { if (!initialized) { return; } coordinateSystemGrid = CoordinateSystemGridFinder.Find(this); }
private static CoordinateSystemGrid FindForLineCalibration(CalibrationHelper calibrationHelper) { CoordinateSystemGrid grid = new CoordinateSystemGrid(); RectangleF imageBounds = new RectangleF(PointF.Empty, calibrationHelper.ImageSize); // The clip window is an inflated version of the image to account for distortion. RectangleF clipWindow = imageBounds.CenteredScale(1.3f); // Create a fake plane to act as the user-defined projected plane. QuadrilateralF quadImage = new QuadrilateralF(imageBounds.Deflate(2.0f)); PointF a = calibrationHelper.GetPointFromRectified(quadImage.A); PointF b = calibrationHelper.GetPointFromRectified(quadImage.B); PointF d = calibrationHelper.GetPointFromRectified(quadImage.D); RectangleF plane = new RectangleF(0, 0, b.X - a.X, a.Y - d.Y); // Define the extended plane (for vanishing point replacement and drawing stop condition) as the reprojection of the whole image. QuadrilateralF extendedPlane = ReprojectImageBounds(calibrationHelper, new QuadrilateralF(imageBounds)); CalibrationPlane calibrator = new CalibrationPlane(); calibrator.Initialize(plane.Size, quadImage); PointF originImage = calibrationHelper.GetOrigin(); PointF originRectified = originImage; if (calibrationHelper.DistortionHelper != null && calibrationHelper.DistortionHelper.Initialized) { originRectified = calibrationHelper.DistortionHelper.Undistort(originImage); } calibrator.SetOrigin(originRectified); // From this point on we are mostly in the same situation as for plane calibration. // stepping size is the same in both directions. int targetSteps = 15; float width = extendedPlane.B.X - extendedPlane.A.X; float step = RangeHelper.FindUsableStepSize(width, targetSteps); CreateVerticalGridLines(grid, 0, -step, calibrator, clipWindow, plane, extendedPlane, true, false, PointF.Empty); CreateVerticalGridLines(grid, step, step, calibrator, clipWindow, plane, extendedPlane, true, false, PointF.Empty); CreateHorizontalGridLines(grid, 0, -step, calibrator, clipWindow, plane, extendedPlane, true, false, PointF.Empty); CreateHorizontalGridLines(grid, step, step, calibrator, clipWindow, plane, extendedPlane, true, false, PointF.Empty); return(grid); }
public override void Draw(Graphics canvas, DistortionHelper distorter, IImageToViewportTransformer transformer, bool selected, long currentTimestamp) { if (!Visible || CalibrationHelper == null) { return; } if (CalibrationHelper.CalibratorType == CalibratorType.Plane && !CalibrationHelper.CalibrationByPlane_IsValid()) { return; } CoordinateSystemGrid grid = CalibrationHelper.GetCoordinateSystemGrid(); using (Pen penLine = styleHelper.GetBackgroundPen(255)) { DrawGrid(canvas, distorter, transformer, grid); } }
private void DrawGrid(Graphics canvas, DistortionHelper distorter, IImageToViewportTransformer transformer, CoordinateSystemGrid grid) { Pen p = styleHelper.GetBackgroundPen(gridAlpha); p.DashStyle = DashStyle.Solid; p.Width = 2; if (grid.VerticalAxis != null) { DrawGridLine(canvas, distorter, transformer, p, grid.VerticalAxis.Start, grid.VerticalAxis.End); } if (grid.HorizontalAxis != null) { DrawGridLine(canvas, distorter, transformer, p, grid.HorizontalAxis.Start, grid.HorizontalAxis.End); } p.DashStyle = DashStyle.Dash; p.Width = 1; foreach (GridLine line in grid.GridLines) { DrawGridLine(canvas, distorter, transformer, p, line.Start, line.End); } SolidBrush brushFill = styleHelper.GetBackgroundBrush(defaultBackgroundAlpha); SolidBrush fontBrush = styleHelper.GetForegroundBrush(255); Font font = styleHelper.GetFont(1.0F); foreach (TickMark tick in grid.TickMarks) { DrawTickMark(canvas, distorter, transformer, tick, brushFill, fontBrush, font); } font.Dispose(); fontBrush.Dispose(); brushFill.Dispose(); p.Dispose(); }
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; } }