How to get real image pixel point x,y from PictureBox - c#

I have a pictureBox2 and it is set to zoom, I am trying to find out how to to get a real x,y pixel location on the image by Mouse.Click on pictureBox2. but I tried 3 possible ideas I knew of: without/with PointToClient,PointToScreen but I can never get it right.
private void pictureBox2_Click(object sender, EventArgs e)
{
MouseEventArgs me = (MouseEventArgs)e;
txtpictureHeight.Text =(
(OriginalImage.GetImageHeight()*me.Location.Y)/ pictureBox2.Image.Height).ToString();
txtpictureWidth.Text = (
(OriginalImage.GetImageWidth()* me.Location.X)/ pictureBox2.Image.Width).ToString();
}
There must be some factor I need to take care of so I thought to use double result from above and I get closed but there is still 80px off for the height on my test image (1371x2221). As I use Zoom so there are 2 extra spaces on my pictureBox2

Note that with SizeMode set to Zoom, the PictureBox keeps aspect ratio, and centers the image, so on top of calculating the adjusted coordinates, you also have to take padding into account.
My advice, don't use the Click event; it is meant to detect button clicks, not to actually process interaction of the mouse with an object. Use MouseDown instead.
The first thing we need to do is get the width and height of the original image. As I noted in my comment, this is simply the object inside the Image property of the PictureBox.
Next, we need the dimensions and location of the zoomed image. For that, we can start from the dimensions of the ClientRectangle of the PictureBox. Divide those by the image width and height and you'll get the horizontal and vertical zoom values. If the SizeMode would be set to StretchImage, that'd be all we need, but since aspect ratio is conserved, you need the smallest of the two values to have the actual zoom factor.
Once we got that, multiply the original width and height by this zoom factor to get the zoomed width and height, then subtract that from the actual ClientRectangle dimensions and divide it by two to get the padding for both dimensions. This can of course be simplified by checking which of the two possible zoom factors is used, and only calculating the padding for the other one, since the dimension of which the zoom factor was used obviously has 0 padding.
Now you got the padding and zoom factor, the rest is simple: subtract the padding values from the mouse coordinates, and then divide both results by the zoom factor.
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
// Default check: left mouse button only
if (e.Button == MouseButtons.Left)
ShowCoords(e.X, e.Y);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
// Allows dragging to also update the coords. Checking the button
// on a MouseMove is an easy way to detect click dragging.
if (e.Button == MouseButtons.Left)
ShowCoords(e.X, e.Y);
}
private void ShowCoords(Int32 mouseX, Int32 mouseY)
{
Int32 realW = pictureBox1.Image.Width;
Int32 realH = pictureBox1.Image.Height;
Int32 currentW = pictureBox1.ClientRectangle.Width;
Int32 currentH = pictureBox1.ClientRectangle.Height;
Double zoomW = (currentW / (Double)realW);
Double zoomH = (currentH / (Double)realH);
Double zoomActual = Math.Min(zoomW, zoomH);
Double padX = zoomActual == zoomW ? 0 : (currentW - (zoomActual * realW)) / 2;
Double padY = zoomActual == zoomH ? 0 : (currentH - (zoomActual * realH)) / 2;
Int32 realX = (Int32)((mouseX - padX) / zoomActual);
Int32 realY = (Int32)((mouseY - padY) / zoomActual);
lblPosXval.Text = realX < 0 || realX > realW ? "-" : realX.ToString();
lblPosYVal.Text = realY < 0 || realY > realH ? "-" : realY.ToString();
}
Note, I used sharp pixel zoom here to better show the effect. It's a little trick you can do by subclassing PictureBox and overriding its OnPaint method, to adjust the Graphics object from the PaintEventArgs object and set its InterpolationMode to NearestNeighbor (It's also advised to set PixelOffsetMode to Half; there's a bug where sharp zoom is shifted half a pixel unless you do that). Then you call base.OnPaint() with that adjusted event args object.
I also added some more info on it here, but that's all just stuff you can get from the in-between values of the pixel coordinates calculation process anyway.

Related

Relocate PictureBox after zoom C#

