Translating a Point(X,Y) for Images of Different S

2019-06-14 15:44发布

问题:

I have to process several images in bulk.I have to place some text at a particular point(X, Y).There is a custom control that is derived from picturebox that allows the user to drag the text and place it at the desired location.

There are two types of images for which I set the PictureBoxSizeMode differently

Vertical Images

I set > PictureBoxSizeMode.Zoom;

Horizontal

For Horizontal Images that fill up the PictureBox i set > PictureBoxSizeMode.StretchImage

The user can select the location to place the text by dragging the text over this picturebox control.The original image is resized to the control size(for horizontal images) and the user drags the text over this image.

Based on the SizeMode of the Picturebox the selected point is translated to point within the original image using the following code

    if (sizemode == 1)
    {

     transpoint = TranslateStretchImageMousePosition(new Point(e.X - 20, e.Y -20));

    }

    else if (sizemode == 2)
    {
        transpoint = TranslateZoomMousePosition(new Point(e.X - 20, e.Y - 20));

    }

public Point TranslateStretchImageMousePosition(Point coordinates)
        {
            // test to make sure our image is not null
            if (Image == null) return coordinates;
            // Make sure our control width and height are not 0
            if (Width == 0 || Height == 0) return coordinates;
            // First, get the ratio (image to control) the height and width
            float ratioWidth = (float)Image.Width / Width;
            //MessageBox.Show(ratioWidth.ToString());

            float ratioHeight = (float)Image.Height / Height;
           // MessageBox.Show(ratioHeight.ToString());
            // Scale the points by our ratio
            float newX = coordinates.X;
            float newY = coordinates.Y;
            newX *= ratioWidth;
            newY *= ratioHeight;
            return new Point((int)newX, (int)newY);
        }

public Point TranslateZoomMousePosition(Point coordinates)
        {
            // test to make sure our image is not null
            if (Image == null) return coordinates;
            // Make sure our control width and height are not 0 and our 
            // image width and height are not 0
            if (Width == 0 || Height == 0 || Image.Width == 0 || Image.Height == 0) return coordinates;
            // This is the one that gets a little tricky. Essentially, need to check 
            // the aspect ratio of the image to the aspect ratio of the control
            // to determine how it is being rendered
            float imageAspect = (float)Image.Width / Image.Height;
            float controlAspect = (float)Width / Height;
            float newX = coordinates.X;
            float newY = coordinates.Y;
            if (imageAspect > controlAspect)
            {
                // This means that we are limited by width, 
                // meaning the image fills up the entire control from left to right
                float ratioWidth = (float)Image.Width / Width;
                newX *= ratioWidth;
                float scale = (float)Width / Image.Width;
                float displayHeight = scale * Image.Height;
                float diffHeight = Height - displayHeight;
                diffHeight /= 2;
                newY -= diffHeight;
                newY /= scale;
            }
            else
            {
                // This means that we are limited by height, 
                // meaning the image fills up the entire control from top to bottom
                float ratioHeight = (float)Image.Height / Height;
                newY *= ratioHeight;
                float scale = (float)Height / Image.Height;
                float displayWidth = scale * Image.Width;
                float diffWidth = Width - displayWidth;
                diffWidth /= 2;
                newX -= diffWidth;
                newX /= scale;
            }
            return new Point((int)newX, (int)newY);
        }

Now after getting the Point I have to call another method within the Main Form to get the approximate text location

    point= translatemanualpoint(transpoint, img, refimgsize.Width, refimgsize.Height);

Where refimgsize is the size of the original image(unscaled) used to place the text.

 private Point translatemanualpoint(Point coordinates, Bitmap Image, int Width, int Height)
        {

            //---------------------------------
            // test to make sure our image is not null
            if (Image == null) return coordinates;
            // Make sure our control width and height are not 0
            if (Width == 0 || Height == 0) return coordinates;
            // First, get the ratio (image to control) the height and width
            float ratioWidth = (float)Image.Width / Width;


            float ratioHeight = (float)Image.Height / Height;

            // Scale the points by our ratio
            float newX = coordinates.X;
            float newY = coordinates.Y;
            newX *= ratioWidth;
            newY *= ratioHeight;
            return new Point((int)newX, (int)newY);  

        }

