为来自自定义 PictureBox 控件的不同大小的图像平移一个点 (X,Y)

Translating a Point(X,Y) for Images of Different Sizes from a Custom PictureBox Control

我必须处理 bulk.I 中的多个图像必须在特定点 (X, Y) 放置一些文本。有一个派生自 picturebox 的自定义控件,允许用户拖动文本并将其放置在所需位置。

我为两种类型的图像设置了不同的 PictureBoxSizeMode

垂直图像

我设置>PictureBoxSizeMode.Zoom;

水平

对于填满 PictureBox 的水平图像,我设置 > PictureBoxSizeMode.StretchImage

用户可以 select 通过将文本拖动到此图片框上来放置文本的位置 control.The 原始图像调整为控件大小(对于水平图像)并且用户拖动文本在这张图片上。

基于 Picturebox 的 SizeMode,使用以下代码 selected 点转换为原始图像内的点

    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);
        }

现在,在获得点后,我必须在主窗体中调用另一个方法来获取大致的文本位置

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

其中 refimgsize 是用于放置文本的原始图像(未缩放)的大小。

 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);  

        }

问题是这个方法不是accurate.When我使用水平图像作为参考来放置文本,当该点转换为垂直图像中的一个点时;位置这个点不是 correct.Same 当我使用垂直图像作为参考并且翻译完成到水平图像中的一个点时发生的事情

我哪里做错了?请指教。

如果我需要 post 控件的完整代码,请告诉我。

更新:

这就是我想要的 achieve.The 下面图片中的 Logo 和文字是手工制作的 placed.You 可以看出,在不同方面的图片中,logo 和文字出现在大致相同的位置比率。

更新: 根据@Taw 的评论,我采用以下方法找到 2 个最近的边缘并使用各自的间距。

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;
        }

现在在主窗体中我执行以下操作

  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;


    }

我还是没有得到正确的定位。

我做错了什么?

这就是我想要实现的...

更新:

根据@Abion47 的回答,我使用了以下方法

在自定义图片框控件中

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;

主要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;

这几乎没问题.. 除了右下边缘。

在自定义图片框中

在主窗体中

我做错了什么?请指教

更新:

我所做的是从图片框控件计算比率,并在主窗体中使用这样的比率来平移点

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);

对于徽标,我也这样做

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;

在我放置文本和徽标之前效果很好 closely.In 自定义图片框控件文本和徽标出现 correctly.In 主 window,对于垂直图像它们看起来不错,但是对于水平图像,两者都重叠......这里出了什么问题?请指教..

更新

这有效 well.But 我如何将点从 Main window 转换到自定义图片框控件(带有允许拖动的文本)。

我试过下面的方法code.But这不是精确定位

  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;
        }

请指教..

我无法通读你所有的代码来告诉你应该如何编码,但这里有一些示例代码作为可能:

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;
}

在您的实践中,您可能还需要进行一些偏移以考虑边框厚度或其他因素。

编辑:

您可以对 "correct" 位置做的一件事是根据文本元素在图像中的位置来偏移文本元素的位置。例如,左上角的文本将相对于它自己的左上角定位,右下角的文本将相对于它们自己的右下角定位,而图像中心的文本将相对于它们的中心定位。

基于我在示例项目中给出的示例(在评论中下载 link),您可以这样做:

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;
}