I have a PictureBox inside a Panel, and I have implemented the zoom function with a TrackBar.
When I increase (or decrease) the PictureBox, the position of the image remain fixed on the left side of the PictureBox.
See the example to better understand the problem.
What I want is the possibility to relocate the image compared to the center of the Panel. See the following example
For example I tried to define the PictureBox X origin in this way:
before the zoom I calculate the distance (Δdx) between the origin of the PictureBox (x0) and the center of the Panel (x1).
I increase the distance with the zoom factor (Δdx').
I calculate the new origin of the image (x0') as x1 - Δdx'
I do the same with Y and I define the new PictureBox location with x0' and y0'.
Here the code:
// new image width after the zoom
double width = pbImg.Image.Width + (pbImg.Image.Width * trackbar.Value / 100);
// new image height after the zoom
double height = pbImg.Image.Height + (pbImg.Image.Height * trackbar.Value / 100);
// panel center
int cX = panel.Width / 2;
int cY = panel.Height / 2;
// actual origin for the picturebox
int imgX = pbImg.Location.X;
int imgY = pbImg.Location.Y;
// distance the panel center and the picturebox origin
int distFromXc = cX - imgX;
int distFromYc = cY - imgY;
// new distance with zoom factor
distFromXc = distFromXc + (distFromXc * trackbar.Value / 100);
distFromYc = distFromYc + (distFromYc * trackbar.Value / 100);
// new origin point for the picturebox
int pbX = (cX - distFromXc);
int pbY = (cY - distFromYc);
// new dimension for the picturebox
pbImg.Size = new Size(Convert.ToInt32(width), Convert.ToInt32(height));
// relocate picturebox
Point p = new Point(pbX, pbY);
pbImg.Location = p;
I tried to modify this C# code, but I’m not familiar with it.
In my case I want to manage the Picturebox and the image inside it as the same object (if it’s possible).
What I want is the possibility to increase (or decrease) the Picturebox (and the image inside) but I want the Picturebox to stay centered where it currently is.
The SizeMode of the picture is StretchImage.
The Trackbar has 0% as he minimum value and 100% as the maximum.
The size of the Picturebox, and the image isnside, can be variable, I receive the images from another software.
A zoomed Picturebox can be bigger than Panel, but it’s not a problem, because I can move it.
The problems are the following:
1. If i use the code I wrote above, the reposition seems to work, but the Picturebox isn’t resized.
2. If I use a fixed value for the origin of the Picturebox (for example Point p = new Point(50, 50)), the resize works but obviously the position of the Picturebox is fixed.
This is because you are changing the size of the picturebox and not the size of the image within it. To ensure an image matches the size of the picture box ensure you set the stretchimage sizemode
pbImg.SizeMode = PictureBoxSizeMode.StretchImage
to get it working you could add this line just before you change the size of the picture box, however i recommend setting this on picturebox during its creation.
Refer to : Fit Image into PictureBox
If you want the PictureBox to stay centered where it currently is, and only expand or deflate "in place", then try something like this:
double width = pbImg.Width * trackbar.Value / 100;
double height = pbImg.Height * trackbar.Value / 100;
Rectangle rc = pbImg.Bounds;
rc.Inflate((int)((width - pbImg.Width) / 2), (int)((height - pbImg.Height) / 2));
pbImg.Bounds = rc;
Note that this is all based on the size of the PictureBox itself, NOT the Image within. Not sure what SizeMode you have set for the PB...
---------- EDIT ----------
I'm using StretchImage as SizeMode. Is possible to have the same behavior but without the button? When I move the cursor from left to right the Pb increase and decrease from right to left – Scarj
Of course. Put my code into the ValueChanged() and/or Scroll() events. - Idle_Mind
The original post works off the current size of the PictureBox. You might want to store the original Bounds() of the PB (maybe in the Tag() property) and then always compute the new size based on that instead.
Here's an example of that:
private void Form1_Load(object sender, EventArgs e)
{
pbImg.Tag = pbImg.Bounds;
}
private void button1_Click(object sender, EventArgs e)
{
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
ZoomPB();
}
private void trackBar1_ValueChanged(object sender, EventArgs e)
{
ZoomPB();
}
private void ZoomPB()
{
Rectangle rc = (Rectangle)pbImg.Tag;
double width = rc.Width * trackbar.Value / 100;
double height = rc.Height * trackbar.Value / 100;
rc.Inflate((int)((width - rc.Width) / 2), (int)((height - rc.Height) / 2));
pbImg.Bounds = rc;
}

How to get mouse coordinates between two points in a picture box

Can anyone help me on this?
I have a picture box with a image and this image have some coordinates.
My X starts at 60 and end at 135
My Y stats at 75 and end at 120
Because i have only the first and the last point, i want to calculate and see the coordinates when i mouse over my image.
I started with solving my first problem: i have to delimitate my start and my end.
So i tried a trackbar.
Im trying first get the current X position:
Set my picturebox at position x = 0;
Set my trackbar at position x = -10, so my first pin will be at position 0;
Set my tracbar size.x = picturebox.x + 20, so my last pin will be at end of picture box.
My trackbar have the current properties:
Minimum = 60, Maximum = 135;
Set a mouse move event in my picturebox:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
double dblValue;
dblValue = ((double)e.X/ (double)trackBar1.Width) * (trackBar1.Maximum - trackBar1.Minimum);
dblValue = dblValue + 60;
trackBar1.Value = Convert.ToInt32(dblValue);
lblX.Text = dblValue.ToString();
}
It's almost working, but still not very accurate.
Anyone have some idea that may work?
I'm not exactly sure what it is you are trying to do, but if you are trying to get to the coordinates within the picturebox, there is a function on the pictureBox class called PointToClient(Point) that computes the location of the specified screen point into client coordinates. You can use the X and Y coordinates from the MouseEventArgs to create the Point object to pass to the function.
To clarify:
The X and Y properties of the MouseEventArgs in the MouseMove event are the screen coordinates (0,0) being the upper left corner of the screen.
Many controls like the PictureBox control include a PointToClient method that will convert the screen coordinates to the local control's coordinates, where (0,0) will be the upper left corner of the control.
So, for example, if your control is placed on the screen at location (60, 75) and has a bottom right coordinate of (135, 120). If your mouse is over the control, and is 10 pixels from the left and 20 pixels from the top, then the X and Y properties of the MouseEventArgs in the MouseMove event would be: X = 70 and Y = 95. If you convert these to the internal coordinates of the picturebox control using PointToClient, it will indicate that X = 10 and Y = 20.
Now, if you were wanting to have a TrackBar that displays a relative indication of where the X coordinate of the mouse is over some control, you would calculate it as follows:
// Set the minimum and maximum of the trackbar to 0 and 100 for a simple percentage.
trackBar1.Minimum = 0;
trackBar1.Maximum = 100;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = pictureBox1.PointToClient(new Point(e.X, e.Y)).X * 100 / pictureBox1.Width;
If you wanted to have the trackbar use screen coordinates to track the relative position of the X coordinate of the mouse over some control, you would calculate it as follows:
// Set the minimum and maximum values of the track bar to the screen coordinates of the
// control we want to track.
trackBar1.Minimum = pictureBox1.PointToScreen(0,0).X;
trackBar1.Maximum = pictureBox1.PointToScreen(pictureBox1.Width, 0).X;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = e.X;
If you wanted to have the trackbar use the internal coordinates of some control to track the internal position of the X coordinate of the mouse over that control, you would calculate it as follows:
// Set the minimum and maximum values of the track bar to zero and the width of the
// control we want to track.
trackBar1.Minimum = 0;
trackBar1.Maximum = pictureBox1.Width;
// In the pictureBox1_MouseMove event have the following code:
trackBar1.Value = pictureBox1.PointToClient(new Point(e.X, e.Y)).X;
// or - not recommended - read below.
trackBar1.Value = e.X - pictureBox1.Left;
Now, there is one caveat, and that is if you put controls inside other controls, like a panel inside a panel inside a panel, etc. then the 'world' coordinates of a control inside of another control are based upon their location within the parent control. That is why it is a good idea to use the internal coordinates of the control via PointToClient and screen coordinates from internal coordinates via PointToScreen because otherwise you would have to work your way up through all of the containers until you reached the screen, keeping track of Top and Left coordinates all the way.
I hope this answers your question.

Recompute Panel AutoScrollPosition after zoom

Have C# forms application with PictureBox embedded in Panel, to take advantage of Panel AutoScroll as suggested in other posts when image and thus PictureBox need to be scrolled horizontally or vertically. Want to zoom the image and recompute AutoScrollPosition to keep same Point visible after zooming. Can double size of PictureBox, then recopy source image, accomplishing zoom. But AutoScrollPosition remains unchanged, thus what was visible before zoom has moved off screen.
How to recompute AutoScrollPosition to keep image focus after zoom?
There are three typical types of zooming:
zoom into the center, triggered by zoom buttons
zoom into the mouse position, triggered by clicking or scroll-wheeling
zoom into a rectangle, by drawing a rectangle
I assume the typical setup: A PictureBox set to SizeMode=Zoom nested in a Panel with AutoScroll=true and zooming that takes care to keep the aspect ratios of Image and PictureBox equal.
Let's start by introducing terminology:
There is an Image we call bitmap and
it is displayed by a PictureBox; let's call it canvas..
.. which is nested in a Panel we call frame
User-friendly zooming needs a fixed point, that is a point that shall stay put.
For 1) it is the center of the frame, for 2) it is the mouse location and for 3) it is the center of the rectangle.
Before zooming we calculate the old zoom ratio, the fixed point in the frame, the fixed point in the canvas and finally the fixed point in the bitmap.
After zoming we calculate the new zoom ratio and the new fixed point in the canvas. Finally we use it to move the canvas to bring the fixed canvas point to the fixed frame point.
Here is an example for zooming into the (current) center; it is a common click event for two buttons and it only doubles and halves the zoom ratio.
Much finer grained factors are of course simple to implement; even better is a fixed list of zoom levels, like Photoshop has!
private void zoom_Click(object sender, EventArgs e)
{
PictureBox canvas = pictureBox1;
Panel frame = panel1;
// Set new zoom level, depending on the button
float zoom = sender == btn_ZoomIn ? 2f : 0.5f;
// calculate old ratio:
float ratio = 1f * canvas.ClientSize.Width / canvas.Image.Width;
// calculate frame fixed pixel:
Point fFix = new Point( frame.Width / 2, frame.Height / 2);
// calculate the canvas fixed pixel:
Point cFix = new Point(-canvas.Left + fFix.X, -canvas.Top + fFix.Y );
// calculate the bitmap fixed pixel:
Point iFix = new Point((int)(cFix.X / ratio),(int)( cFix.Y / ratio));
// do the zoom
canvas.Size = new Size( (int)(canvas.Width * zoom), (int)(canvas.Height * zoom) );
// calculate new ratio:
float ratio2 = 1f * canvas.ClientSize.Width / canvas.Image.Width;
// calculate the new canvas fixed pixel:
Point cFix2 = new Point((int)(iFix.X * ratio2),(int)( iFix.Y * ratio2));
// move the canvas:
canvas.Location = new Point(-cFix2.X + fFix.X, -cFix2.Y + fFix.Y);
}
Note that while one can try to restore the relative AutoScrollValues this is not only hard, because their values are a little quirky but it is also won't be adaptable to the other zoom types.

