public void ReadLineXml(XmlReader r, PointF scaling)
        {
            r.ReadStartElement();
            float    length = 0;
            SegmentF line   = SegmentF.Empty;

            while (r.NodeType == XmlNodeType.Element)
            {
                switch (r.Name)
                {
                case "Length":
                    length = float.Parse(r.ReadElementContentAsString(), CultureInfo.InvariantCulture);
                    break;

                case "Segment":
                    line = ParseSegment(r, scaling);
                    break;

                case "Origin":
                    // Import from older format.
                    origin = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    if (float.IsNaN(origin.X) || float.IsNaN(origin.Y))
                    {
                        origin = PointF.Empty;
                    }

                    origin = origin.Scale(scaling.X, scaling.Y);
                    break;

                case "Scale":
                    // Import and convert from older format.
                    // Create a fake line of 100 px horizontal at the origin.
                    float  bakedScale   = float.Parse(r.ReadElementContentAsString(), CultureInfo.InvariantCulture);
                    float  lengthPixels = 100;
                    PointF start        = origin;
                    PointF end          = origin.Translate(lengthPixels, 0);
                    line   = new SegmentF(start, end);
                    length = lengthPixels * bakedScale;

                    // The actual origin should be expressed in the calibrated plane coordinate system, which has its true origin at the A point of the quad.
                    origin = new PointF(0, length);
                    break;

                default:
                    string unparsed = r.ReadOuterXml();
                    log.DebugFormat("Unparsed content in KVA XML: {0}", unparsed);
                    break;
                }
            }

            r.ReadEndElement();

            // Update mapping.
            size      = new SizeF(length, length);
            quadImage = MakeQuad(line.Start, line.End);

            mapping.Update(new QuadrilateralF(size.Width, size.Height), quadImage);
            valid       = quadImage.IsConvex;
            initialized = true;
        }
