65.9K
CodeProject 正在变化。 阅读更多。
Home

裁剪图像

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (43投票s)

2008年11月5日

CPOL

2分钟阅读

viewsIcon

219481

downloadIcon

20255

一个演示,展示如何通过使用鼠标选择区域来裁剪图像。

Cropping01.JPG

Cropping02.JPG

(图片展示了早期“家用电脑”)

引言

在本文中,我将演示如何通过使用鼠标选择区域来裁剪图像。

背景

我们选择图像的一个区域,创建另一个包含所选区域的图像,并将新图像缩放到 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;
}

选择区域

选择区域很简单。 只需要使用 MouseDownMouseMove 事件即可。

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日:初始发布。
© . All rights reserved.