Click here to Skip to main content
15,887,812 members
Articles / Desktop Programming / Windows Forms
Article

Cropping Images

Rate me:
Please Sign up or sign in to vote.
4.87/5 (43 votes)
5 Nov 2008CPOL2 min read 215K   20.2K   111   27
A demo that shows how to crop images by selecting a region with the mouse.

Cropping01.JPG

Cropping02.JPG

(The images show the "home computers" in early days)

Introduction

In this article, I'll give a demo of how to crop an image by selecting a region with the mouse.

Background

We select a region of an image, create another image of the selected region, and zoom the new image to the size of the PictureBox.

Although the PictureBox has its the SizeMode = PictureBoxSizeMode.Zoom, a MemoryOutOfRange exception may be thrown when cropping the image several times. So, I assume this property is just for fitting an image to a PictureBox once when loading the form.

Extension methods for Image

I've implemented the cropping and fitting to the PictureBox as an extension to the Image class.

Cropping

The image is cropped by cloning a region of the original image.

C#
/// <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;
}

Fitting the image to the PictureBox

As mentioned under the Background section, fitting can't be done by the PictureBox itself. So, I've coded this myself.

As the first step, the scale factors in the vertical and horizontal directions are calculated. To scale (or zoom) the image so that the original ratio of width to height is maintained, the bigger ratio of the scale is used. The interpolation-mode is set to produce high quality pictures.

C#
/// <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;
}

The user interface (for the demo)

Restore original image

To restore the original image, it is saved in the form-load event. Notice, save a copy of the image and not a reference. In the latter case, the copy would point to the (cropped) image in the PictureBox. Also, on restoring, a copy is assigned to the PictureBox.

C#
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;
}

Selecting the region

Selecting a region is straightforward. Just use the MouseDown and MouseMove events.

C#
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();
    }
}

To display the selection, a rectangle is drawn on the picture.

C#
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);
    }
}

Cropping

The end of selection is indicated by a MouseUp. Before cropping, the size of the selection is validated because it could be zero in the case of double-clicking in the PictureBox.

C#
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;
}

History

  • 5th November 2008: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Foidl Günther
Austria Austria
Engineer in combustion engine development.
Programming languages: C#, FORTRAN 95, Matlab

FIS-overall worldcup winner in Speedski (Downhill) 2008/09 and 2009/10.

Comments and Discussions

 
GeneralMy vote of 5 Pin
viragdesai13-Oct-17 23:23
viragdesai13-Oct-17 23:23 
QuestionUsefull Pin
Twiggy Ramirezz30-Nov-16 5:19
Twiggy Ramirezz30-Nov-16 5:19 
Questionrecorte de imagen utilizando Find de matlab Pin
Member 127237001-Oct-16 20:04
Member 127237001-Oct-16 20:04 
Questionrecorte de imagen cuncion Find Pin
Member 127237001-Oct-16 20:04
Member 127237001-Oct-16 20:04 
GeneralMy vote of 5 Pin
Britteandy27-Aug-13 23:52
Britteandy27-Aug-13 23:52 
GeneralMy vote of 5 Pin
razor198922-Aug-13 5:21
razor198922-Aug-13 5:21 
QuestionCropping an image with particular resolution Pin
rajucreations27-Jan-13 0:04
rajucreations27-Jan-13 0:04 
QuestionExcellent Pin
Member 114552423-Jul-12 4:54
Member 114552423-Jul-12 4:54 
GeneralExcellent example Pin
kinotech711-Apr-12 21:54
kinotech711-Apr-12 21:54 
QuestionOut of Memory Exceptions and potential solutions Pin
vanleeuwena8-Mar-12 14:24
vanleeuwena8-Mar-12 14:24 
In working with this project, I experienced a number of out-of-memory exceptions with the picture box control. My approach was to consult the internet and determine what others have suggested as a potential solution to this problem. Two suggestions were implemented: (1) To make sure that the images associated with the pictureBox1 control are properly disposed prior to assigning a new image to the control; and (2) to force garbage collection by including GC.Collect(); statement. These two suggestions appear to have reduced the number of out-of-memory errors experienced. The pictureBox1_MouseUp routine was modified by adding the following code fragment (see complete modified routine below):

C#
// if the picture box has an image, dispose of the image
if (pictureBox1.Image != null)
{
    pictureBox1.Image.Dispose();
    GC.Collect();
}