PictureBox zoom miscalculation

I am attempting to zoom in a picturebox using the mousewheel.
Using the following variables:
public static int offsetX = 0;
public static int offsetY = 0;
public static double scale = .05;
I draw a series of polygons to the picture box. However, I wanted the bottom left corner to refer to 0,, so I draw everything to the form with a -y. The drawn points have been affected by the above variables but the real points stay the same.
void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
scale += .025;
else
scale -= .025;
pictureBox1.Invalidate();
}
When the mousewheel is moved forward I increase the scale variable and the box is refreshed. It is repainted using this code in the picturebox paint method:
Graphics g = e.Graphics;
foreach (Member m in activeTruss.members)
{
if (m.Visible == true)
{
Point[] pointArray = new Point[m.poly.Points.Count()];
int index = 0;
foreach (System.Windows.Point p in m.poly.Points)
{
pointArray[index].X = (int)((p.X + offsetX) * scale);
pointArray[index].Y = (int)-((p.Y + offsetY) * scale);
index++;
}
SolidBrush myBrush = new SolidBrush(m.color);
g.FillPolygon(myBrush, pointArray);
}
}
it zooms the correct amount, however it appears to zoom towards the upper left corner because the offsets stay the same. The opposite is true when zooming out. How should I edit the offsets as I turn the mouse wheel in order to zoom directly towards the point under my mouse?
If you want to center your zoom
double offsetX = scale/2
double offsetY = scale/2
Update
To answer your second part about flipped picture, it sounds like you are not making a distinction on the x,y of your picture vs the x,y screen coordinates. Usually, screen coordinates start on the top of the screen, so top left is 0,0, however when you draw your picture, your picture's 0,0 is on the bottom left so you use -y. What you want to do is to start drawing from 0,0 to 0,MAXY without the flip. For this implementation detail, your code would help alot hehe :)

