Draw Rectangle inside picture box SizeMode Zoom

2019-03-01 10:18发布

问题:

I have a picture box in a WindowsForms project with its SizeMode to "Zoom".

I want to draw a rectangle inside image and get its coordinates relative to the image and not to the picture box.

The problem is that the rectangle's coordinates do not match with the same rectangle selected on Windows Paint Application.

Here is the code used:

  1. Start Painting:

    /// <summary>
    /// Starts drawing.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        backupImage = pictureBox1.Image;
        _once = true;
        RectStartPoint = e.Location;
        pictureBox1.Invalidate();
    }
    
  2. While moving mouse:

    /// <summary>
    /// While moving mouse event, paint rectangle
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (_once) //Only draw rectangle while drawing mode
        {
            Point tempEndPoint = e.Location;
            Rect.Location = new Point(Math.Min(RectStartPoint.X, tempEndPoint.X),
                Math.Min(RectStartPoint.Y, tempEndPoint.Y));
    
            Rect = new Rectangle(
                Math.Min(tempEndPoint.X, Rect.Left),
                Math.Min(tempEndPoint.Y, Rect.Top),
                Math.Min(e.X - RectStartPoint.X, pictureBox1.ClientRectangle.Width - RectStartPoint.X),
                Math.Min(e.Y - RectStartPoint.Y, pictureBox1.ClientRectangle.Height - RectStartPoint.Y));
    
            pictureBox1.Refresh();
            pictureBox1.CreateGraphics().DrawRectangle(cropPen, Rect);
        }
    }
    
  3. When 2 click, finhish painting rectange:

    /// <summary>
    /// When mouse click is released, write in texbox the rectangle's coordinates.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        if (_once)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                Point tempEndPoint = e.Location;
    
                _once = false;
                string sAux = string.Format("Left: {0}; Top: {1}; Width: {2}; Height: {3} \r\n", Math.Min(tempEndPoint.X, Rect.Left), Math.Min(tempEndPoint.Y, Rect.Top),
                        Math.Min(e.X - RectStartPoint.X, pictureBox1.ClientRectangle.Width - RectStartPoint.X), Math.Min(e.Y - RectStartPoint.Y, pictureBox1.ClientRectangle.Height - RectStartPoint.Y));
    
                textBox1.Text += sAux;
            }
        }
    }
    

The results are:

Windows Image

Paint Image

As you can see on both images, left, top, width and height do not match.

Can you tell me how to obtain the same result?

Example2

回答1:

Here is a function to help with various calculations:

void SetImageScale(PictureBox pbox, out RectangleF ImgArea , out float zoom)
{
    SizeF sp = pbox.ClientSize;
    SizeF si = pbox.Image.Size;
    float rp = sp.Width / sp.Height;   // calculate the ratios of
    float ri = si.Width / si.Height;   // pbox and image

    if (rp > ri)
    {
        zoom = sp.Height / si.Height;
        float width = si.Width * zoom;
        float left = (sp.Width - width) / 2;
        ImgArea = new RectangleF(left, 0, width, sp.Height);
    }
    else
    {
        zoom = sp.Width / si.Width;
        float height = si.Height * zoom;
        float top = (sp.Height - height) / 2;
        ImgArea = new RectangleF(0, top, sp.Width, height);
    }
}

Here is how you can use it, given a Rectangle Rect which you created from the mouse coordinates:

float zoom = 1f;
RectangleF ImgArea = Rectangle.Empty;

SetImageScale(pictureBox1, out ImgArea, out zoom);

Point RLoc = Point.Round(new PointF( (Rect.X - ImgArea.X) / zoom, 
                                     (Rect.Y - ImgArea.Y) / zoom ));
Size RSz = Size.Round(new SizeF(Rect.Width / zoom, Rect.Height / zoom));

label1.Text =  "Selection in mouse coordinates: "  + Rect.ToString();
label2.Text =  "Selection in image coordinates: "  + new Rectangle(RLoc, RSz).ToString();

This should work no matter whether the images are landscape or portrait or which ratio if any is greater, the Image's or the PictureBox's.

Note that with the images strongly zoomed it is hard to do a pixel-pefect selection..

The function is variant of the one in this post.