I made a program that lets you do a random selection then the selected area to save into a new image, but I got a problem it doesn't work how it's supposed to... I will post my code here so you can have a look:
private List<Point> Points = null;
private bool Selecting = false;
private Bitmap SelectedArea = null;
private void pictureBox5_MouseDown(object sender, MouseEventArgs e)
{
Points = new List<Point>();
Selecting = true;
}
private void pictureBox5_MouseMove(object sender, MouseEventArgs e)
{
if (!Selecting) return;
Points.Add(new Point(e.X, e.Y));
pictureBox5.Invalidate();
}
private void pictureBox5_MouseUp(object sender, MouseEventArgs e)
{
Selecting = false;
// Copy the selected area.
SelectedArea = GetSelectedArea(pictureBox5.Image, Color.Transparent, Points);
SelectedArea.Save(#"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.jpeg", ImageFormat.Jpeg);
string filename = #"C:\Users\User\Desktop\Gallery\image" + NumberOfClick.ToString() + "cropped.jpeg";
if(File.Exists(filename))
{
button1.Visible = true;
pictureBox5.Visible = false;
}
}
private void pictureBox5_Paint(object sender, PaintEventArgs e)
{
if ((Points != null) && (Points.Count > 1))
{
using (Pen dashed_pen = new Pen(Color.Black))
{
dashed_pen.DashPattern = new float[] { 5, 5 };
e.Graphics.DrawLines(Pens.White, Points.ToArray());
e.Graphics.DrawLines(dashed_pen, Points.ToArray());
}
}
}
private Bitmap GetSelectedArea(Image source, Color bg_color, List<Point> points)
{
// Make a new bitmap that has the background
// color except in the selected area.
Bitmap big_bm = new Bitmap(source);
using (Graphics gr = Graphics.FromImage(big_bm))
{
// Set the background color.
gr.Clear(bg_color);
// Make a brush out of the original image.
using (Brush br = new TextureBrush(source))
{
// Fill the selected area with the brush.
gr.FillPolygon(br, points.ToArray());
// Find the bounds of the selected area.
Rectangle source_rect = GetPointListBounds(points);
// Make a bitmap that only holds the selected area.
Bitmap result = new Bitmap(
source_rect.Width, source_rect.Height);
// Copy the selected area to the result bitmap.
using (Graphics result_gr = Graphics.FromImage(result))
{
Rectangle dest_rect = new Rectangle(0, 0,
source_rect.Width, source_rect.Height);
result_gr.DrawImage(big_bm, dest_rect,
source_rect, GraphicsUnit.Pixel);
}
// Return the result.
return result;
}
}
}
private Rectangle GetPointListBounds(List<Point> points)
{
int xmin = points[0].X;
int xmax = xmin;
int ymin = points[0].Y;
int ymax = ymin;
for (int i = 1; i < points.Count; i++)
{
if (xmin > points[i].X) xmin = points[i].X;
if (xmax < points[i].X) xmax = points[i].X;
if (ymin > points[i].Y) ymin = points[i].Y;
if (ymax < points[i].Y) ymax = points[i].Y;
}
return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
}
This is how I am doing and saving the cropped images.
And also this is how I am uploading the pictures:
OpenFileDialog f = new OpenFileDialog();
f.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png) | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";
if (f.ShowDialog() == DialogResult.OK)
{
currentImage = Image.FromFile(f.FileName);
pictureBox1.Image = currentImage;
}
pictureBox1.Image.Save(#"C:\Users\User\Desktop\Gallery\image1.jpeg", ImageFormat.Jpeg);
DialogResult result = MessageBox.Show("Crop your image", "Information", MessageBoxButtons.OK);
if(result == DialogResult.OK)
{
pictureBox5.Visible = true;
button1.Visible = false;
pictureBox5.Image = pictureBox1.Image;
}
In pictureBox5 I am selecting and cropping the picture.
mySelection
croppedImage
You need to calculate the zoom and the offset of the image when it is zoomed.
Here is how to do that; this assumes the PictureBox is indeed in Zoom mode, not in Stretch mode. If you stretch it you need to calculate the zooms for x and y separately..
SizeF sp = pictureBox5.ClientSize;
SizeF si = pictureBox5.Image.Size;
float rp = sp.Width / sp.Height; // calculate the ratios of
float ri = si.Width / si.Height; // pbox and image
float zoom = (rp > ri) ? sp.Height / si.Height : sp.Width / si.Width;
float offx = (rp > ri) ? (sp.Width - si.Width * zoom) / 2 : 0;
float offy = (rp <= ri)? (sp.Height - si.Height * zoom) / 2 : 0;
Point offset = Point.Round(new PointF(offx, offy));
You calculate this after setting the Image and after resizing the PictureBox..
Now you can transform each drawn point into a zoomed or an unzoomed coordinate:
PointF zoomed(Point p1, float zoom, Point offset)
{
return (new PointF(p1.X * zoom + offset.X, p1.Y * zoom + offset.Y));
}
PointF unZoomed(Point p1, float zoom, Point offset)
{
return (new PointF((p1.X - offset.X) / zoom, (p1.Y - offset.Y) / zoom));
}
Here is a demo the draws on to either a normal (left) or a zoomed in (middle) image. To the right is the result of placing your GetSelectedArea bitmap onto a PictureBox with a checkerbox background:
Case 1: If you store the points as they come in: In your GetSelectedArea method use this point list instead:
private Bitmap GetSelectedArea(Image source, Color bg_color, List<Point> points)
{
var unzoomedPoints =
points.Select(x => Point.Round((unZoomed(Point.Round(x), zoom, offset))))
.ToList();
// Make a new bitmap that has the background
After this replace each reference to points in the method by one to unzoomedPoints. Actually there are just two of them..
Case 2: If you store the points already 'unZoomed' :
Points.Add(unZoomed(e.Location, zoom, offset));
you can use the list directly..
Related
I have created a program to draw square grids on a selected image. It works fine for images that has small resolution, but it doesn't work properly on large images.
The all grid lines are not visible seem when the image is saved as file.
The image I am testing has resolution 3600x4320 and can be shown in the link.
How can I fix this problem?
My code:
Image drawGrid(int n, string imgPath)
{
Image img = Image.FromFile(imgPath);
Graphics grp = Graphics.FromImage(img);
grp.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
float m = img.Width * 1f / n;
for (int i = 1; i < n; i++)
{
var x = new PointF(i * m, 0);
var y = new PointF(i * m, img.Height);
grp.DrawLine(Pens.Red, x, y);
}
for (int i = 1; i <= (int)(this.Height / m); i++)
{
var x = new PointF(0, i * m);
var y = new PointF(img.Width, i * m);
grp.DrawLine(new Pen(Color.Red, 5f), x, y);
}
return img;
}
void BtnExportClick(object sender, EventArgs e)
{
if(saveFileDialog1.ShowDialog() == DialogResult.OK)
{
int n = (int)numericUpDown1.Value;
drawGrid(n, txtImagePath.Text).Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
MessageBox.Show("Done");
}
}
The result image is below (resolution reduced to upload)
The grid lines not shown correctly.
The major problem is in this line:
for (int i = 1; i <= (int)(this.Height / m); i++)
▶ this.Height is clearly not what you wanted to write, let's replace it with the Image.Height
▶ grp.DrawLine(Pens.Red, x, y); and grp.DrawLine(new Pen(Color.Red, 5f), x, y); will draw lines of different size (1 and 5 pixels). In the sample code, the two methods accept Color and float arguments that define the Pen color and size.
▶ grp.SmoothingMode: we don't want any smoothing mode here, not needed to draw straight lines and it will add anti-alias which will be clearly visible, especially when saving the Image in JPEG format (it will anti-alias - sort of, it actually mangles the colors - these lines by itself).
▶ You're not disposing of any of the Graphics object you create. This is quite important with both frequent graphics operations and when working with large Bitmaps.
The Pen and Graphics objects needs to be disposed.
Since it's not exactly clear if you want to generate a grid that has the same number of lines in both dimensions - hence, a grid with Cells in which the Width is not equal to the Height, most probably - or a grid with squared Cells (this is what the sample Image seems to show, not the code), I posted two method that draw both grid types:
First method, same number of lines in both width and height:
var gridSizeX = (float)image.Width / lines;
var gridSizeY = (float)image.Height / lines;
private Image DrawGridLines(int lines, string imgPath, Color penColor, float penSize)
{
var image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgPath)), true);
using (var g = Graphics.FromImage(image)) {
g.PixelOffsetMode = PixelOffsetMode.Half;
var gridSizeX = (float)image.Width / lines;
var gridSizeY = (float)image.Height / lines;
for (int i = 1; i < lines; i++) {
var pointX1 = new PointF(0, i * gridSizeY);
var pointX2 = new PointF(image.Width, i * gridSizeY);
var pointY1 = new PointF(i * gridSizeX, 0);
var pointY2 = new PointF(i * gridSizeX, image.Height);
using (var pen = new Pen(penColor, penSize)) {
g.DrawLine(pen, pointX1, pointX2);
g.DrawLine(pen, pointY1, pointY2);
}
}
return image;
}
}
Second method, drawing a squared grid. The integer value, gridSection, is used to define a grid Cell based on the minimum dimension of the Bitmap.
This dimension is then used to determine how many lines to draw in the other dimension.
The grid size is calculated on the minimum dimension:
var gridSize = (float)Math.Min(image.Width, image.Height) / gridSection;
And the Cell are determined as a consequence:
var gridStepMin = Math.Min(image.Width, image.Height) / gridSize;
var gridStepMax = Math.Max(image.Width, image.Height) / gridSize;
private Image DrawSquaredGrid(int gridSection, string imgPath, Color penColor, float penSize)
{
var image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imgPath)), true);
using (var g = Graphics.FromImage(image)) {
g.PixelOffsetMode = PixelOffsetMode.Half;
var gridSize = (float)Math.Min(image.Width, image.Height) / gridSection;
var gridStepMin = Math.Min(image.Width, image.Height) / gridSize;
var gridStepMax = Math.Max(image.Width, image.Height) / gridSize;
for (int i = 1; i < gridStepMin; i++) {
var pointY1 = new PointF(i * gridSize, 0);
var pointY2 = new PointF(i * gridSize, image.Height);
using (var pen = new Pen(penColor, penSize)) {
g.DrawLine(pen, pointY1, pointY2);
}
}
for (int i = 1; i < gridStepMax; i++) {
var pointX1 = new PointF(0, i * gridSize);
var pointX2 = new PointF(image.Width, i * gridSize);
using (var pen = new Pen(penColor, penSize)) {
g.DrawLine(pen, pointX1, pointX2);
}
}
return image;
}
}
The SaveFileDialog is refactored to allow multiple Image formats. and to call one of the drawing methods based on a selection (in the sample code, a CheckBox (chkSquared) is used select one of the Grid types).
You can add more formats, the ImageFormatFromFileName() methods selects the ImageFormat type based on the SaveFileDialog.FielName extension.
private void BtnExportClick(object sender, EventArgs e)
{
string imagePath = [Some Path];
using (var sfd = new SaveFileDialog()) {
sfd.Filter = "PNG Image (*.png)|*.png|TIFF Image (*.tif)|*.tif|JPEG Image (*.jpg)|*.jpg";
sfd.RestoreDirectory = true;
sfd.AddExtension = true;
if (sfd.ShowDialog() == DialogResult.OK) {
Image image = null;
if (chkSquared.Checked) {
image = DrawSquaredGrid((int)numericUpDown1.Value, imagePath, Color.Red, 5.0f);
}
else {
image = DrawGridLines((int)numericUpDown1.Value, imagePath, Color.Red, 5.0f);
}
image.Save(sfd.FileName, ImageFormatFromFileName(sfd.FileName));
MessageBox.Show("Done");
image.Dispose();
}
}
}
private ImageFormat ImageFormatFromFileName(string fileName)
{
string fileType = Path.GetExtension(fileName).Remove(0, 1);
if (fileType.Equals("tif")) fileType = "tiff";
if (fileType.Equals("jpg")) fileType = "jpeg";
return (ImageFormat)new ImageFormatConverter().ConvertFromString(fileType);
}
This question already has an answer here:
Draw Rectangle inside picture box SizeMode Zoom
(1 answer)
Closed 3 years ago.
I have a PictureBox1 with it's sizemode set to Stretch and PictureBox1. The PictureBox1 contains an image and let's me select part of it and then crop it and store the cropped part inside PictureBox2. It works great when the sizemode is set to Stretch and the picture is not zoomed, but not when I zoom it or set the sizemode to zoom.
working example - sizemode set to 'stretch'
The code I use to crop part of the picture (original source)
try
{
float stretch1X = 1f * pictureBox1.Image.Width / pictureBox1.ClientSize.Width;
float stretch1Y = 1f * pictureBox1.Image.Height / pictureBox1.ClientSize.Height;
Point pt = new Point((int)(_mDown.X * stretch1X), (int)(_mDown.Y * stretch1Y));
Size sz = new Size((int)((_mCurr.X - _mDown.X) * stretch1X),
(int)((_mCurr.Y - _mDown.Y) * stretch1Y));
if (sz.Width > 0 && sz.Height > 0)
{
Rectangle rSrc = new Rectangle(pt, sz);
Rectangle rDest = new Rectangle(Point.Empty, sz);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
using (Graphics G = Graphics.FromImage(bmp))
G.DrawImage(pictureBox1.Image, rDest, rSrc, GraphicsUnit.Pixel);
return bmp;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
How do I calculate it properly? How can I make the crop function work in a way so it lets the user zoom in/out and still crop the correct part of the picture?
You need to calculate the points using the stretch factor and maybe also the offset.
For Zoom there is only one factor as aspect ratio is always the same for Image and PictureBox, but there usually is an offset; for Stretch you need no offset but two factors.
Here is an example that goes all the way using two PictureBoxes two show a zoomed version and the cropped bitmap. It makes use of an all-purpose function ImageArea that determines size and offset.
Two class level variables:
Point pDown = Point.Empty;
Rectangle rect = Rectangle.Empty;
Three mouse events:
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
pDown = e.Location;
pictureBox1.Refresh();
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (!e.Button.HasFlag(MouseButtons.Left)) return;
rect = new Rectangle(pDown, new Size(e.X - pDown.X, e.Y - pDown.Y));
using (Graphics g = pictureBox1.CreateGraphics())
{
pictureBox1.Refresh();
g.DrawRectangle(Pens.Orange, rect);
}
}
private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Rectangle iR = ImageArea(pictureBox2);
rect = new Rectangle(pDown.X - iR.X, pDown.Y - iR.Y,
e.X - pDown.X, e.Y - pDown.Y);
Rectangle rectSrc = Scaled(rect, pictureBox2, true);
Rectangle rectDest = new Rectangle(Point.Empty, rectSrc.Size);
Bitmap bmp = new Bitmap(rectDest.Width, rectDest.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(pictureBox2.Image, rectDest, rectSrc, GraphicsUnit.Pixel);
}
pictureBox2.Image = bmp;
}
Here is a useful function that returns the area of the actual image inside a picturebox for any sizemode..:
Rectangle ImageArea(PictureBox pbox)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
if (pbox.SizeMode == PictureBoxSizeMode.StretchImage)
return pbox.ClientRectangle;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize)
return new Rectangle(Point.Empty, si);
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
return new Rectangle(new Point((sp.Width - si.Width) / 2,
(sp.Height - si.Height) / 2), si);
// PictureBoxSizeMode.Zoom
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
if (rp > ri)
{
int width = si.Width * sp.Height / si.Height;
int left = (sp.Width - width) / 2;
return new Rectangle(left, 0, width, sp.Height);
}
else
{
int height = si.Height * sp.Width / si.Width;
int top = (sp.Height - height) / 2;
return new Rectangle(0, top, sp.Width, height);
}
}
We only need the offset to determine the rectangle unscaled. We also need to scale it:
Rectangle Scaled(Rectangle rect, PictureBox pbox, bool scale)
{
float factor = GetFactor(pbox);
if (!scale) factor = 1f / factor;
return Rectangle.Round(new RectangleF(rect.X * factor, rect.Y * factor,
rect.Width * factor, rect.Height * factor));
}
For this need to know the scaling factor, which depends on the aspect ratio:
float GetFactor(PictureBox pBox)
{
if (pBox.Image == null) return 0;
Size si = pBox.Image.Size;
Size sp = pBox.ClientSize;
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
float factor = 1f * pBox.Image.Width / pBox.ClientSize.Width;
if (rp > ri) factor = 1f * pBox.Image.Height / pBox.ClientSize.Height;
return factor;
}
This solution will also work if the PictureBox is zoomed in or out by placing it inside a AutoScrolling Panel and changing the Pbox.Size.
I have a picturebox that I want the user of my winform to be able to interact with by zooming and dragging the image. Right now that form looks like this
I have the drag and zoom methods implemented. Those look like this.
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
// If the mouse wheel is moved forward (Zoom in)
if (e.Delta > 0)
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
pictureBox1.Width = (int)(pictureBox1.Width * 1.25);
pictureBox1.Height = (int)(pictureBox1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(e.Y - 1.25 * (e.Y - pictureBox1.Top));
pictureBox1.Left = (int)(e.X - 1.25 * (e.X - pictureBox1.Left));
}
else
{
pictureBox1.Width = (int)(pictureBox1.Width / 1.25);
pictureBox1.Height = (int)(pictureBox1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(e.Y - 0.80 * (e.Y - pictureBox1.Top));
pictureBox1.Left = (int)(e.X - 0.80 * (e.X - pictureBox1.Left));
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
Console.WriteLine("Dragging: false");
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragging = true;
xDrag = e.X;
yDrag = e.Y;
Console.WriteLine("Dragging: true");
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Control c = sender as Control;
if (dragging && c != null)
{
c.Top = e.Y + c.Top - yDrag;
c.Left = e.X + c.Left - xDrag;
Console.WriteLine(e.Location.ToString());
}
}
The dragging works fine but the zooming does not work as expected. Instead of zooming in it pushes the image up and to the left as I "zoom in"
Out of curiousity I loaded a bitmap I saved off Google in to test the zoom and it works fine with bitmap images from files; it just doesn't work with images drawn with the Graphics object and I have no idea why. Any help would be greatly appreciated.
Paint event code for picturebox
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
RunEntry entry = this.passedHistory.SelectedItem as RunEntry;
if (entry == null)
{
return;
}
else if(entry.FileRead != null && checkBox1.Checked) //If ANS is selected and show all, get TRE
{
foreach(RunEntry r in passedHistory.Items)
{
if (r.TreComponentRead != null)
{
string ansName = Path.GetFileNameWithoutExtension(entry.FileName);
string treName = Path.GetFileNameWithoutExtension(r.FileName);
if(ansName.Equals(treName, StringComparison.OrdinalIgnoreCase))
{
entry.TreComponentRead = r.TreComponentRead;
}
}
}
}
if (isDraw && entry.FileRead != null)
{
//Preparing to draw
Graphics g = e.Graphics;
g.ScaleTransform(2f, 2f);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
AnsFile objToDraw = entry.FileRead;
Pen pen = new Pen(Color.Black);
//Getting size of bitmap
int maxWidth = 0, maxHeight = 0;
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
if (joint.Location.X.Length > maxWidth)
{
maxWidth = (int)joint.Location.X.Length;
}
if (joint.Location.Y.Length > maxHeight)
{
maxHeight = (int)joint.Location.Y.Length;
}
}
//Drawing joints //TODO: (Trello: Improve math behind visualizer)
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
PointF jointPoint = this.ToCartesian(new PointF((float)joint.Location.X.Length - 4f, (float)joint.Location.Y.Length + 10f), maxHeight);
e.Graphics.DrawString(joint.JointID.ToString(), new Font(FontFamily.GenericMonospace, 6f, FontStyle.Regular, GraphicsUnit.Point, 1, false), Brushes.Black, jointPoint);
}
//Draw the panels and links //TODO: (Trello: Improve math behind visualizer)
foreach (AnsMember member in objToDraw.AnsMembers)
{
List<AnsPanel> panels = member.Panels; //Drawing the panels
foreach (AnsPanel pan in panels)
{
pen.Color = Color.Red;
PointF p1 = this.ToCartesian(new PointF((float)pan.I.Location.X.Length, (float)pan.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)pan.J.Location.X.Length, (float)pan.J.Location.Y.Length), maxHeight);
g.DrawEllipse(pen, p1.X - 2.5f, p1.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p2.X - 2.5f, p2.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p1.X - 3, p1.Y - 3.3f, 5, 5);
g.DrawEllipse(pen, p2.X - 3, p2.Y - 3.3f, 5, 5);
pen.Color = Color.Black;
g.DrawLine(pen, p1, p2);
}
List<AnsLink> links = member.Links; //Drawing the links
foreach (AnsLink link in links)
{
PointF p1 = this.ToCartesian(new PointF((float)link.I.Location.X.Length, (float)link.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)link.J.Location.X.Length, (float)link.J.Location.Y.Length), maxHeight);
g.FillEllipse(Brushes.Green, p1.X - 1.5f, p1.Y - 1.5f, 3, 3);
g.FillEllipse(Brushes.Green, p2.X - 1.5f, p2.Y - 1.5f, 3, 3);
g.DrawLine(pen, p1, p2);
}
}
pictureBox1.Tag = entry.FileName;
}
if (isDraw && entry.TreComponentRead != null)
{
//Preparing to draw
Graphics g = e.Graphics;
g.ScaleTransform(2f, 2f);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
TreComponent objToDraw = entry.TreComponentRead;
Pen pen = new Pen(Color.Black);
int maxWidth = 0, maxHeight = 0;
foreach (Member member in objToDraw.Members)
{
foreach (GeometryClassLibrary.Point p in member.Geometry.Vertices)
{
if (p.X.Inches > maxWidth)
{
maxWidth = (int)p.X.Inches;
}
if (p.Y.Inches > maxHeight)
{
maxHeight = (int)p.Y.Inches;
}
}
}
maxHeight += 5; maxWidth += 5;
maxHeight += 15; maxWidth += 5;
foreach (Member member in objToDraw.Members)
{
List<PointF> pointsToDraw = new List<PointF>();
foreach (GeometryClassLibrary.Point p in member.Geometry.Vertices)
{
pointsToDraw.Add(ToCartesian(new PointF((float)p.X.Inches, (float)p.Y.Inches), maxHeight));
PointF pointToDraw = this.ToCartesian(new PointF((float)p.X.Inches, (float)p.Y.Inches), maxHeight);
g.FillEllipse(Brushes.Red, pointToDraw.X - 1.5f, pointToDraw.Y - 1.5f, 3, 3);
pointsToDraw.Add(pointToDraw);
}
g.DrawPolygon(pen, pointsToDraw.ToArray());
//Getting center of member and labeling member
float totalX = 0, totalY = 0;
foreach (PointF p in pointsToDraw)
{
totalX += p.X;
totalY += p.Y;
}
float centerX = totalX / pointsToDraw.Count;
float centerY = totalY / pointsToDraw.Count - 10;
PointF midPoint = new PointF(centerX, centerY);
g.DrawString(member.Name, new Font(FontFamily.GenericMonospace, 6f, FontStyle.Regular, GraphicsUnit.Point, 1, false), Brushes.Black, midPoint);
}
pictureBox1.Tag = entry.FileName;
}
}
I have been trying to write a program, to be able to load an image on a form and select a rectangle on it, then load that rectangle onto another picture box (pictureBox2), and I also want to be able to highlight what I have selected on the original picture, in pictureBox1, as I move my mouse.
So far I have this code, but it doesn't properly respond to the mouseMove event, the rectangle that I highlight isn't selected properly. What is the problem?
public partial class Form1 : Form
{
Bitmap original;
bool isSelecting;
int x0, y0, x1, y1;
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(picOriginal_MouseDown);
pictureBox1.MouseMove += new MouseEventHandler(picOriginal_MouseMove);
pictureBox1.MouseUp += new MouseEventHandler(picOriginal_MouseUp);
}
#region helpder methods
// Start selecting the rectangle.
private void picOriginal_MouseDown(object sender, MouseEventArgs e)
{
if(original != null)
{
isSelecting = true;
// Save the start point.
x0 = e.X;
y0 = e.Y;
}
}
// Continue selecting.
private void picOriginal_MouseMove(object sender, MouseEventArgs e)
{
if(original != null)
{
// Do nothing it we're not selecting an area.
if(!isSelecting) return;
// Save the new point.
x1 = e.X;
y1 = e.Y;
// Make a Bitmap to display the selection rectangle.
Bitmap bm = new Bitmap(original);
// Draw the rectangle.
using(Graphics gr = Graphics.FromImage(bm))
{
gr.DrawRectangle(Pens.Red,
Math.Min(x0, x1), Math.Min(y0, y1),
Math.Abs(x0 - x1), Math.Abs(y0 - y1)
);
}
// Display the temporary bitmap.
pictureBox1.Image = new Bitmap(bm, new Size(pictureBox1.Width, pictureBox1.Height));
}
}
// Finish selecting the area.
private void picOriginal_MouseUp(object sender, MouseEventArgs e)
{
if(original != null)
{
// Do nothing it we're not selecting an area.
if(!isSelecting) return;
isSelecting = false;
// Display the original image.
pictureBox1.Image = original;
// Copy the selected part of the image.
int wid = Math.Abs(x0 - x1);
int hgt = Math.Abs(y0 - y1);
if((wid < 1) || (hgt < 1)) return;
Bitmap area = new Bitmap(wid, hgt);
using(Graphics gr = Graphics.FromImage(area))
{
Rectangle source_rectangle =
new Rectangle(Math.Min(x0, x1), Math.Min(y0, y1),
wid, hgt);
Rectangle dest_rectangle =
new Rectangle(0, 0, wid, hgt);
gr.DrawImage(original, dest_rectangle,
source_rectangle, GraphicsUnit.Pixel);
}
// Display the result.
pictureBox2.Image = area;
}
}
#endregion
private void button1_Click(object sender, EventArgs e)
{
if(original != null)
{
}
}
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "jpg files (*.jpg)|*.jpg|All files(*.*)|*.*";
if(dialog.ShowDialog() == DialogResult.OK)
{
original = new Bitmap(dialog.FileName);
pictureBox1.Image = new Bitmap(original, new Size(pictureBox1.Width, pictureBox1.Height));
}
dialog.Dispose();
}
}
I think your problem is the 'original' image size which is not the same as the picturebox size. Try this code to compensate for the zoom ratio between your 'original' image and the picturebox size:
// Draw the rectangle.
float zoomX = (float)original.Size.Width / pictureBox1.Width;
float zoomY = (float)original.Size.Height / pictureBox1.Height;
using (Graphics gr = Graphics.FromImage(bm))
{
gr.DrawRectangle(Pens.Red,
Math.Min(x0, x1) * zoomX, Math.Min(y0, y1) * zoomY,
Math.Abs(x0 - x1) * zoomX, Math.Abs(y0 - y1) * zoomY
);
}
This fixes the red rectangle in my case. But the copied part in picturebox2 is still not correct...
And this fixes the copy to the second picturebox:
// Copy the selected part of the image.
float zoomX = (float)original.Size.Width / pictureBox1.Width;
float zoomY = (float)original.Size.Height / pictureBox1.Height;
int wid = (int)(zoomX * Math.Abs(x0 - x1));
int hgt = (int)(zoomY * Math.Abs(y0 - y1));
if ((wid < 1) || (hgt < 1)) return;
Bitmap area = new Bitmap(wid, hgt);
using (Graphics gr = Graphics.FromImage(area))
{
Rectangle source_rectangle =
new Rectangle((int)(zoomX * Math.Min(x0, x1)), (int)(zoomY * Math.Min(y0, y1)),
wid, hgt);
Rectangle dest_rectangle =
new Rectangle(0, 0, wid, hgt);
gr.DrawImage(original, dest_rectangle,
source_rectangle, GraphicsUnit.Pixel);
}
I have the below which draws a rectangle on mouse drag and also a grid drawing script which draws a 32x32 grid on the picture box what I'm trying to do is snap the rectangle to the grid then screen shot inside the rectangle.
I've got the screen shot bit and the drawing of the rectangle just not the snapping to grid bit working.
private bool _selecting;
private Rectangle _selection;
private void picCanvas_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_selecting = true;
_selection = new Rectangle(new Point(e.X, e.Y), new Size());
}
}
private void picCanvas_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (_selecting)
{
_selection.Width = e.X - _selection.X;
_selection.Height = e.Y - _selection.Y;
pictureBox1.Refresh();
}
}
public Image Crop(Image image, Rectangle selection)
{
Bitmap bmp = image as Bitmap;
// Check if it is a bitmap:
if (bmp == null)
throw new ArgumentException("No valid bitmap");
// Crop the image:
Bitmap cropBmp = bmp.Clone(selection, bmp.PixelFormat);
// Release the resources:
image.Dispose();
return cropBmp;
}
private void picCanvas_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left &&
_selecting &&
_selection.Size != new Size())
{
// Create cropped image:
//Image img = Crop(pictureBox1.Image, _selection);
// Fit image to the picturebox:
//pictureBox1.Image = img;
_selecting = false;
}
else
_selecting = false;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (_selecting)
{
// Draw a rectangle displaying the current selection
Pen pen = Pens.GreenYellow;
e.Graphics.DrawRectangle(pen, _selection);
}
Graphics g = e.Graphics;
int numOfCells = amount;
Pen p = new Pen(Color.LightGray);
for (int y = 0; y < numOfCells; ++y)
{
g.DrawLine(p, 0, y * ysize, numOfCells * ysize, y * ysize);
}
for (int x = 0; x < numOfCells; ++x)
{
g.DrawLine(p, x * xsize, 0, x * xsize, numOfCells * xsize);
}
}
First I would declare a snapping method
private Point SnapToGrid(Point p)
{
double x = Math.Round((double)p.X / xsize) * xsize;
double y = Math.Round((double)p.Y / ysize) * ysize;
return new Point((int)x, (int)y);
}
Then you can initialize the selection like this in MouseDown:
_selection = new Rectangle(SnapToGrid(e.Location), new Size());
And you can adjust the width in MouseMove like this:
Point dest = SnapToGrid(e.Location);
_selection.Width = dest.X - _selection.X;
_selection.Height = dest.Y - _selection.Y;