Перевести положение прямоугольника в режиме масштабирования Picturebox - PullRequest
0 голосов
/ 16 декабря 2018

Я определяю прямоугольную область на изображении и показываю ее пользователю в 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 голосов
/ 16 декабря 2018

Считайте, что это дополнение к Reza Aghaei answer .


Специализированный класс, который предоставляет некоторые вспомогательные инструменты для определения коэффициента масштабирования выбора и переводит выборкоординаты с масштабированными Bitmap координатами.
Эта версия предназначена только для увеличенных изображений.

Класс ZoomFactor предоставляет следующие методы :

PointF TranslateZoomPosition(PointF Coordinates, SizeF ContainerSize, SizeF ImageSize):
возвращает PointF переведенные координаты точкирасположение внутри Контейнера до положения Точки внутри Растрового изображения, увеличенного в контейнере.

RectangleF TranslateZoomSelection(RectangleF Selection, SizeF ContainerSize, SizeF ImageSize):
возвращает RectangleF, представляющее выделение, созданное внутри Контейнера, переведенноек координатам растрового изображения.

RectangleF TranslateSelectionToZoomedSel(RectangleF SelectionRect, SizeF ContainerSize, SizeF ImageSize):
возвращает RectangleF, представляющий предварительно выбранную область исходного растрового изображения, преобразованную в увеличенный фрагментИзображение внутри Контейнера.

PointF GetImageScaledOrigin(SizeF ContainerSize, SizeF ImageSize):
возвращает PointF ссылку на масштабированные исходные координаты Изображения внутри Контейнера.

SizeF GetImageScaledSize(SizeF ContainerSize, SizeF ImageSize):
возвращает ссылку SizeF на изображение при масштабировании внутри контейнера.

Пример использования, показывающий, как обрезать растровое изображение с помощью прямоугольника выбора, созданного внутри элемента управления «Контейнер».Метод 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

Примечание : В этом примере предварительный выбор изображения в книжной ориентации инвертирует Width и Height

Класс 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);
    }
}
0 голосов
/ 16 декабря 2018

Вы можете перевести выбранный прямоугольник в графическом окне в прямоугольник на изображении следующим образом:

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 метод исходный код здесь и использовать егокак написать такой метод как часть кода вашего приложения.

Пример - обрезать изображение PictureBox, имеющее SizeMode = Zoom

В качестве примера, следующий код будет обрезать заданныйпрямоугольник графического блока 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

...