I am trying to create an application in C# (WinForms), something similar to this iOS Question
I managed to get part of it working, I can blur an image using this algorithm
Also I am able to draw a selection rectangle, I do not know if I am going wrong with the blurring or passing the rectangle. I have attached the file as shown below.
As seen, the blurring is outside the selection box.
I have pasted the code below:
// Start Rectangle
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
// Determine the initial rectangle coordinates...
RectStartPoint = e.Location;
Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RectStartPoint.X, tempEndPoint.X),
Math.Min(RectStartPoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RectStartPoint.X - tempEndPoint.X),
Math.Abs(RectStartPoint.Y - tempEndPoint.Y));
pictureBox1.Invalidate();
}
// Draw Area
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// Draw the rectangle...
if (pictureBox1.Image != null)
{
if (Rect != null && Rect.Width > 0 && Rect.Height > 0)
{
e.Graphics.DrawRectangle(selectionPen, Rect);
}
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//Right now I am using right click as a call to blur
if (e.Button == MouseButtons.Right)
{
if (Rect.Contains(e.Location))
{
pictureBox1.Image = Blur(pictureBox1.Image, Rect, 5);
pictureBox1.Refresh();
}
}
}
private void blurPageToolStripMenuItem_Click(object sender, EventArgs e)
{
FullRect = new Rectangle(0, 0, pictureBox1.Image.Width, pictureBox1.Image.Height);
pictureBox1.Image = Blur(pictureBox1.Image, FullRect, 5);
}
private System.Drawing.Image Blur(System.Drawing.Image image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image); //image.Width, image.Height);
using (Graphics graphics = Graphics.FromImage(blurred))
{
// look at every pixel in the blur rectangle
for (Int32 xx = rectangle.Left; xx < rectangle.Right; xx += blurSize)
{
for (Int32 yy = rectangle.Top; yy < rectangle.Bottom; yy += blurSize)
{
Int32 avgR = 0, avgG = 0, avgB = 0;
Int32 blurPixelCount = 0;
Rectangle currentRect = new Rectangle(xx, yy, blurSize, blurSize);
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (Int32 x = currentRect.Left; (x < currentRect.Right && x < image.Width); x++)
{
for (Int32 y = currentRect.Top; (y < currentRect.Bottom && y < image.Height); y++)
{
Color pixel = blurred.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
graphics.FillRectangle(new SolidBrush(Color.FromArgb(avgR, avgG, avgB)), currentRect);
}
}
graphics.Flush();
}
return blurred;
}
Another problem I am facing is, when the form is loaded initially, it starts in minimised mode, now if I use the selection (red rectangle), and then if I maximise the application the selected part of the picture is different.
Thank you for the help/suggestion in advance. If any links to a tool similar is around, please do share as I might have missed it. Thanks
You may be experiencing this issue because your image is stretched in the PictureBox. You can verify this is the issue by setting the SizeMode property of the PictureBox to Normal.
This is the sequence of events:
The selection rectangle is drawn.
The point/rectangle for the selection is determined.
The unstretched image is retrieved from the PictureBox, and is passed to the Blur method, with the calculated rectangle.
The unstretched image is blurred over the area described by the rectangle.
The unstretched image, now blurred, is assigned to the PictureBox.
The PictureBox stretches the image out to according to its SizeMode setting.
This makes the image appear to have blurred in a different location than what you selected.
The code you have looks at the selection rectangle, and uses those points to operate on the original image, not the stretched version of the image. If you want to blur the stretched image, you will need to get the stretched image first, then apply the blur to the rectangle selected on that image. Here is an example:
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
//Right now I am using right click as a call to blur
if (e.Button == MouseButtons.Right)
{
if (Rect.Contains(e.Location))
{
pictureBox1.Image = Blur(getPictureBoxImage(), Rect, 5);
pictureBox1.Refresh();
}
}
}
private Bitmap getPictureBoxImage()
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(pictureBox1.Image,
new Rectangle(0, 0, bmp.Width, bmp.Height));
}
return bmp;
}
Code for retrieving the stretched image is derived from this answer: https://stackoverflow.com/a/8702405/935052
Related
I am developping a custom C# UserControl WinForm to display an image on background and display scrollbars when I zoom with the mouse. For this, I overrided the OnPaint method. In it, if I have an image loaded, according some parameters I know the source and destination rectangle sizes. In the same way, I know what scale and translation apply to always keeping the top left corner on screen when zooming. And for the zoom, I use the scrollmouse event to update the zoom factory.
Here is my code related to this override method.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw image
if(image != null)
{
//
Rectangle srcRect, destRect;
Point pt = new Point((int)(hScrollBar1.Value/zoom), (int)(vScrollBar1.Value/zoom));
if (canvasSize.Width * zoom < viewRectWidth && canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(0, 0, canvasSize.Width, canvasSize.Height); // view all image
else if (canvasSize.Width * zoom < viewRectWidth)
srcRect = new Rectangle(0, pt.Y, canvasSize.Width, (int)(viewRectHeight / zoom)); // view a portion of image but center on width
else if (canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(pt.X, 0, (int)(viewRectWidth / zoom), canvasSize.Height); // view a portion of image but center on height
else
srcRect = new Rectangle(pt, new Size((int)(viewRectWidth / zoom), (int)(viewRectHeight / zoom))); // view a portion of image
destRect = new Rectangle((int)(-srcRect.Width/2),
(int)-srcRect.Height/2,
srcRect.Width,
srcRect.Height); // the center of apparent image is on origin
Matrix mx = new Matrix(); // create an identity matrix
mx.Scale(zoom, zoom); // zoom image
// Move image to view window center
mx.Translate(viewRectWidth / 2.0f, viewRectHeight / 2.0f, MatrixOrder.Append);
// Display image on widget
Graphics g = e.Graphics;
g.InterpolationMode = interMode;
g.Transform = mx;
g.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
}
}
My question is how to get the pixel value when I am on the MouseMove override method of this WinForm ?
I think understand that it is possible only in method with PaintEventArgs but I am not sure how to deal with it. I tried a lot of things but for now the better I got is use the mouse position on the screen and find the pixel value in the original bitmap with these "wrong" coordinates. I can't link this relative position on the screen with the real coordinates of the pixel of the image display at this place. Maybe there is method to "just" get the pixel value not passing through the image bitmap I use for the paint method ? Or maybe not.
Thank you in advance for your help. Best regards.
I couldn't completely understand your drawing code but you can do an inverse transformation on mouse coordinate. So, you can translate the mouse coordinate back to the origin and scale it by 1/zoom. This simple process gives you the image space coordinate.
I provide an example code with its own drawing code (not your code/algorithm) but that can still give you the idea of inverse transformation. It is pretty simple so look at the example code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GetPixelFromZoomedImage
{
public partial class MainForm : Form
{
public Form1()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
private float m_zoom = 1.0f;
private Bitmap m_image;
private Point m_origin = Point.Empty;
private Point m_delta = Point.Empty;
private SolidBrush m_brush = new SolidBrush(Color.Transparent);
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.TranslateTransform(m_origin.X, m_origin.Y);
g.ScaleTransform(m_zoom, m_zoom);
g.DrawImageUnscaled(m_image, Point.Empty);
g.ResetTransform();
g.FillRectangle(m_brush, ClientSize.Width - 50, 0, 50, 50);
base.OnPaint(e);
}
protected override void OnHandleCreated(EventArgs e)
{
m_image = (Bitmap)Image.FromFile("test.png");
base.OnHandleCreated(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_delta = new Point(m_origin.X - e.X, m_origin.Y - e.Y);
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_origin = new Point(e.X + m_delta.X, e.Y + m_delta.Y);
Invalidate();
}
int x = (int)((e.X - m_origin.X) / m_zoom);
int y = (int)((e.Y - m_origin.Y) / m_zoom);
if (x < 0 || x >= m_image.Width || y < 0 || y >= m_image.Height)
return;
m_brush.Color = m_image.GetPixel(x, y);
Invalidate();
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
float scaleFactor = 1.6f * (float)Math.Abs(e.Delta) / 120;
if(e.Delta > 0)
m_zoom *= scaleFactor;
else
m_zoom /= scaleFactor;
m_zoom = m_zoom > 64.0f ? 64.0f : m_zoom;
m_zoom = m_zoom < 0.1f ? 0.1f : m_zoom;
Invalidate();
base.OnMouseWheel(e);
}
}
}
I'm triying to cover an image with another in order to provide a watermark but it has to cover the entire source image. The problem is that the watermark provided is of 600x600 and the source image can have any size and aspect ratio. So far it does not cover the source image entirely.
I solved it like this (in a very straightforward way).
private void button1_Click(object sender, EventArgs e)
{
var image = new Bitmap( this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
var rect = new Rectangle(0, 0, this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
Graphics graphics = Graphics.FromImage(image);
graphics.DrawImage(this.pictureBox1.Image, 0, 0);
var waterMarkImage = new Bitmap(this.pictureBox2.Image.Width, this.pictureBox2.Image.Height);
for (int y = 0; y < waterMarkImage.Height; y++)
{
for (int x = 0; x < waterMarkImage.Width; x++)
{
var color = (this.pictureBox2.Image as Bitmap).GetPixel(x, y);
color = Color.FromArgb(50, color.R, color.G, color.B);
waterMarkImage.SetPixel(x, y, color);
}
}
graphics.DrawImage(waterMarkImage, rect);
this.pictureBox3.Image = image;
}
In pictureBox1 I loaded main image. In pictureBox2 I loaded "water mark". In the event handler I created resulting image (first main image then the second) and loaded it into pictureBox3. To get water mark affect I reduced alpha component of color (I set it to 50).
How can I get the color of a pixel at the location of the cursor? I know how to get the mouses position using MousePosition but I can not figure out how to get the pixel color at that location. I write the code put I have no result when run
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
s= pictureBox1.PointToClient(Cursor.Position);
bitmap.SetPixel(s.X / 40, s.Y / 40, Color.Red);
}
It is easier to use the e.Location point in the parameter of the Mouseclick event:
Color c = ( (Bitmap)pictureBox1.Image).GetPixel(e.X, e.Y);
This assumes that indeed the bitmap is in the PicturBox's Image, not painted on top of the Control..
Make sure the event is actually hooked up!
To set a clicked pixel to, say red, you would get the Bitmap from the PB's Image and set the pixels, then move the Bitmap back in::
Bitmap bmp = (Bitmap)pictureBox1.Image;
bmp.SetPixel(e.X, e.Y, Color.Red);
pictureBox1.Image = bmp;
also in the MouseClick event.
If you want to get a larger mark you should use the Graphics methods, maybe like this:
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
G.DrawEllipse(Pens.Red, e.X - 3, e.Y - 3, 6, 6);
}
Update: To combine getting and setting you could write:
Bitmap bmp = (Bitmap)pictureBox1.Image;
Color target = Color.FromArgb(255, 255, 255, 255);
Color c == bmp .GetPixel(e.X, e.Y);
if (c == target )
{
bmp.SetPixel(e.X, e.Y, Color.Red);
pictureBox1.Image = bmp;
}
else MessageBox.Show("Click only on white spots! You have hit " + c.ToString(),
"Wrong spot! ");
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;
I have drawn an image in pictureBox, now i want to save it in the folder. I have tried so many ways nothing worked. I am drawing image using the fallowing code. I am drawing the image based on Textbox values.
private void btnTransferBottleRegenerate_Click(object sender, EventArgs e)
{
float[] volumetransfer = new float[1];
volumetransfer[0] = float.Parse(txtTransferVolume.Text);
int[] percentages = new int[6];
percentages[0] = int.Parse(txtTransferNotIdentified.Text);
percentages[1] = int.Parse(txtTransferWaterBasedMud.Text);
percentages[2] = int.Parse(txtTransferOilBasedMud.Text);
percentages[3] = int.Parse(txtTransferWater.Text);
percentages[4] = int.Parse(txtTransferHydrocarbonLiq.Text);
percentages[5] = int.Parse(txtTransferGas.Text);
Color[] colors = new Color[6];
colors[0] = Color.Gray;
colors[1] = Color.Chocolate;
colors[2] = Color.SaddleBrown;
colors[3] = Color.Blue;
colors[4] = Color.Red;
colors[5] = Color.Lime;
// Finally, call the method
DrawPercentages(percentages, colors, volumetransfer);
//string filename = Application.StartupPath + "\\volumetransfer.jpg";
// pictureBox1.Image.Save(Application.StartupPath + "\\Image\\picture1.jpg");
// pictureBox1.Refresh();
// if (pictureBox1 != null)
// {
pictureBox1.Image.Save(Application.StartupPath + "\\test.bmp");
// }
}
private void DrawPercentages(int[] percentages, Color[] colors, float[] volumetransfer)
{
// Create a Graphics object to draw on the picturebox
Graphics G = pictureBox1.CreateGraphics();
// Calculate the number of pixels per 1 percent
float pixelsPerPercent = pictureBox1.Height / volumetransfer[0];
// Keep track of the height at which to start drawing (starting from the bottom going up)
int drawHeight = pictureBox1.Height;
// Loop through all percentages and draw a rectangle for each
for (int i = 0; i < percentages.Length; i++)
{
// Create a brush with the current color
SolidBrush brush = new SolidBrush(colors[i]);
// Update the height at which the next rectangle is drawn.
drawHeight -= (int)(pixelsPerPercent * percentages[i]);
// Draw a filled rectangle
G.FillRectangle(brush, 0, drawHeight, pictureBox1.Width, pixelsPerPercent * percentages[i]);
}
}
}
}
when I click "Regenerate" button then it is going to draw the image in pictureBox after that i want to save it in a folder. I have the design like this.
A solution is draw on a bitmap, set it as the image of the PictureBox and then save it:
private void DrawPercentages(int[] percentages, Color[] colors, float[] volumetransfer){
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using(Graphics G = Graphics.FromImage(bmp)){
//...
}
pictureBox1.Image = bmp;
}
And then your code should work perfectly without any problem.
First, you should paint within the correct event PictureBox1_Paint so that your drawn image stays visible (better: got repaint) even if your window gets eg: resized.
Afterwards you could make use of a snippet posted by #Hans Passant - How to save Graphics object to save your drawn image to disk.
// global to be accesible within paint
float[] volumetransfer = new float[1];
int[] percentages = new int[6];
Color[] colors = new Color[6];
private void btnTransferBottleRegenerate_Click(object sender, EventArgs e)
{
/// initialization goes here
// force pictureBox to be redrawn
// so resizing your window won't let your rectangles disapear
pictureBox1.Invalidate();
using (var bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height))
{
pictureBox1.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save(#"e:\temp\test.png"); //Application.StartupPath + "\\Image\\picture1.jpg"
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// use GraphicsObject of PaintEventArgs
Graphics G = e.Graphics;
float pixelsPerPercent = pictureBox1.Height / volumetransfer[0];
int drawHeight = pictureBox1.Height;
for (int i = 0; i < percentages.Length; i++)
{
SolidBrush brush = new SolidBrush(colors[i]);
drawHeight -= (int)(pixelsPerPercent * percentages[i]);
G.FillRectangle(brush, 0, drawHeight, pictureBox1.Width, pixelsPerPercent * percentages[i]);
}
}
On the other hand your DrawPercentage(..) could return a new Image - which you could afterwards assign to the pictureBox and save it with pictureBox1.Image.Save(...)
private void button1_Click(object sender, EventArgs e)
{
float[] volumetransfer = new float[1];
int[] percentages = new int[6];
Color[] colors = new Color[6];
/// initialization goes here
pictureBox1.Image = CreateImage(volumetransfer, percentages, colors);
pictureBox1.Image.Save(#"e:\temp\test.png");
}
private Image CreateImage(float[] volumetransfer, int[] percentages, Color[] colors)
{
Image img = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g = Graphics.FromImage(img);
float pixelsPerPercent = pictureBox1.Height / volumetransfer[0];
int drawHeight = pictureBox1.Height;
for (int i = 0; i < percentages.Length; i++)
{
SolidBrush brush = new SolidBrush(colors[i]);
drawHeight -= (int)(pixelsPerPercent * percentages[i]);
g.FillRectangle(brush, 0, drawHeight, pictureBox1.Width, pixelsPerPercent * percentages[i]);
}
return img;
}