Exemplo n.º 2
0
        public override void Draw(Graphics canvas, DistortionHelper distorter, IImageToViewportTransformer transformer, bool selected, long currentTimestamp)
        {
            double opacityFactor = infosFading.GetOpacityFactor(currentTimestamp);

            if (opacityFactor <= 0)
            {
                return;
            }

            QuadrilateralF quad = transformer.Transform(quadImage);

            int alpha = (int)(opacityFactor * 255);

            using (Pen p = styleHelper.GetPen(alpha, transformer.Scale))
            {
                p.EndCap = LineCap.Square;
                if (styleHelper.PenShape == PenShape.Dash)
                {
                    p.DashStyle = DashStyle.Dash;
                }

                canvas.DrawLine(p, quad.A, quad.B);
                canvas.DrawLine(p, quad.B, quad.C);
                canvas.DrawLine(p, quad.C, quad.D);
                canvas.DrawLine(p, quad.D, quad.A);
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Takes a circle in image space and returns a cooresponding ellipse in image space.
        /// This is an ill posed problem. The center is respected but the radius could be taken
        /// anywhere around the circle in image space, and that yields different radii in world space.
        /// We use the point along the X axis as the radius.
        /// </summary>
        public Ellipse GetEllipseFromCircle(PointF center, float radius, out PointF radiusLeftInImage, out PointF radiusRightInImage)
        {
            radiusLeftInImage  = new PointF(center.X - radius, center.Y);
            radiusRightInImage = new PointF(center.X + radius, center.Y);

            if (calibratorType == CalibratorType.Line)
            {
                return(new Ellipse(center, radius, radius, 0));
            }

            // Rebuild the world-space circle based on center and radius alone.
            PointF centerInWorld = GetPoint(center);

            // Estimate the radius in world space.
            // Get scalar will assumes reference direction to be X-axis in image space.
            float radiusInWorld = GetScalar(radius);

            // Get the intersection points of a horizontal diameter.
            // This is used to draw a line from the center to the outline of the ellipse in perspective.
            radiusLeftInImage  = GetImagePoint(centerInWorld.Translate(-radiusInWorld, 0));
            radiusRightInImage = GetImagePoint(centerInWorld.Translate(radiusInWorld, 0));

            // Get the square enclosing the circle for mapping.
            PointF         a         = GetImagePoint(centerInWorld.Translate(-radiusInWorld, -radiusInWorld));
            PointF         b         = GetImagePoint(centerInWorld.Translate(radiusInWorld, -radiusInWorld));
            PointF         c         = GetImagePoint(centerInWorld.Translate(radiusInWorld, radiusInWorld));
            PointF         d         = GetImagePoint(centerInWorld.Translate(-radiusInWorld, radiusInWorld));
            QuadrilateralF quadImage = new QuadrilateralF(a, b, c, d);

            ProjectiveMapping mapping = new ProjectiveMapping();

            mapping.Update(QuadrilateralF.CenteredUnitSquare, quadImage);
            return(mapping.Ellipse());
        }
Exemplo n.º 4
0
        public DrawingPlane(PointF origin, long timestamp, long averageTimeStampsPerFrame, DrawingStyle preset = null)
        {
            // Decoration
            styleHelper.Color         = Color.Empty;
            styleHelper.GridDivisions = 8;
            styleHelper.Perspective   = true;
            styleHelper.ValueChanged += StyleHelper_ValueChanged;
            if (preset == null)
            {
                preset = ToolManager.GetStylePreset("Plane");
            }

            style = preset.Clone();
            BindStyle();

            infosFading               = new InfosFading(timestamp, averageTimeStampsPerFrame);
            infosFading.UseDefault    = false;
            infosFading.AlwaysVisible = true;

            planeWidth  = 100;
            planeHeight = 100;
            quadPlane   = new QuadrilateralF(planeWidth, planeHeight);

            mnuCalibrate.Click += new EventHandler(mnuCalibrate_Click);
            mnuCalibrate.Image  = Properties.Drawings.linecalibrate;
        }
Exemplo n.º 5
0
        public void ReadPlaneXml(XmlReader r, PointF scale)
        {
            r.ReadStartElement();

            while (r.NodeType == XmlNodeType.Element)
            {
                switch (r.Name)
                {
                case "Size":
                    size = XmlHelper.ParseSizeF(r.ReadElementContentAsString());
                    break;

                case "Quadrilateral":
                    quadImage = ParseQuadrilateral(r, scale);
                    break;

                case "Origin":
                    origin = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    break;

                default:
                    string unparsed = r.ReadOuterXml();
                    log.DebugFormat("Unparsed content in KVA XML: {0}", unparsed);
                    break;
                }
            }

            r.ReadEndElement();

            mapping.Update(new QuadrilateralF(size.Width, size.Height), quadImage);
            valid       = quadImage.IsConvex;
            initialized = true;
        }
Exemplo n.º 6
0
        public void Update(QuadrilateralF plane, QuadrilateralF image)
        {
            double[,] squareToInput  = MapSquareToQuad(plane);
            double[,] squareToOutput = MapSquareToQuad(image);

            if (squareToOutput == null)
            {
                return;
            }

            mapMatrix   = MultiplyMatrix(squareToOutput, AdjugateMatrix(squareToInput));
            unmapMatrix = AdjugateMatrix(mapMatrix);

            Vector3 row0 = new Vector3((float)mapMatrix[0, 0], (float)mapMatrix[0, 1], (float)mapMatrix[0, 2]);
            Vector3 row1 = new Vector3((float)mapMatrix[1, 0], (float)mapMatrix[1, 1], (float)mapMatrix[1, 2]);
            Vector3 row2 = new Vector3((float)mapMatrix[2, 0], (float)mapMatrix[2, 1], (float)mapMatrix[2, 2]);

            matrix = Matrix3x3.CreateFromRows(row0, row1, row2);

            adjugate = matrix.Adjugate();

            try
            {
                inverse = matrix.Inverse();
            }
            catch
            {
                // Singular matrix.
                inverse = Matrix3x3.Identity;
            }
        }
        public void CalibrationByPlane_Initialize(Guid id, SizeF size, QuadrilateralF quadImage)
        {
            calibrationDrawingId = id;
            QuadrilateralF undistorted = distortionHelper.Undistort(quadImage);

            calibrationPlane.Initialize(size, undistorted);
            AfterCalibrationChanged();
        }
Exemplo n.º 8
0
        public void Reset()
        {
            // Used on metadata over load.
            planeIsConvex = true;
            initialized   = false;

            quadImage = quadPlane.Clone();
        }
Exemplo n.º 9
0
        public void UpdateMapping(SizeF size)
        {
            planeWidth  = size.Width;
            planeHeight = size.Height;
            quadPlane   = new QuadrilateralF(planeWidth, planeHeight);

            projectiveMapping.Update(quadPlane, quadImage);
        }
 /// <summary>
 /// Returns an undistorted quadrilateral.
 /// </summary>
 public QuadrilateralF Undistort(QuadrilateralF quad)
 {
     return(new QuadrilateralF(
                Undistort(quad.A),
                Undistort(quad.B),
                Undistort(quad.C),
                Undistort(quad.D)));
 }
        /// <summary>
        /// Initialize the projective mapping from a line.
        /// length: Real world length of the line.
        /// a, b: Image coordinates of the line vertices.
        /// </summary>
        public void Initialize(float lengthWorld, PointF startImage, PointF endImage)
        {
            QuadrilateralF quadImage = MakeQuad(startImage, endImage);
            SizeF          sizeWorld = new SizeF(lengthWorld, lengthWorld);

            Initialize(sizeWorld, quadImage);
            perspective = false;
        }
Exemplo n.º 12
0
        /// <summary>
        /// Maps a quadrilateral from image coordinates to plane coordinates.
        /// </summary>
        public QuadrilateralF Backward(QuadrilateralF q)
        {
            PointF a = Backward(q.A);
            PointF b = Backward(q.B);
            PointF c = Backward(q.C);
            PointF d = Backward(q.D);

            return(new QuadrilateralF(a, b, c, d));
        }
Exemplo n.º 13
0
        /// <summary>
        /// Maps a quadrilateral from plane coordinates to image coordinates.
        /// </summary>
        public QuadrilateralF Forward(QuadrilateralF q)
        {
            PointF a = Forward(q.A);
            PointF b = Forward(q.B);
            PointF c = Forward(q.C);
            PointF d = Forward(q.D);

            return(new QuadrilateralF(a, b, c, d));
        }
Exemplo n.º 14
0
        public QuadrilateralF Transform(QuadrilateralF quadrilateral)
        {
            Point a = Transform(quadrilateral.A);
            Point b = Transform(quadrilateral.B);
            Point c = Transform(quadrilateral.C);
            Point d = Transform(quadrilateral.D);

            return(new QuadrilateralF(a, b, c, d));
        }
Exemplo n.º 15
0
        /// <summary>
        /// Initialize the projective mapping from a line.
        /// length: Real world length of the line.
        /// a, b: Image coordinates of the line vertices.
        /// </summary>
        public void Initialize(float lengthWorld, PointF startImage, PointF endImage, CalibrationAxis calibrationAxis)
        {
            this.calibrationAxis = calibrationAxis;
            QuadrilateralF quadImage = MakeQuad(startImage, endImage, calibrationAxis);
            SizeF          sizeWorld = new SizeF(lengthWorld, lengthWorld);

            Initialize(sizeWorld, quadImage);
            perspective = false;
        }
Exemplo n.º 16
0
        // Get the transform matrix from unit square to quad.
        private static double[,] MapSquareToQuad(QuadrilateralF quad)
        {
            double[,] sq = new double[3, 3];
            double px, py;

            px = quad[0].X - quad[1].X + quad[2].X - quad[3].X;
            py = quad[0].Y - quad[1].Y + quad[2].Y - quad[3].Y;

            if ((px < TOLERANCE) && (px > -TOLERANCE) &&
                (py < TOLERANCE) && (py > -TOLERANCE))
            {
                // Input quadrilateral is a parallelogram, the mapping is affine.
                sq[0, 0] = quad[1].X - quad[0].X;
                sq[0, 1] = quad[2].X - quad[1].X;
                sq[0, 2] = quad[0].X;

                sq[1, 0] = quad[1].Y - quad[0].Y;
                sq[1, 1] = quad[2].Y - quad[1].Y;
                sq[1, 2] = quad[0].Y;

                sq[2, 0] = 0.0;
                sq[2, 1] = 0.0;
                sq[2, 2] = 1.0;
            }
            else
            {
                // Projective mapping.
                double dx1, dx2, dy1, dy2, del;

                dx1 = quad[1].X - quad[2].X;
                dx2 = quad[3].X - quad[2].X;
                dy1 = quad[1].Y - quad[2].Y;
                dy2 = quad[3].Y - quad[2].Y;

                del = Det2(dx1, dx2, dy1, dy2);

                if (del == 0.0)
                {
                    return(null);
                }

                sq[2, 0] = Det2(px, dx2, py, dy2) / del;
                sq[2, 1] = Det2(dx1, px, dy1, py) / del;
                sq[2, 2] = 1.0;

                sq[0, 0] = quad[1].X - quad[0].X + sq[2, 0] * quad[1].X;
                sq[0, 1] = quad[3].X - quad[0].X + sq[2, 1] * quad[3].X;
                sq[0, 2] = quad[0].X;

                sq[1, 0] = quad[1].Y - quad[0].Y + sq[2, 0] * quad[1].Y;
                sq[1, 1] = quad[3].Y - quad[0].Y + sq[2, 1] * quad[3].Y;
                sq[1, 2] = quad[0].Y;
            }

            return(sq);
        }
Exemplo n.º 17
0
 public QuadrilateralF Transform(QuadrilateralF quad)
 {
     return(new QuadrilateralF()
     {
         A = Transform(quad.A),
         B = Transform(quad.B),
         C = Transform(quad.C),
         D = Transform(quad.D)
     });
 }
Exemplo n.º 18
0
        public FormCalibratePlane(CalibrationHelper calibrationHelper, DrawingPlane drawingPlane)
        {
            this.calibrationHelper = calibrationHelper;
            this.drawingPlane      = drawingPlane;
            this.quadrilateral     = drawingPlane.QuadImage;

            InitializeComponent();
            LocalizeForm();
            InitializeValues();
        }
Exemplo n.º 19
0
        private void DrawDiagonals(Graphics canvas, Pen pen, QuadrilateralF quadPlane, ProjectiveMapping projectiveMapping, DistortionHelper distorter, IImageToViewportTransformer transformer)
        {
            DrawDistortedLine(canvas, penEdges, quadPlane.A, quadPlane.B, projectiveMapping, distorter, transformer);
            DrawDistortedLine(canvas, penEdges, quadPlane.B, quadPlane.C, projectiveMapping, distorter, transformer);
            DrawDistortedLine(canvas, penEdges, quadPlane.C, quadPlane.D, projectiveMapping, distorter, transformer);
            DrawDistortedLine(canvas, penEdges, quadPlane.D, quadPlane.A, projectiveMapping, distorter, transformer);

            DrawDistortedLine(canvas, penEdges, quadPlane.A, quadPlane.C, projectiveMapping, distorter, transformer);
            DrawDistortedLine(canvas, penEdges, quadPlane.B, quadPlane.D, projectiveMapping, distorter, transformer);
        }
Exemplo n.º 20
0
        public void CalibrationByPlane_Update(QuadrilateralF quadImage)
        {
            QuadrilateralF undistorted = new QuadrilateralF(
                distortionHelper.Undistort(quadImage.A),
                distortionHelper.Undistort(quadImage.B),
                distortionHelper.Undistort(quadImage.C),
                distortionHelper.Undistort(quadImage.D));

            calibrationPlane.Update(undistorted);
            AfterCalibrationChanged();
        }
Exemplo n.º 21
0
        public void CalibrationByPlane_Initialize(SizeF size, QuadrilateralF quadImage)
        {
            QuadrilateralF undistorted = new QuadrilateralF(
                distortionHelper.Undistort(quadImage.A),
                distortionHelper.Undistort(quadImage.B),
                distortionHelper.Undistort(quadImage.C),
                distortionHelper.Undistort(quadImage.D));

            calibrationPlane.Initialize(size, undistorted);
            AfterCalibrationChanged();
        }
        public void CalibrationByPlane_Update(Guid id, QuadrilateralF quadImage)
        {
            if (calibratorType != CalibratorType.Plane || id != calibrationDrawingId)
            {
                return;
            }

            QuadrilateralF undistorted = distortionHelper.Undistort(quadImage);

            calibrationPlane.Update(undistorted);
            AfterCalibrationChanged();
        }
Exemplo n.º 23
0
        /// <summary>
        /// Initialize the projective mapping.
        /// </summary>
        /// <param name="size">Real world dimension of the reference rectangle.</param>
        /// <param name="quadImage">Image coordinates of the reference rectangle.</param>
        public void Initialize(SizeF size, QuadrilateralF quadImage)
        {
            PointF originImage = initialized ? Untransform(PointF.Empty) : quadImage.D;

            this.size      = size;
            this.quadImage = quadImage.Clone();
            mapping.Update(new QuadrilateralF(size.Width, size.Height), quadImage);
            SetOrigin(originImage);
            this.initialized = true;

            valid = quadImage.IsConvex;
        }
Exemplo n.º 24
0
        /// <summary>
        /// Updates the calibration coordinate system without changing the real-world scale of the rectangle or the user-defined origin.
        /// Quadrilateral variant.
        /// </summary>
        public void Update(QuadrilateralF quadImage)
        {
            if (!initialized || size.IsEmpty)
            {
                valid = false;
                return;
            }

            this.quadImage = quadImage.Clone();
            mapping.Update(new QuadrilateralF(size.Width, size.Height), quadImage);
            valid = quadImage.IsConvex;
        }
Exemplo n.º 25
0
        /// <summary>
        /// Initialize the projective mapping from a quadrilateral.
        /// size: Real world dimension of the reference rectangle.
        /// quadImage: Image coordinates of the reference rectangle.
        /// </summary>
        public void Initialize(SizeF sizeWorld, QuadrilateralF quadImage)
        {
            //PointF originImage = initialized ? Untransform(PointF.Empty) : quadImage.D;
            PointF originImage = quadImage.D;

            this.size      = sizeWorld;
            this.quadImage = quadImage.Clone();
            mapping.Update(new QuadrilateralF(size.Width, size.Height), quadImage);
            origin           = mapping.Backward(originImage);
            this.initialized = true;

            valid       = quadImage.IsConvex;
            perspective = !quadImage.IsAxisAlignedRectangle;
        }
Exemplo n.º 26
0
        public DrawingRectangle(PointF origin, long timestamp, long averageTimeStampsPerFrame, DrawingStyle preset = null, IImageToViewportTransformer transformer = null)
        {
            quadImage = new QuadrilateralF(origin, origin.Translate(50, 0), origin.Translate(50, 50), origin.Translate(0, 50));

            styleHelper.Color    = Color.Empty;
            styleHelper.LineSize = 1;
            styleHelper.PenShape = PenShape.Solid;
            if (preset == null)
            {
                preset = ToolManager.GetStylePreset("Rectangle");
            }

            style = preset.Clone();
            BindStyle();

            infosFading = new InfosFading(timestamp, averageTimeStampsPerFrame);
        }
Exemplo n.º 27
0
        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);
        }
