裁剪图像






4.87/5 (43投票s)
一个演示,展示如何通过使用鼠标选择区域来裁剪图像。
(图片展示了早期“家用电脑”)
引言
在本文中,我将演示如何通过使用鼠标选择区域来裁剪图像。
背景
我们选择图像的一个区域,创建另一个包含所选区域的图像,并将新图像缩放到 PictureBox
的大小。
虽然 PictureBox
具有 SizeMode = PictureBoxSizeMode.Zoom
属性,但在多次裁剪图像时可能会抛出 MemoryOutOfRange
异常。 因此,我认为此属性仅用于在加载表单时将图像适配到 PictureBox
一次。
Image 的扩展方法
我将裁剪和适配 PictureBox
的操作实现为 Image
类的一个扩展。
裁剪
图像通过克隆原始图像的一个区域来进行裁剪。
/// <summary>
/// Crops an image according to a selection rectangel
/// </summary>
/// <param name="image">
/// the image to be cropped
/// </param>
/// <param name="selection">
/// the selection
/// </param>
/// <returns>
/// cropped image
/// </returns>
public static Image Crop(this 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;
}
将图像适配到 PictureBox
如“背景”部分所述,适配不能由 PictureBox
本身完成。 因此,我自行编写了这段代码。
首先,计算垂直和水平方向上的缩放比例。 为了缩放(或缩放)图像,以保持原始的宽高比,使用较大的缩放比例。 插值模式被设置为生成高质量的图像。
/// <summary>
/// Fits an image to the size of a picturebox
/// </summary>
/// <param name="image">
/// image to be fit
/// </param>
/// <param name="picBox">
/// picturebox in that the image should fit
/// </param>
/// <returns>
/// fitted image
/// </returns>
/// <remarks>
/// Although the picturebox has the SizeMode-property that offers
/// the same functionality an OutOfMemory-Exception is thrown
/// when assigning images to a picturebox several times.
///
/// AFAIK the SizeMode is designed for assigning an image to
/// picturebox only once.
/// </remarks>
public static Image Fit2PictureBox(this Image image, PictureBox picBox)
{
Bitmap bmp = null;
Graphics g;
// Scale:
double scaleY = (double)image.Width / picBox.Width;
double scaleX = (double)image.Height / picBox.Height;
double scale = scaleY < scaleX ? scaleX : scaleY;
// Create new bitmap:
bmp = new Bitmap(
(int)((double)image.Width / scale),
(int)((double)image.Height / scale));
// Set resolution of the new image:
bmp.SetResolution(
image.HorizontalResolution,
image.VerticalResolution);
// Create graphics:
g = Graphics.FromImage(bmp);
// Set interpolation mode:
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the new image:
g.DrawImage(
image,
new Rectangle( // Destination
0, 0,
bmp.Width, bmp.Height),
new Rectangle( // Source
0, 0,
image.Width, image.Height),
GraphicsUnit.Pixel);
// Release the resources of the graphics:
g.Dispose();
// Release the resources of the origin image:
image.Dispose();
return bmp;
}
用户界面(用于演示)
恢复原始图像
要恢复原始图像,它是在表单加载事件中保存的。 请注意,保存图像的副本,而不是引用。 在后一种情况下,副本将指向 PictureBox
中的(裁剪的)图像。 另外,在恢复时,将副本分配给 PictureBox
。
private Image _originalImage;
private void Form1_Load(object sender, System.EventArgs e)
{
// Save just a copy of the image on no reference!
_originalImage = pictureBox1.Image.Clone() as Image;
}
private void button1_Click(object sender, System.EventArgs e)
{
pictureBox1.Image = _originalImage.Clone() as Image;
}
选择区域
选择区域很简单。 只需要使用 MouseDown
和 MouseMove
事件即可。
private bool _selecting;
private Rectangle _selection;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
// Starting point of the selection:
if (e.Button == MouseButtons.Left)
{
_selecting = true;
_selection = new Rectangle(new Point(e.X, e.Y), new Size());
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
// Update the actual size of the selection:
if (_selecting)
{
_selection.Width = e.X - _selection.X;
_selection.Height = e.Y - _selection.Y;
// Redraw the picturebox:
pictureBox1.Refresh();
}
}
为了显示选择,会在图片上绘制一个矩形。
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);
}
}
裁剪
选择的结束由 MouseUp
表示。 在裁剪之前,会验证选择的大小,因为它在双击 PictureBox
的情况下可能为零。
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left &&
_selecting &&
_selection.Size != new Size())
{
// Create cropped image:
Image img = pictureBox1.Image.Crop(_selection);
// Fit image to the picturebox:
pictureBox1.Image = img.Fit2PictureBox(pictureBox1);
_selecting = false;
}
else
_selecting = false;
}
历史
- 2008年11月5日:初始发布。