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);
Related
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;
}
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.
I have a PictureBox inside a Panel in order to zoom and pan. I created the possibility to select 4 points with the mouse click and draw a rectangle on the PictureBox. Once the rectangle is over my picture I pass the coordinates of the rectangle to the method "cropRectangle". This method crops the rectangle and replace the old image with the cropped one. This works very well:
(OriginalImage is the bitmap of the actual image in the pictureBox)
private void cropRectangle(Rectangle rect){
double left = (rect.X) * originalImage.Width / pictureBox.Width,
top = (rect.Y) * originalImage.Width / pictureBox.Height,
right = (rect.Width) * originalImage.Width / pictureBox.Width,
bottom = (rect.Height) * originalImage.Height / pictureBox.Height;
rect = new Rectangle (Convert.ToInt32(left), Convert.ToInt32(top), Convert.ToInt32(right), Convert.ToInt32(bottom));
Bitmap bitmap = orignalImage.Clone(rect, originalImage.PixelFormat);
pictureBox.Image = (Image)bitmap;
centerPictureBox();
// fit image into pictureBox with respect to the ratio
float ratio = orignalImage.Width / orignalImage.Height;
pictureBox.Width = panel.Width;
pictureBox.Height = Convert.ToInt32(pictureBox.Width * ratio);
centerPictureBox();
}
What I am trying to do now is to zoom the selected area instead to crop it. The rectangle of the picturebox has to match with the panel.
How can I show only the selected area (rectangle) of the picturebox through the panel without cropping the image?
You should stick with modifying the existing Bitmap using the Graphics object instead of changing the size of the PictureBox. You don't want to be tied to a UI control when the desired functionality is already available elsewhere.
Here are rough steps to achieve that:
Create a temporary Bitmap object that will store the zoomed image. Bitmap tBitmap = new Bitmap(zoomX, zoomY, PixelFormat.Format24bppRgb);
Calculate the zoom factors and stuff like you already do (I didn't check if the code is correct but I assume that it is) when you want to zoom.
Create a new Graphics object from the temporary bitmap. Graphics graphics = Graphics.FromImage(tBitmap);
Set the InterpolationMode so that the image is scaled with a good quality. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Draw the zoomed image using the DrawImage method (using the original image from the PictureBox). graphics.DrawImage(pictureBox.Image, new Rectangle(0, 0, pictureBox.Width, pictureBox.Height), new Rectangle(/*The crop rectangle you draw already*/), GraphicsUnit.Pixel);
Set the newly drawn bitmap as the Image in your PictureBox. pictureBox.Image = tBitmap;
Remember to dispose of the graphics object we used for drawing. graphics.Dispose();
You might need to refresh the PictureBox to force it to redraw itself. pictureBox.Refresh();
Those are the basic steps to follow. I didn't have time to go through your existing code that deeply so you might need to change some additional things to make it work.
Here's also an MSDN article that covers the same stuff: Cropping and Scaling Images in GDI+
You might be interested in this control (ZoomPicBox) which allows for zooming and panning a picture box.
All credit for this code is Bob Powell and it was taken from his site (which seems to be down now and has been for a long time now.).
I copied the code from archive.org at this link:
https://web.archive.org/web/20080313161349/http://www.bobpowell.net/zoompicbox.htm
That link has additional information and is worth a read. The code is available in VB.Net as well.
I don't know why Bob Powell's site is down, but it was a great site for Windows Graphics information.
I felt this code was worth repeating. This control can be dragged onto the form.
namespace bobpowell.net
{
/// <summary>
/// ZoomPicBox does what it says on the wrapper.
/// </summary>
/// <remarks>
/// PictureBox doesn't lend itself well to overriding. Why not start with something basic and do the job properly?
/// </remarks>
public class ZoomPicBox : ScrollableControl
{
Image _image;
[
Category("Appearance"),
Description("The image to be displayed")
]
public Image Image
{
get{return _image;}
set
{
_image=value;
UpdateScaleFactor();
Invalidate();
}
}
float _zoom=1.0f;
[
Category("Appearance"),
Description("The zoom factor. Less than 1 to reduce. More than 1 to magnify.")
]
public float Zoom
{
get{return _zoom;}
set
{
if(value<0 || value<0.00001)
value=0.00001f;
_zoom=value;
UpdateScaleFactor();
Invalidate();
}
}
/// <summary>
/// Calculates the effective size of the image
///after zooming and updates the AutoScrollSize accordingly
/// </summary>
private void UpdateScaleFactor()
{
if(_image==null)
this.AutoScrollMinSize=this.Size;
else
{
this.AutoScrollMinSize=new Size(
(int)(this._image.Width*_zoom+0.5f),
(int)(this._image.Height*_zoom+0.5f)
);
}
}
InterpolationMode _interpolationMode=InterpolationMode.High;
[
Category("Appearance"),
Description("The interpolation mode used to smooth the drawing")
]
public InterpolationMode InterpolationMode
{
get{return _interpolationMode;}
set{_interpolationMode=value;}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing.
}
protected override void OnPaint(PaintEventArgs e)
{
//if no image, don't bother
if(_image==null)
{
base.OnPaintBackground(e);
return;
}
//Set up a zoom matrix
Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);
//now translate the matrix into position for the scrollbars
mx.Translate(this.AutoScrollPosition.X / _zoom, this.AutoScrollPosition.Y / _zoom);
//use the transform
e.Graphics.Transform=mx;
//and the desired interpolation mode
e.Graphics.InterpolationMode=_interpolationMode;
//Draw the image ignoring the images resolution settings.
e.Graphics.DrawImage(_image,new Rectangle(0,0,this._image.Width,this._image.Height),0,0,_image.Width, _image.Height,GraphicsUnit.Pixel);
base.OnPaint (e);
}
public ZoomPicBox()
{
//Double buffer the control
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer, true);
this.AutoScroll=true;
}
}
}
I have found a elegant solution to my problem:
private void zoomInsideRectangle(Rectangle rect){
float zoomFactor = ((float)panel.Width / rect.Width) - 1;
pictureBox.Width = pictureBox.Width + convertToIntPerfect(pictureBox.Width * zoomFactor);
pictureBox.Height = pictureBox.Height + convertToIntPerfect(pictureBox.Height * zoomFactor);
rect.X = rect.X + convertToIntPerfect(rect.X * zoomFactor);
rect.Y = rect.Y + convertToIntPerfect(rect.Y * zoomFactor);
pictureBox.Left = convertToIntPerfect(-rect.X);
pictureBox.Top = convertToIntPerfect(-rect.Y);
}
Since I know the length of the panel where I can see the picturebox. I take the ratio of the panel and the width of my rectangle that I want to zoom in. This ratio is my zoomratio.
I multiply the size of the picturebox with the ratio that I calculated.
I anchor the picturebox by the left and top with the coordinates of my rectangle. But right before doing that I have to multiply my coordinates of my rectangle with the zoomratio since I changed the size of the picturebox.
I didn't implemented the Y transformation since the original ratio of the image will be damaged.
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 :)
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.