Exemplo n.º 28
0
        public override void Draw(Graphics canvas, DistortionHelper distorter, IImageToViewportTransformer transformer, bool selected, long currentTimestamp)
        {
            double opacityFactor = infosFading.GetOpacityTrackable(trackingTimestamps, currentTimestamp);

            if (opacityFactor <= 0)
            {
                return;
            }

            QuadrilateralF quad = transformer.Transform(quadImage);

            bool drawEdgesOnly = !planeIsConvex || (!styleHelper.Perspective && !quadImage.IsAxisAlignedRectangle);

            using (penEdges = styleHelper.GetPen(opacityFactor, 1.0))
                using (SolidBrush br = styleHelper.GetBrush(opacityFactor))
                {
                    foreach (PointF p in quad)
                    {
                        canvas.FillEllipse(br, p.Box(4));
                    }

                    if (!drawEdgesOnly)
                    {
                        if (distorter != null && distorter.Initialized)
                        {
                            QuadrilateralF undistortedQuadImage = distorter.Undistort(quadImage);
                            projectiveMapping.Update(quadPlane, undistortedQuadImage);
                        }
                        else
                        {
                            projectiveMapping.Update(quadPlane, quadImage);
                        }

                        DrawGrid(canvas, penEdges, projectiveMapping, distorter, transformer);
                    }
                    else
                    {
                        // Non convex quadrilateral or non rectangle 2d grid: only draw the edges.
                        canvas.DrawLine(penEdges, quad.A, quad.B);
                        canvas.DrawLine(penEdges, quad.B, quad.C);
                        canvas.DrawLine(penEdges, quad.C, quad.D);
                        canvas.DrawLine(penEdges, quad.D, quad.A);
                    }
                }
        }
