在缩放模式 Picturebox 中平移矩形位置

Translate Rectangle Position in Zoom Mode Picturebox

我正在确定图像中的矩形区域并在 PictureBox 中将其显示给用户。
由于图像有时可能非常大,我使用的是 PictureBox,其 SizeMode 设置为 Zoom

我正在使用以下代码来平移矩形 (X, Y) 坐标:

public Point TranslateZoomMousePosition(Point coordinates)
{
    // test to make sure our image is not null
    if (pictureBox5.Image == null) return coordinates;
    // Make sure our control width and height are not 0 and our 
    // image width and height are not 0
    if (pictureBox5.Width == 0 || pictureBox5.Height == 0 || pictureBox5.Image.Width == 0 || pictureBox5.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)pictureBox5.Image.Width / pictureBox5.Image.Height;
    float controlAspect = (float)pictureBox5.Width / pictureBox5.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)pictureBox5.Image.Width / pictureBox5.Width;
        newX *= ratioWidth;
        float scale = (float)pictureBox5.Width / pictureBox5.Image.Width;
        float displayHeight = scale * pictureBox5.Image.Height;
        float diffHeight = pictureBox5.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)pictureBox5.Image.Height / pictureBox5.Height;
        newY *= ratioHeight;
        float scale = (float)pictureBox5.Height / pictureBox5.Image.Height;
        float displayWidth = scale * pictureBox5.Image.Width;
        float diffWidth = pictureBox5.Width - displayWidth;
        diffWidth /= 2;
        newX -= diffWidth;
        newX /= scale;
    }
    return new Point((int)newX, (int)newY);
}

在确定的位置添加一个框架控件:

pictureBox5.Controls.Clear();
var c = new FrameControl();
c.Size = new Size(myrect.Width, myrect.Height);
c.Location=TranslateZoomMousePosition(newPoint(myrect.Location.X,myrect.Location.Y));
pictureBox5.Controls.Add(c);

但是确定的frame/rectangle位置不正确。
我做错了什么?

更新: 我正在尝试使用类似代码将图像上的矩形转换为 PictureBox 上的框架控件

public Rectangle GetRectangeOnPictureBox(PictureBox p, Rectangle selectionRect,Bitmap bit)
    {
        var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        var imageRect = (Rectangle)method.Invoke(p, new object[] { p.SizeMode });
        if (p.Image == null)
            return selectionRect;
        int cx = bit.Width / imageRect.Width;
        int cy = bit.Height / imageRect.Height;
        Rectangle trsRectangle = new Rectangle(selectionRect.X * cx, selectionRect.Y * cy, selectionRect.Width * cx, selectionRect.Height * cy);

        trsRectangle.Offset(imageRect.X, imageRect.Y);
        return trsRectangle;
    }

这会产生无效的 result.Please 建议

您可以通过以下方式将图片框上选定的矩形转换为图像上的矩形:

public RectangleF GetRectangeOnImage(PictureBox p, Rectangle selectionRect)
{
    var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    var imageRect = (Rectangle)method.Invoke(p, new object[] { p.SizeMode });
    if (p.Image == null)
        return selectionRect;
    var cx = (float)p.Image.Width / (float)imageRect.Width;
    var cy = (float)p.Image.Height / (float)imageRect.Height;
    var r2 = Rectangle.Intersect(imageRect, selectionRect);
    r2.Offset(-imageRect.X, -imageRect.Y);
    return new RectangleF(r2.X * cx, r2.Y * cy, r2.Width * cx, r2.Height * cy);
}

注意:您可以找到 ImageRectangleFromSizeMode 方法 source code here 并将其用作应用程序代码的一部分。

示例 - 裁剪具有 SizeMode = Zoom 的 PictureBox 图像

例如,以下代码将裁剪图片框 1 的给定矩形并将结果设置为图片框 2 的图像:

var selectedRectangle = new Rectangle(7, 30, 50, 40);
var result = GetRectangeOnImage(pictureBox1, selectedRectangle);
using (var bm = new Bitmap((int)result.Width, (int)result.Height))
{
    using (var g = Graphics.FromImage(bm))
        g.DrawImage(pictureBox1.Image, 0, 0, result, GraphicsUnit.Pixel);
    pictureBox2.Image = (Image)bm.Clone();
}

这是输入图像:

这是结果:

一个专门的 class,它提供了一些辅助工具来确定选区的缩放因子并将选区坐标转换为缩放后的 Bitmap 坐标。
版本 仅适用于缩放图像。

ZoomFactorclass提供了这些方法:

PointF TranslateZoomPosition(PointF Coordinates, SizeF ContainerSize, SizeF ImageSize):
returns PointF 将 Container 内的 Point 位置坐标转换为 Bitmap 内的 Point 位置,放大容器。

RectangleF TranslateZoomSelection(RectangleF Selection, SizeF ContainerSize, SizeF ImageSize):
returns RectangleF 表示在 Container 中创建的选区,转换为位图坐标。

RectangleF TranslateSelectionToZoomedSel(RectangleF SelectionRect, SizeF ContainerSize, SizeF ImageSize):
returns RectangleF 表示原始位图的预选区域转换为容器内的缩放选择图像。

PointF GetImageScaledOrigin(SizeF ContainerSize, SizeF ImageSize):
returns Container 内缩放图像原点坐标的 PointF 参考。

SizeF GetImageScaledSize(SizeF ContainerSize, SizeF ImageSize):
returns 在容器内缩放时图像的 SizeF 引用。

示例用法,显示如何使用在容器控件内创建的选择矩形裁剪位图。 TranslateZoomSelection方法returns选择区域对应的Bitmap部分:

ZoomFactor zoomHelper = new ZoomFactor()
Bitmap originalBitmap;

RectangleF currentSelection = [Current Selection Rectangle];
RectangleF bitmapRect = zoomHelper.TranslateZoomSelection(currentSelection, [Container].Size, originalBitmap.Size);

var croppedBitmap = new Bitmap((int)bitmapRect.Width, (int)bitmapRect.Height, originalBitmap.PixelFormat))
using (var g = Graphics.FromImage(croppedBitmap))
{
    g.DrawImage(originalBitmap, new Rectangle(Point.Empty, Size.Round(bitmapRect.Size)), 
                bitmapRect, GraphicsUnit.Pixel);
    [Container].Image = croppedBitmap;
}

上述行为的示例:

注意在示例中,预选图像中的纵向反转WidthHeight

ZoomFactorclass:

public class ZoomFactor
{
    public ZoomFactor() { }

    public PointF TranslateZoomPosition(PointF coordinates, SizeF containerSize, SizeF imageSize)
    {
        PointF imageOrigin = TranslateCoordinatesOrigin(coordinates, containerSize, imageSize);
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        return new PointF(imageOrigin.X / scaleFactor, imageOrigin.Y / scaleFactor);
    }

    public RectangleF TranslateZoomSelection(RectangleF selectionRect, SizeF containerSize, SizeF imageSize)
    {
        PointF selectionTrueOrigin = TranslateZoomPosition(selectionRect.Location, containerSize, imageSize);
        float scaleFactor = GetScaleFactor(containerSize, imageSize);

        SizeF selectionTrueSize = new SizeF(selectionRect.Width / scaleFactor, selectionRect.Height / scaleFactor);
        return new RectangleF(selectionTrueOrigin, selectionTrueSize);
    }

    public RectangleF TranslateSelectionToZoomedSel(RectangleF selectionRect, SizeF containerSize, SizeF imageSize)
    {
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        RectangleF zoomedSelectionRect = new
            RectangleF(selectionRect.X * scaleFactor, selectionRect.Y * scaleFactor,
                       selectionRect.Width * scaleFactor, selectionRect.Height * scaleFactor);

        PointF imageScaledOrigin = GetImageScaledOrigin(containerSize, imageSize);
        zoomedSelectionRect.Location = new PointF(zoomedSelectionRect.Location.X + imageScaledOrigin.X,
                                                  zoomedSelectionRect.Location.Y + imageScaledOrigin.Y);
        return zoomedSelectionRect;
    }

    public PointF TranslateCoordinatesOrigin(PointF coordinates, SizeF containerSize, SizeF imageSize)
    {
        PointF imageOrigin = GetImageScaledOrigin(containerSize, imageSize);
        return new PointF(coordinates.X - imageOrigin.X, coordinates.Y - imageOrigin.Y);
    }

    public PointF GetImageScaledOrigin(SizeF containerSize, SizeF imageSize)
    {
        SizeF imageScaleSize = GetImageScaledSize(containerSize, imageSize);
        return new PointF((containerSize.Width - imageScaleSize.Width) / 2,
                          (containerSize.Height - imageScaleSize.Height) / 2);
    }

    public SizeF GetImageScaledSize(SizeF containerSize, SizeF imageSize)
    {
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        return new SizeF(imageSize.Width * scaleFactor, imageSize.Height * scaleFactor);

    }
    internal float GetScaleFactor(SizeF scaled, SizeF original)
    {
        return (original.Width > original.Height) ? (scaled.Width / original.Width)
                                                  : (scaled.Height / original.Height);
    }
}