C# Mouse position over pictureBox

How can I know at what pixel of a pictureBox the mouse is placed (coordinates)?
Catch mouse move event:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Text = String.Format("X: {0}; Y: {1}", e.X, e.Y);
}
You have X and Y position there.
If your image has been zoomed and/or panned, rememeber you have to apply transformations on those coords.
To be clear: if your image has been placed on (x0,y0) and been zoomed with zf (remember that zf<1 means reduced), pixel coords will be
px = (e.X - x0) / zoom;
py = (e.Y - y0) / zoom;
I think that the question is a little bit vague since you did not tell us what exactly are you planning to do nor what you have effectively tried.
The Control.PointToClient method seems to do what you need:
Computes the location of the specified screen point into client
coordinates.
You can then use the Bitmap.GetPixel and use the X-Y coordinates to get the pixel at the given mouse co-ordinates:
Gets the color of the specified pixel in this Bitmap
All of this can be triggered by a Mouse_Over event, Mouse_Click, etc.
If you determine the color inside a MouseEvent, you can just use the coordinates provided from MouseEventArgs
// Declare a Bitmap
Bitmap mybitmap;
// Load Picturebox image to bitmap
mybitmap = new Bitmap(pictureBox1.Image);
// In the mouse move event
var pixelcolor = mybitmap.GetPixel(e.X, e.Y);
// Displays R / G / B Color
pixelcolor.ToString()
There is a static method on the Mouse class that allows you to get the position of the mouse pointer relative to another element . Look at Mouse.GetPosition(UIElement).
Here's how you use it.
Point point = Mouse.GetPosition(pictureBox);
Debug.WriteLine("X: " + point.X +"\n Y: "+ point.Y);

Categories

Resources