Exemplo n.º 29
0
        public override void Draw(Graphics canvas, DistortionHelper distorter, IImageToViewportTransformer transformer, bool selected, long currentTimestamp)
        {
            double opacityFactor = infosFading.GetOpacityFactor(currentTimestamp);

            if (opacityFactor <= 0)
            {
                return;
            }

            QuadrilateralF quad = transformer.Transform(quadImage);

            using (penEdges = styleHelper.GetPen(opacityFactor, 1.0))
                using (SolidBrush br = styleHelper.GetBrush(opacityFactor))
                {
                    foreach (PointF p in quad)
                    {
                        canvas.FillEllipse(br, p.Box(4));
                    }

                    if (planeIsConvex)
                    {
                        if (distorter != null && distorter.Initialized)
                        {
                            QuadrilateralF undistortedQuadImage = distorter.Undistort(quadImage);
                            projectiveMapping.Update(quadPlane, undistortedQuadImage);
                        }
                        else
                        {
                            projectiveMapping.Update(quadPlane, quadImage);
                        }

                        //DrawDiagonals(canvas, penEdges, quadPlane, projectiveMapping, distorter, transformer);
                        DrawGrid(canvas, penEdges, projectiveMapping, distorter, transformer);
                    }
                    else
                    {
                        // Non convex quadrilateral: only draw the edges.
                        canvas.DrawLine(penEdges, quad.A, quad.B);
                        canvas.DrawLine(penEdges, quad.B, quad.C);
                        canvas.DrawLine(penEdges, quad.C, quad.D);
                        canvas.DrawLine(penEdges, quad.D, quad.A);
                    }
                }
        }
Exemplo n.º 30
0
        private QuadrilateralF ParseQuadrilateral(XmlReader r, PointF scale)
        {
            r.ReadStartElement();
            PointF a = PointF.Empty;
            PointF b = PointF.Empty;
            PointF c = PointF.Empty;
            PointF d = PointF.Empty;

            while (r.NodeType == XmlNodeType.Element)
            {
                switch (r.Name)
                {
                case "A":
                    a = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    break;

                case "B":
                    b = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    break;

                case "C":
                    c = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    break;

                case "D":
                    d = XmlHelper.ParsePointF(r.ReadElementContentAsString());
                    break;

                default:
                    string unparsed = r.ReadOuterXml();
                    log.DebugFormat("Unparsed content in KVA XML: {0}", unparsed);
                    break;
                }
            }

            QuadrilateralF quad = new QuadrilateralF(a, b, c, d);

            quad.Scale(scale.X, scale.Y);

            r.ReadEndElement();

            return(quad);
        }