Point translation (X, Y) for images of different sizes from custom PictureBox control
I need to process multiple images in bulk. I need to place some text at a specific point (X, Y). There is a custom control that pops out of the image window that allows the user to drag and drop text and place it at the desired location.
There are two types of images for which I set PictureBoxSizeMode
differently
Vertical images
I installed> PictureBoxSizeMode.Zoom;
Horizontal
For horizontal images that fill the PictureBox, I set> PictureBoxSizeMode.StretchImage
The user can select a location to place the text by dragging the text over this image control. The original image is resized to the size of the control (for horizontal images) and the user drags text over that image.
Based on the SizeMode parameter in the Picturebox, the selected point is translated to 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 on the Main Form to get the approximate position of the text
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 a reference to place the text, and when the point translates to a point in the vertical image, the point position is wrong. The same happens when I use a vertical image as a link and the translation is done to the point of the horizontal image
What am I doing wrong? Please advice.
Please let me know if I need to post the complete control code.
UPDATE:
This is what I want to achieve. The logo and text in the images below are hand-placed. You can see how the logo and text appear in roughly the same places in images with different aspect ratios.
UPDATE: Following @Taw's comments, I took the following approach to find the 2 closest edges and use the appropriate 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 am doing 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;
}
However, I am not getting the correct positioning.
What am I doing wrong?
This is what I want to achieve ...
UPDATE:
As per @ Abion47 answer I used the following approach
In a custom image 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 fine, except for the "Bottom Right" edge.
In custom image window
In basic form
What am I doing wrong? Please advice.
UPDATE:
What I did was calculate the ratio from the image control and use a 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 as here
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 really well until I put the text and logo close. The image and logo are rendered correctly in the custom control. In the main window they look good for vertical images, but for horizontal images both overlap ... What's going wrong here? Please advice.
UPDATE
It works well. But how do I translate point from the main window into a custom kernel control (with text that allows drag and drop).
I have tried the following code. But this does not provide accurate 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;
}
I ask for advice ..
source to share
I can't read all of your code to tell you exactly how you should code this, but here's some example code:
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 correction to account for the thickness of the border or whatever.
EDIT:
One thing you can do to "fix" the position is to shift the position of text elements based on where they are in the image. For example, the text in the upper left corner will be positioned relative to its upper left upper corner, and the text in the lower right corner will be located in the lower right corner, and those located in the center of the image will be located relative to their center.
Building the example given in my sample project (download link in comments), you can do like this:
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;
}
source to share