The problem is that this method is not accurate.When I use a Horizontal Image as reference to place the Text and when the point is translated to a point in the vertical image;the position of the point is not correct.Same thing happens when i use a vertical Image as reference and translation is done to a point in the Horizontal Image

What I'm I doing wrong?Please advice.

Please let me know if I need to post the full code of the control.

UPDATE:

This is what i want to achieve.The Logo and the Text in the pictures below are manually placed.You can see that how the logo and the text appears in approximately same locations in images of different aspect ratios.

UPDATE: As per @Taw's comments i have taken the following approach to find the 2 closest edges and use the respective spacing.

void findclosestedges(Point p)
        {         

            //Xedge=1 -- Left Edge is closer to Point 2--Right Edge 

            //Finding closest Left/Right Edge
            if (p.X < (ClientSize.Width - p.X))
            {

                LaunchOrigin.Xedge = 1;
                LaunchOrigin.Xspacing = p.X;
                LaunchOrigin2.closestedge.Text = " ";
                LaunchOrigin2.closestedge.Text = LaunchOrigin2.closestedge.Text + " left";
            }
            else
            {
                LaunchOrigin.Xedge = 2;
                LaunchOrigin.Xspacing = (ClientSize.Width - p.X);
                LaunchOrigin2.closestedge.Text = " ";
                LaunchOrigin2.closestedge.Text = LaunchOrigin2.closestedge.Text + " right";
            }

            //Finding closest Top/Bottom Edge

            if (p.Y < (ClientSize.Height - p.Y))
            {
                LaunchOrigin.Yedge = 1;
                LaunchOrigin.Yspacing =p.Y;
                LaunchOrigin2.closestedge.Text = LaunchOrigin2.closestedge.Text + " top";
            }
            else
            {
                LaunchOrigin.Yedge = 2;
                LaunchOrigin.Yspacing = (ClientSize.Height - p.Y);
                LaunchOrigin2.closestedge.Text = LaunchOrigin2.closestedge.Text + " bottom";
            }
            LaunchOrigin.ewidth = Width;
            LaunchOrigin.eheight = Height;
        }

Now in the Main Form i do the following

  int wratio = img.Width / ewidth;
    int hratio = img.Height / eheight;
    if (Xedge == 1)
    {

            cpoint.X = Xspacing*wratio;


    }
    else
    {

        cpoint.X = img.Width - Xspacing * wratio;



    }
    if (Yedge == 1)
    {

        cpoint.Y = Yspacing * hratio;




    }
    else
    {


            cpoint.Y = img.Height - Yspacing*hratio;


    }

Still i don't get the proper positioning.

What i'm i doing wrong?

This is what i want to achieve...

UPDATE:

As per @Abion47 answer i have used the following approach

In the custom picturebox control

Point src = e.Location;
PointF ratio = new PointF((float)src.X / Width, (float)src.Y / Height);
LaunchOrigin.ratio = ratio;
Point origin = new Point((int)(backupbit1.Width * ratio.X), (int)(backupbit1.Height * ratio.Y));
LaunchOrigin.origin = origin;
point.X = src.X - origin.X;
point.Y = src.Y - origin.Y;

In the Main window

Point pos2 = new Point((int)(ratio.X * img.Width), (int)(ratio.Y * img.Height));
cpoint.X = pos2.X  - origin.X;
cpoint.Y = pos2.Y  - origin.Y;

This works almost okay.. except the Bottom-Right edge.

In the custom picture-box

In in the Main form

What i'm i doing wrong? Please advice.

UPDATE:

What i have done is calculate the ratio from the picturebox control and use the ratio like this in the Main form to translate the point

Point origin = new Point((int)(bitmap.Width * textratio.X), (int)(bitmap.Height * textratio.Y));
Point pos2 = new Point((int)(textratio.X * img.Width), (int)(textratio.Y * img.Height));
cpoint.X = pos2.X - (int)(origin.X);
cpoint.Y = pos2.Y - (int)(origin.Y);

