[如何获取使用WPF渲染的jpeg图像上两点之间的精确像素数。我的解决方案不同于GIMP

问题描述 投票:1回答:1

我正在尝试获取jpeg图像上两点之间的精确像素数。为此,我使用Line类在WPF应用程序中使用用户绘制的线。我的解决方案是计算这条线的两个端点之间的距离似乎很差,并且返回的分数显然不是像素数。这是画线的代码:

        // The "size" of an object for mouse over purposes.
    private const int ObjectRadius = 3;

    // We're over an object if the distance squared
    // between the mouse and the object is less than this.
    private const int OverDistSquared = ObjectRadius * ObjectRadius;

    // The line we're drawing or moving.
    private Line _selectedLine;

    private List<Line> _lines = new List<Line>();

    // True if we're moving the line's first starting end point.
    private bool _movingStartEndPoint = false;

    // The offset from the mouse to the object being moved.
    private double _offsetX, _offsetY;

    // Save the trash can dimensions.
    private double _trashWidth, _trashHeight;

    // The mouse is up. See whether we're over an end point or segment.
    private void canDrawing_MouseMove_NotDown(object sender, MouseEventArgs e)
    {
        Cursor newCursor = Cursors.Cross;

        // See what we're over.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        if (MouseIsOverEndpoint(location, out _selectedLine, out _movingStartEndPoint))
            newCursor = Cursors.Arrow;
        else if (MouseIsOverLine(location, out _selectedLine))
            newCursor = Cursors.Hand;

        // Set the new cursor.
        if (canDrawing.Cursor != newCursor)
            canDrawing.Cursor = newCursor;
    }

    // See what we're over and start doing whatever is appropriate.
    private void canDrawing_MouseDown(object sender, MouseButtonEventArgs e)
    {
        // See what we're over.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        if (MouseIsOverEndpoint(location, out _selectedLine, out _movingStartEndPoint))
        {
            // Start moving this end point.
            canDrawing.MouseMove -= canDrawing_MouseMove_NotDown;
            canDrawing.MouseMove += canDrawing_MouseMove_MovingEndPoint;
            canDrawing.MouseUp += canDrawing_MouseUp_MovingEndPoint;

            // Remember the offset from the mouse to the point.
            Point hitPoint;
            if (_movingStartEndPoint)
                hitPoint = new Point(_selectedLine.X1, _selectedLine.Y1);
            else
                hitPoint = new Point(_selectedLine.X2, _selectedLine.Y2);
            _offsetX = hitPoint.X - location.X;
            _offsetY = hitPoint.Y - location.Y;
        }
        else if (MouseIsOverLine(location, out _selectedLine))
        {
            // Start moving this segment.
            canDrawing.MouseMove -= canDrawing_MouseMove_NotDown;
            canDrawing.MouseMove += canDrawing_MouseMove_MovingSegment;
            canDrawing.MouseUp += canDrawing_MouseUp_MovingSegment;

            // Remember the offset from the mouse
            // to the segment's first end point.
            _offsetX = _selectedLine.X1 - location.X;
            _offsetY = _selectedLine.Y1 - location.Y;
        }
        else
        {
            // Start drawing a new segment.
            canDrawing.MouseMove -= canDrawing_MouseMove_NotDown;
            canDrawing.MouseMove += canDrawing_MouseMove_Drawing;
            canDrawing.MouseUp += canDrawing_MouseUp_Drawing;

            _selectedLine = new Line
            {
                Stroke = Brushes.Red,
                X1 = location.X,
                Y1 = location.Y,
                X2 = location.X,
                Y2 = location.Y
            };

            canDrawing.Children.Add(_selectedLine);
        }
    }

    #region Distance Methods

    // See if the mouse is over an end point.
    private bool MouseIsOverEndpoint(Point mousePt, out Line hitLine, out bool startEndpoint)
    {
        foreach (object obj in canDrawing.Children)
        {
            // Only process Lines.
            if (obj is Line)
            {
                Line line = obj as Line;

                // Check the starting point.
                Point point = new Point(line.X1, line.Y1);
                if (FindDistanceToPointSquared(mousePt, point) < OverDistSquared)
                {
                    // We're over this point.
                    hitLine = line;
                    startEndpoint = true;
                    return true;
                }

                // Check the end point.
                point = new Point(line.X2, line.Y2);
                if (FindDistanceToPointSquared(mousePt, point) < OverDistSquared)
                {
                    // We're over this point.
                    hitLine = line;
                    startEndpoint = false;
                    return true;
                }
            }
        }

        hitLine = null;
        startEndpoint = false;
        return false;
    }

    // See if the mouse is over a line segment.
    private bool MouseIsOverLine(Point mousePt, out Line hitLine)
    {
        foreach (object obj in canDrawing.Children)
        {
            // Only process Lines.
            if (obj is Line)
            {
                Line line = obj as Line;

                // See if we're over this line.
                Point closest;
                Point pt1 = new Point(line.X1, line.Y1);
                Point pt2 = new Point(line.X2, line.Y2);
                if (FindDistanceToSegmentSquared(
                    mousePt, pt1, pt2, out closest)
                        < OverDistSquared)
                {
                    // We're over this segment.
                    hitLine = line;
                    return true;
                }
            }
        }

        hitLine = null;
        return false;
    }

    // Calculate the distance squared between two points.
    private double FindDistanceToPointSquared(Point pt1, Point pt2)
    {
        double dx = pt1.X - pt2.X;
        double dy = pt1.Y - pt2.Y;
        return dx * dx + dy * dy;
    }

    // Calculate the distance squared between
    // point pt and the segment p1 --> p2.
    private double FindDistanceToSegmentSquared(Point pt, Point p1, Point p2, out Point closest)
    {
        double dx = p2.X - p1.X;
        double dy = p2.Y - p1.Y;
        if ((dx == 0) && (dy == 0))
        {
            // It's a point not a line segment.
            closest = p1;
            dx = pt.X - p1.X;
            dy = pt.Y - p1.Y;
            return dx * dx + dy * dy;
        }

        // Calculate the t that minimizes the distance.
        double t = ((pt.X - p1.X) * dx + (pt.Y - p1.Y) * dy) / (dx * dx + dy * dy);

        // See if this represents one of the segment's
        // end points or a point in the middle.
        if (t < 0)
        {
            closest = new Point(p1.X, p1.Y);
            dx = pt.X - p1.X;
            dy = pt.Y - p1.Y;
        }
        else if (t > 1)
        {
            closest = new Point(p2.X, p2.Y);
            dx = pt.X - p2.X;
            dy = pt.Y - p2.Y;
        }
        else
        {
            closest = new Point(p1.X + t * dx, p1.Y + t * dy);
            dx = pt.X - closest.X;
            dy = pt.Y - closest.Y;
        }

        return dx * dx + dy * dy;
    }

    private double FindDistanceToPoint(Point pt1, Point pt2)
    {
        double dx = pt1.X - pt2.X;
        double dy = pt1.Y - pt2.Y;
        return Math.Sqrt(dx * dx + dy * dy);
    }

    #endregion Distance Methods

    #region Moving End Point

    // We're moving an end point.
    private void canDrawing_MouseMove_MovingEndPoint(object sender, MouseEventArgs e)
    {
        // Move the point to its new location.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        if (_movingStartEndPoint)
        {
            _selectedLine.X1 = location.X + _offsetX;
            _selectedLine.Y1 = location.Y + _offsetY;
        }
        else
        {
            _selectedLine.X2 = location.X + _offsetX;
            _selectedLine.Y2 = location.Y + _offsetY;
        }
    }

    // Stop moving the end point.
    private void canDrawing_MouseUp_MovingEndPoint(object sender, MouseEventArgs e)
    {
        // Reset the event handlers.
        canDrawing.MouseMove += canDrawing_MouseMove_NotDown;
        canDrawing.MouseMove -= canDrawing_MouseMove_MovingEndPoint;
        canDrawing.MouseUp -= canDrawing_MouseUp_MovingEndPoint;
    }

    #endregion Moving End Point

    #region Drawing

    // We're drawing a new segment.
    private void canDrawing_MouseMove_Drawing(object sender, MouseEventArgs e)
    {
        // Update the new line's end point.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        _selectedLine.X2 = location.X;
        _selectedLine.Y2 = location.Y;
    }

    // Stop drawing.
    private void canDrawing_MouseUp_Drawing(object sender, MouseEventArgs e)
    {
        _selectedLine.Stroke = Brushes.DeepPink;

        // Reset the event handlers.
        canDrawing.MouseMove -= canDrawing_MouseMove_Drawing;
        canDrawing.MouseMove += canDrawing_MouseMove_NotDown;
        canDrawing.MouseUp -= canDrawing_MouseUp_Drawing;

        // If the new segment has no length, delete it.
        if ((_selectedLine.X1 == _selectedLine.X2) && (_selectedLine.Y1 == _selectedLine.Y2))
            canDrawing.Children.Remove(_selectedLine);
        else
        {
            _lines.Add(_selectedLine);
            var point1 = new Point(_selectedLine.X1, _selectedLine.Y1);
            var point2 = new Point(_selectedLine.X2, _selectedLine.Y2);
            PixelsInMillimeterTextBox.Text = FindDistanceToPoint(point1, point2).ToString(CultureInfo.InvariantCulture);
        }

    }

    #endregion Drawing

    #region "Moving Segment"

    // We're moving a segment.
    private void canDrawing_MouseMove_MovingSegment(object sender, MouseEventArgs e)
    {
        // Find the new location for the first end point.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        double newX1 = location.X + _offsetX;
        double newY1 = location.Y + _offsetY;

        // See how far we are moving that point.
        double dx = newX1 - _selectedLine.X1;
        double dy = newY1 - _selectedLine.Y1;

        // Move the line.
        _selectedLine.X1 = newX1;
        _selectedLine.Y1 = newY1;
        _selectedLine.X2 += dx;
        _selectedLine.Y2 += dy;
    }

    // Stop moving the segment.
    private void canDrawing_MouseUp_MovingSegment(object sender, MouseEventArgs e)
    {
        // Reset the event handlers.
        canDrawing.MouseMove += canDrawing_MouseMove_NotDown;
        canDrawing.MouseMove -= canDrawing_MouseMove_MovingSegment;
        canDrawing.MouseUp -= canDrawing_MouseUp_MovingSegment;

        // See if the mouse is over the trash can.
        Point location = e.MouseDevice.GetPosition(canDrawing);
        if ((location.X >= 0) && (location.X < _trashWidth) &&
            (location.Y >= 0) && (location.Y < _trashHeight))
        {
            if (MessageBox.Show("Delete this segment?",
                "Delete Segment?", MessageBoxButton.YesNo)
                    == MessageBoxResult.Yes)
            {
                // Delete the segment.
                canDrawing.Children.Remove(_selectedLine);
            }
        }
    }

    #endregion // Moving End Point

这是包含图像的控件的XAML:

                 <Grid>
                    <Border Name="Border" BorderThickness="1" BorderBrush="#34558b" Margin="10,44,10,10">
                        <utility:ZoomBorder x:Name="border" ClipToBounds="True" Background="Gray">
                            <Canvas Name="canDrawing"
                                    MouseMove="canDrawing_MouseMove_NotDown"
                                    MouseDown="canDrawing_MouseDown">
                                <Image Stretch="None" Name="ReferenceImage" Canvas.Left="0" Canvas.Top="0"/>
                            </Canvas>
                        </utility:ZoomBorder>
                    </Border>
                </Grid>
c# wpf line draw measure
1个回答
1
投票

有一个Win32 GDI调用(LineDDA),它将枚举两个点之间的点(像素)。

获取点数将为您提供以像素为单位的线的长度。

answer具有您可以使用的C#代码。我用您的示例代码进行了尝试,并能够获得适当的像素长度。

这里是参考代码:

public static List<Point> GetPointsOnLine(System.Drawing.Point point1, System.Drawing.Point point2)
{
    var points = new List<Point>();
    var handle = GCHandle.Alloc(points);
    try
    {
        LineDDA(point1.X, point1.Y, point2.X, point2.Y, GetPointsOnLineCallback, GCHandle.ToIntPtr(handle));
    }
    finally
    {
        handle.Free();
    }
    return points;
}

private static void GetPointsOnLineCallback(int x, int y, IntPtr lpData)
{
    var handle = GCHandle.FromIntPtr(lpData);
    var points = (List<Point>)handle.Target;
    points.Add(new Point(x, y));
}

[DllImport("gdi32.dll")]
private static extern bool LineDDA(int nXStart, int nYStart, int nXEnd, int nYEnd, LineDDAProc lpLineFunc, IntPtr lpData);

// The signature for the callback method
private delegate void LineDDAProc(int x, int y, IntPtr lpData);

注意,GetPointsOnLine方法使用System.Drawing.Point而不是System.Windows.Point

© www.soinside.com 2019 - 2024. All rights reserved.