32541

Translate Rectangle Position in Zoom Mode Picturebox

Question:

I'm determining the rectangular area in an image and showing it to the user in a PictureBox.<br /> Since the image can sometimes be very large, I'm using a PictureBox with its SizeMode set to Zoom.

I'm using the following code to translate the Rectangle (X, Y) coordinates:

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

Adding a frame control at the determined position:

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

But the determined frame/rectangle location is not correct.<br /> What am I i doing wrong?

Answer1:

You can translate selected rectangle on the picture box to the rectangle on image this way:

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

Note: You can find ImageRectangleFromSizeMode method <a href="https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/PictureBox.cs,5c2ab37313f547c2" rel="nofollow">source code here</a> and use it as write such method as part of your application code.

<strong>Example - Crop Image of PictureBox having SizeMode = Zoom</strong>

As an example, the following code will crop the given rectangle of the picture box 1 and will set the result as image of picture box 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(); }

Here is the input image:

<a href="https://i.stack.imgur.com/vk49I.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/vk49I.png" data-original="https://i.stack.imgur.com/vk49I.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

And this is the result:

<a href="https://i.stack.imgur.com/UEyYr.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/UEyYr.png" data-original="https://i.stack.imgur.com/UEyYr.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

Answer2:

Consider this an addition to <a href="https://stackoverflow.com/a/53800590/7444103" rel="nofollow">Reza Aghaei answer</a>.<br />

<hr />

A specialized class that provides some helper tools to determine the scaling factor of a selection and translates the selection coordinates to the scaled Bitmap coordinates.<br /> This <em>version</em> is for zoomed images only.

<strong>The ZoomFactor class provides these methods</strong>:

<strong>PointF TranslateZoomPosition(PointF Coordinates, SizeF ContainerSize, SizeF ImageSize)</strong>:<br /> returns the PointF translated coordinates of a Point location inside a Container to the Point location inside a Bitmap, zoomed in the container.

<strong>RectangleF TranslateZoomSelection(RectangleF Selection, SizeF ContainerSize, SizeF ImageSize)</strong>:<br /> returns a RectangleF representing a selection created inside a Container, translated to the Bitmap coordinates.

<strong>RectangleF TranslateSelectionToZoomedSel(RectangleF SelectionRect, SizeF ContainerSize, SizeF ImageSize)</strong>:<br /> returns a <strong>RectangleF</strong> representing a pre-selected area of the original Bitmap translated to the zoomed selection Image inside a Container.

<strong>PointF GetImageScaledOrigin(SizeF ContainerSize, SizeF ImageSize)</strong>:<br /> returns the PointF reference of the zoomed Image origin coordinates inside the Container.

<strong>SizeF GetImageScaledSize(SizeF ContainerSize, SizeF ImageSize)</strong>:<br /> returns the SizeF reference of the Image when scaled inside the Container.

Sample usage, showing how to crop a Bitmap using a selection Rectangle created inside a Container control. The TranslateZoomSelection method returns the Bitmap section corresponding to a selection area:<br />

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

<strong>A Sample of the behaviour described above</strong>:<br />

<a href="https://i.stack.imgur.com/ofrrW.gif" rel="nofollow"><img alt="PictureBox Zoom Selection" class="b-lazy" data-src="https://i.stack.imgur.com/ofrrW.gif" data-original="https://i.stack.imgur.com/ofrrW.gif" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<strong>Note</strong>: <em>In the example, the pre-selection of the image in Portrait inverts Width and Height</em>

<strong>The ZoomFactor class</strong>:

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

Recommend

  • iphone - how to scroll uiscrollview from event touched by uibutton in scrollview
  • How to rotate object around local axis in OpenGL?
  • JSON - Nesting children RABL or JBuilder for Rails
  • What is the “center” of a Three.js object?
  • Impossible to acquire and present in parallel with rendering?
  • Picasso Taking time to load images
  • Want to understand iframe breakout code
  • A new chart every sheet
  • Universal Image Loader reuse images
  • PayPal API Listener Website Payments Standard URI
  • Why does java tzupdater add leap seconds?
  • How can I tell a form not to dispose a particular control when it closes?
  • WPF version of .ScaleControl?
  • Private IP address in reserved subnet range
  • Clear fused location provider's location for testing
  • Clarification on min distance on LocationManager.requestLocationUpdates method, min Distance paramet
  • Overlapping controls in Windows XP
  • accepts_nested_attributes_for practical form use for in Rails 3
  • Intel-64 and ia32 atomic operations acquire-release semantics and GCC 5+
  • Zoom in and out of jPanel
  • IE7 and TinyMCE with Plone
  • wxPython: displaying multiple widgets in same frame
  • Firefox Extension - Monitor refresh and change of tab
  • Saving Changes After In-App Purchase Has Been Purchased
  • Assign variable to the value in HTML
  • Blackberry - Custom EditField Cursor
  • How to use carriage return with multiple line?
  • Body moving without any force applied? (Box2d)
  • R - Combining Columns to String Based on Logical Match
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • DirectX11 ClearRenderTargetViewback with transparent buffer?
  • Perl system calls when running as another user using sudo
  • To display the title for the current loaction in map in iphone
  • How to delete a row from a dynamic generate table using jquery?
  • using HTMLImports.whenReady not working in chrome
  • embed rChart in Markdown
  • Android Google Maps API OnLocationChanged only called once
  • Authorize attributes not working in MVC 4
  • EntityFramework adding new object to nested object collection
  • Unable to use reactive element in my shiny app