For the Logo i do the same like this

Point origin = new Point((int)(worktag.Width * logoratio.X), (int)(worktag.Height * logoratio.Y));
Point logopositionpoint = new Point((int)(logoratio.X * img.Width), (int)(logoratio.Y * img.Height));
imgpoint.X = logopositionpoint.X - origin.X;
imgpoint.Y = logopositionpoint.Y - origin.Y;

This works pretty well until i place the text and logo closely.In the custom picturebox control text and logo appears correctly.In the Main window,For vertical images they appear alright,but for horizontal images both overlaps... What's going wrong here? Please advice..

UPDATE

This works well.But how do i translate the point from the Main window to the custom picturebox control( with the text that allows dragging).

I have tried the following code.But this is not giving precise positioning

  private Point translatetextpoint(Point mpoint,Bitmap bitmap)
        {

            PointF ratio = new PointF((float)LaunchOrigin.cpoint.X /LaunchOrigin.img.Width, (float)LaunchOrigin.cpoint.Y /LaunchOrigin.img.Height);
            Point origin = new Point((int)(endPointPictureBox1.bit.Width * ratio.X), (int)(endPointPictureBox1.bit.Height * ratio.Y));
            Point pos2 = new Point((int)(ratio.X * endPointPictureBox1.Width), (int)(ratio.Y * endPointPictureBox1
                .Height));
            pos2.X = pos2.X - (int)(origin.X);
            pos2.Y = pos2.Y - (int)(origin.Y);
            return pos2;
        }

Please advice..

回答1:

I can't read through all your code to tell you exactly how you should code this, but here is some example code as a possibility:

PointF GetReferencePoint(Point absoluteReferencePoint)
{
    PointF referencePointAsRatio = new Point();

    referencePointAsRatio.X = (float)absoluteReferencePoint.X / referenceImage.Width;
    referencePointAsRatio.Y = (float)absoluteReferencePoint.Y / referenceImage.Height;

    return referencePointAsRatio;
}

...

Point GetTargetPoint(PointF referencePointAsRatio)
{
    Point targetPoint = new Point();

    targetPoint.X = (int)(referencePointAsRatio.X * targetImage.Width);
    targetPoint.Y = (int)(referencePointAsRatio.Y * targetImage.Height);

    return targetPoint;
}

In your practice, you will probably need to do some offsetting as well to account for border thicknesses or whatever.

EDIT:

One thing you can possibly do to "correct" the position is to offset the positions of the text elements depending on where they are in the image. For example, text in the top left corner will be positioned relative to its own top left corner, those in the lower right will be positioned to their own lower right, and those in the center of the image will be positioned relative to their center.

Building off of the example I gave in my sample project (download link in the comments), you can do it like so:

private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    Point src = e.Location;
    PointF ratio = new PointF((float)src.X / pictureBox1.Width, (float)src.Y / pictureBox1.Height);
    Point origin = new Point((int)(label1.Width * ratio.X), (int)(label1.Height * ratio.Y));

    label1.Left = src.X - origin.X;
    label1.Top = src.Y - origin.Y;

    Point pos2 = new Point((int)(ratio.X * pictureBox2.Width), (int)(ratio.Y * pictureBox2.Height));
    label2.Left = pos2.X + pictureBox2.Left - origin.X;
    label2.Top = pos2.Y + pictureBox2.Top - origin.Y;

    Point pos3 = new Point((int)(ratio.X * pictureBox3.Width), (int)(ratio.Y * pictureBox3.Height));
    label3.Left = pos3.X + pictureBox3.Left - origin.X;
    label3.Top = pos3.Y + pictureBox3.Top - origin.Y;

    Point pos4 = new Point((int)(ratio.X * pictureBox4.Width), (int)(ratio.Y * pictureBox4.Height));
    label4.Left = pos4.X + pictureBox4.Left - origin.X;
    label4.Top = pos4.Y + pictureBox4.Top - origin.Y;
}