Out-of-memory exceptions continued to occur during debugging. It was discovered that when depressing the left mouse button and moving the mouse from the right-to-left occasionally resulted in a selection rectangles with either a zero height or zero width. When this happens an out-of-memory exception would occur when the image is cropped. The exact cause of this has not been determined. The following routine was added to verify that the selection area width and height are non-zero:

C#
// check selection rectangle has non-zero Height and Width
private bool ValidateSelection(Rectangle selection)
{
    // routine to validate the selection
    if (selection.Width == 0 || selection.Height == 0)
    {
        // if you get here a good rectangle was not created
        return false;
    }
    else
    {
        return true;
    }
}



During debugging it was also discovered that on occasion the selection area could extend outside of the image boundaries. When that happens it also resulted in out-of-memory exceptions. The following routine was added to ensure that the selection rectangle does not extend outside the image area:

C#
// Check that selection rectangle does extend outside of image boundaries
private void ValidateRectangleSize()
{
    Size imgSize = this.pictureBox1.Image.Size;
    int selectionWidth;
    int selectionHeight;

    // check width
    if (_selection.X < 0)
    {
        _selection.X = 0;
    }

    selectionWidth = _selection.Width + _selection.X;
    if (selectionWidth > imgSize.Width)
    {
        _selection.Width = imgSize.Width - _selection.X - 1;
    }

    // check height
    if (_selection.Y < 0)
    {
        _selection.Y = 0;
    }

    selectionHeight = _selection.Height + _selection.Y;
    if (selectionHeight > imgSize.Height)
    {
        _selection.Height = imgSize.Height - _selection.Y - 1;
    }
}



The pictureBox1_MouseUp routine was modified to call the validation routines to help prevent out-of-memory exceptions.

C#
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left && _selecting)
    {
       // check selection rectangle has non-zero Height and Width
       if (!ValidateSelection(_selection))
       {
           _selecting = false;
					 
					 return;
       }

       // Check that selection rectangle does extend outside of image boundaries
       ValidateRectangleSize();

       // Create cropped image:
       Image img = pictureBox1.Image.Crop(_selection);

       // if the picture box has an image, dispose of the image
       if (pictureBox1.Image != null)
       {
           pictureBox1.Image.Dispose();
           GC.Collect();
       }

       // Fit image to the picturebox:
       pictureBox1.Image = img.Fit2PictureBox(pictureBox1);
       _selecting = false;
    }
}


The above changes appear to have resolved all of the out-of-memory exceptions.
avl

GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 21:29
professionalManoj Kumar Choubey26-Feb-12 21:29 
GeneralMy vote of 5 Pin
Anurag Gandhi3-Nov-11 22:23
professionalAnurag Gandhi3-Nov-11 22:23 
GeneralRe: My vote of 5 Pin
Zamshed Farhan12-Jan-13 17:49
Zamshed Farhan12-Jan-13 17:49 
GeneralMy vote of 5 Pin
yt99995-Sep-10 21:58
yt99995-Sep-10 21:58 
GeneralCrop region selection improvement Pin
vanleeuwena22-Aug-10 9:03
vanleeuwena22-Aug-10 9:03 
Questionarea crop Pin
ordex15-Feb-09 18:26
ordex15-Feb-09 18:26 
Generalan improvement Pin
Mr.PoorEnglish3-Dec-08 10:26
Mr.PoorEnglish3-Dec-08 10:26 
GeneralRe: an improvement Pin
Günther M. FOIDL3-Dec-08 14:17
Günther M. FOIDL3-Dec-08 14:17 
GeneralExcellent Demo Pin
kjward1-Dec-08 2:24
kjward1-Dec-08 2:24 
GeneralRe: Excellent Demo Pin
Günther M. FOIDL1-Dec-08 2:43
Günther M. FOIDL1-Dec-08 2:43 
GeneralRe: Excellent Demo Pin
kjward1-Dec-08 2:51
kjward1-Dec-08 2:51 
GeneralRe: Excellent Demo Pin
Günther M. FOIDL1-Dec-08 3:04
Günther M. FOIDL1-Dec-08 3:04 
GeneralRe: Excellent Demo Pin
kjward1-Dec-08 3:28
kjward1-Dec-08 3:28 
GeneralInterpolationMode not needed always Pin
Rader.net11-Nov-08 6:14
Rader.net11-Nov-08 6:14 
GeneralRe: InterpolationMode not needed always Pin
Günther M. FOIDL11-Nov-08 7:16
Günther M. FOIDL11-Nov-08 7:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.