在缩放模式图片框中平移矩形位置

时间:2018-12-16 07:38:58

标签: c# .net winforms picturebox system.drawing

我正在确定图像中的矩形区域,并在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);

但是确定的框架/矩形位置不正确。
我在做什么错了?

2 个答案:

答案 0 :(得分:3)

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

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

这是输入图像:

enter image description here

这是结果:

enter image description here

答案 1 :(得分:2)

请考虑将其添加到Reza Aghaei answer中。


一个专门的类,提供一些帮助工具来确定选择的比例因子,并将选择坐标转换为缩放的Bitmap坐标。
版本仅适用于缩放的图像。

ZoomFactor类提供了以下方法

PointF TranslateZoomPosition(PointF Coordinates, SizeF ContainerSize, SizeF ImageSize)
将容器内某个Point位置的PointF转换后的坐标返回到位图内该Point位置的缩放坐标。

RectangleF TranslateZoomSelection(RectangleF Selection, SizeF ContainerSize, SizeF ImageSize)
返回RectangleF,代表在Container中创建的选择,并转换为位图坐标。

RectangleF TranslateSelectionToZoomedSel(RectangleF SelectionRect, SizeF ContainerSize, SizeF ImageSize)
返回 RectangleF ,代表原始位图的预选区域,该区域已转换为容器内缩放后的选择图像。

PointF GetImageScaledOrigin(SizeF ContainerSize, SizeF ImageSize)
返回容器内缩放的图像原点坐标的PointF参考。

SizeF GetImageScaledSize(SizeF ContainerSize, SizeF ImageSize)
在容器内缩放时,返回图像的SizeF参考。

示例用法,显示如何使用在Container控件内创建的选择Rectangle裁剪位图。 TranslateZoomSelection方法返回与选择区域相对应的Bitmap部分:

ZoomFactor ZoomHelper = new ZoomFactor()
Bitmap originalBitmap;

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

上述行为的示例

PictureBox Zoom Selection

注意在该示例中,“人像”中的图像预选择会反转WidthHeight

ZoomFactor

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