269

How do I crop an image using C#?

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
sandy101
  • 3,395
  • 3
  • 23
  • 19
  • I just made a custom class to simply crop any control that has an image or background image. i have uploaded it to github.com: https://github.com/DwainSnickles/ControlCrop/blob/master/README.md – user2180706 Oct 02 '22 at 17:59

15 Answers15

300

Check out this link: http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing

private static Image cropImage(Image img, Rectangle cropArea)
{
   Bitmap bmpImage = new Bitmap(img);
   return bmpImage.Clone(cropArea, bmpImage.PixelFormat);
}
bato3
  • 2,695
  • 1
  • 18
  • 26
Nick
  • 5,848
  • 4
  • 28
  • 33
  • 58
    Agreed, but note that if the cropArea crosses img boundary, it gives a "Out of memory" exception. – ChrisJJ Oct 29 '11 at 14:41
  • Is this method faster than @DanielLeCheminant method? – Ivan Kochurkin Jan 04 '13 at 22:25
  • 1
    @KvanTTT, both of them are pretty slow if you want to crop a big image into smaller ones. – JBeurer Feb 01 '13 at 07:20
  • 1
    @ChrisJJ can you explain more? or give a workaround for that issue? – raym0nd Aug 21 '13 at 19:32
  • 1
    @raym0nd I'm guessing the workaround is to ensure your rectangle's dimensions are no bigger than the image's – Stuart Dobson May 28 '14 at 06:31
  • @ChrisJJ yes, sorry it took me forever to reply back. – raym0nd Aug 21 '14 at 14:31
  • 4
    Their site is down. Anyone got the code from the site? – Sangam Uprety Sep 03 '15 at 19:58
  • 2
    This is the worst solution for performance. Graphics.DrawImage solution is much better. – ElektroStudios Nov 04 '15 at 13:09
  • It is available through the Wayback Machine: https://web.archive.org/web/20120420110925/http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing –  Dec 14 '18 at 14:56
  • So here should you dispose `bmpImage`, or will disposing the clone also dispose the original? – Juan Feb 22 '20 at 19:15
  • After pondering the more memory-secure alternatives, I ended up wrapping the `.Clone()` in a `try` without further handling, because ignoring the out-of-bounds is intuitive in my use-case. – dakab May 06 '21 at 15:00
  • 1
    To avoid "Out of memory" exception you could first do `cropArea.Interset(new Rectangle(0, 0, img.Width, img.Height))` – dashesy Aug 11 '22 at 22:57
250

You can use [Graphics.DrawImage][1] to draw a cropped image onto the graphics object from a bitmap.

Rectangle cropRect = new Rectangle(...);
using (Bitmap src = Image.FromFile("") as Bitmap)
{
    using (Bitmap target = new Bitmap(cropRect.Width, cropRect.Height))
    {
        using (Graphics g = Graphics.FromImage(target))
        {
            g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
                cropRect,
                GraphicsUnit.Pixel);
        }
    }
}
Ofer Zelig
  • 17,068
  • 9
  • 59
  • 93
Daniel LeCheminant
  • 50,583
  • 16
  • 120
  • 115
78

Simpler than the accepted answer is this:

public static Bitmap cropAtRect(this Bitmap b, Rectangle r)
{
    using (var nb = new Bitmap(r.Width, r.Height))
    {
        using (Graphics g = Graphics.FromImage(nb))
        {
            g.DrawImage(b, -r.X, -r.Y);
            return nb;
        }
    }
}

and it avoids the "Out of memory" exception risk of the simplest answer.

Note that Bitmap and Graphics are IDisposable hence the using clauses.

EDIT: I find this is fine with PNGs saved by Bitmap.Save or Paint.exe, but fails with PNGs saved by e.g. Paint Shop Pro 6 - the content is displaced. Addition of GraphicsUnit.Pixel gives a different wrong result. Perhaps just these failing PNGs are faulty.

Ofer Zelig
  • 17,068
  • 9
  • 59
  • 93
ChrisJJ
  • 2,191
  • 1
  • 25
  • 38
  • 7
    Best reply here, this should be awarded the answer. I was experiencing the 'out of memory' as well on other solutions. This worked first time. – c0d3p03t Oct 15 '14 at 21:27
  • I don't understand why adding GraphicsUnit.Pixel gives the wrong result, but it definitely does. – DOKKA Jun 30 '16 at 15:41
  • 2
    My images were cropping with the correct size but at incorrect X/Y until I called SetResolution on the target image as suggested in the answer by @IntellyDev. – Brent Keller May 10 '17 at 12:59
  • 7
    This answer leaks the Grphics object. – TaW May 17 '18 at 15:35
  • 4
    `Bitmap` and `Graphics` are `IDisposable` - add a `using` clause – Dave Thieben Mar 17 '19 at 14:02
  • add this: nb.SetResolution(b.HorizontalResolution, b.VerticalResolution); – Dejan Dozet Oct 29 '20 at 20:51
  • You mention that Paint Shop Pro doesn't work properly. I had the same error in Photoshop, but after saving "Save as web" and 100% quality, it works as it's supposed to. – WoodyDRN May 02 '22 at 09:38
10

use bmp.SetResolution(image.HorizontalResolution, image .VerticalResolution);

this may be necessary to do even if you implement best answer here especially if your image is real great and resolutions are not exactly 96.0

My test example:

    static Bitmap LoadImage()
    {
        return (Bitmap)Bitmap.FromFile( @"e:\Tests\d_bigImage.bmp" ); // here is large image 9222x9222 pixels and 95.96 dpi resolutions
    }

    static void TestBigImagePartDrawing()
    {
        using( var absentRectangleImage = LoadImage() )
        {
            using( var currentTile = new Bitmap( 256, 256 ) )
            {
                currentTile.SetResolution(absentRectangleImage.HorizontalResolution, absentRectangleImage.VerticalResolution);

                using( var currentTileGraphics = Graphics.FromImage( currentTile ) )
                {
                    currentTileGraphics.Clear( Color.Black );
                    var absentRectangleArea = new Rectangle( 3, 8963, 256, 256 );
                    currentTileGraphics.DrawImage( absentRectangleImage, 0, 0, absentRectangleArea, GraphicsUnit.Pixel );
                }

                currentTile.Save(@"e:\Tests\Tile.bmp");
            }
        }
    }
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
IntellyDev
  • 131
  • 1
  • 5
7

Here's a simple example on cropping an image

public Image Crop(string img, int width, int height, int x, int y)
{
    try
    {
        Image image = Image.FromFile(img);
        Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        bmp.SetResolution(80, 60);

        Graphics gfx = Graphics.FromImage(bmp);
        gfx.SmoothingMode = SmoothingMode.AntiAlias;
        gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
        gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
        gfx.DrawImage(image, new Rectangle(0, 0, width, height), x, y, width, height, GraphicsUnit.Pixel);
        // Dispose to free up resources
        image.Dispose();
        bmp.Dispose();
        gfx.Dispose();

        return bmp;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        return null;
    }            
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
PsychoCoder
  • 10,570
  • 12
  • 44
  • 60
  • 6
    He is the only one who mentioned resolution, all the above methods will fail if the source image has non-standard resolution. – net_prog Jan 02 '12 at 20:46
  • 1
    use bmp.SetResolution(image .HorizontalResolution, image .VerticalResolution); to fix resolution issue. – Morbia May 23 '12 at 17:40
  • 3
    On exception this will leak image, bmp and gfx objects. Why not to wrap those in using statements? – Darius Kucinskas Nov 20 '13 at 20:30
5

It's quite easy:

  • Create a new Bitmap object with the cropped size.
  • Use Graphics.FromImage to create a Graphics object for the new bitmap.
  • Use the DrawImage method to draw the image onto the bitmap with a negative X and Y coordinate.
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • It is always better to add some sample code to refer to. Not everyone is a coding expert like you and me! – D J Jul 17 '20 at 13:18
4

If you're using AForge.NET:

using(var croppedBitmap = new Crop(new Rectangle(10, 10, 10, 10)).Apply(bitmap))
{
    // ...
}
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
4

I was looking for a easy and FAST function with no additional libary to do the job. I tried Nicks solution, but i needed 29,4 sec to "extract" 1195 images of an atlas file. So later i managed this way and needed 2,43 sec to do the same job. Maybe this will be helpful.

// content of the Texture class
public class Texture
{
    //name of the texture
    public string name { get; set; }
    //x position of the texture in the atlas image
    public int x { get; set; }
    //y position of the texture in the atlas image
    public int y { get; set; }
    //width of the texture in the atlas image
    public int width { get; set; }
    //height of the texture in the atlas image
    public int height { get; set; }
}

Bitmap atlasImage = new Bitmap(@"C:\somepicture.png");
PixelFormat pixelFormat = atlasImage.PixelFormat;

foreach (Texture t in textureList)
{
     try
     {
           CroppedImage = new Bitmap(t.width, t.height, pixelFormat);
           // copy pixels over to avoid antialiasing or any other side effects of drawing
           // the subimages to the output image using Graphics
           for (int x = 0; x < t.width; x++)
               for (int y = 0; y < t.height; y++)
                   CroppedImage.SetPixel(x, y, atlasImage.GetPixel(t.x + x, t.y + y));
           CroppedImage.Save(Path.Combine(workingFolder, t.name + ".png"), ImageFormat.Png);
     }
     catch (Exception ex)
     {
          // handle the exception
     }
}
GruMu
  • 63
  • 6
2

For anyone willing to use "unsafe" code, you can achieve much better performance than the standard System.Drawing.Graphics method, and even better if you are using Bitmap.Clone().

Just keep in mind 32bpp is the only format the methods support. (Other formats could work as long as 1 pixel is stored as 4bytes)

I've included 2 versions, one uses Span, which is slightly more performant when cropping to a smaller image. If cropping to a 1000x1000 image they are about the same speed.

Benchmarks are below if interested.

public static class BitmapExtension
{
    unsafe public static Bitmap Crop(this Bitmap bitmap, int left, int top, int width, int height)
    {
        Bitmap cropped = new Bitmap(width, height);
        BitmapData originalData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        BitmapData croppedData = cropped.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);

        int* srcPixel = (int*)originalData.Scan0 + (left + originalData.Width * top);
        int nextLine = originalData.Width - width;

        for (int y = 0, i = 0; y < height; y++, srcPixel += nextLine)
        {
            for (int x = 0; x < width; x++, i++, srcPixel++)
            {
                *((int*)croppedData.Scan0 + i) = *srcPixel;
            }
        }

        bitmap.UnlockBits(originalData);
        cropped.UnlockBits(croppedData);

        return cropped;
    }

    unsafe public static Bitmap CropSmall(this Bitmap bitmap, int left, int top, int width, int height)
    {
        Bitmap cropped = new Bitmap(width, height);
        BitmapData originalData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        BitmapData croppedData = cropped.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);

        Span<int> srcPixels = new Span<int>((void*)originalData.Scan0, originalData.Width * originalData.Height);

        int nextLine = originalData.Width - width;

        for (int y = 0, i = 0, s = left + originalData.Width * top; y < height; y++, s += nextLine)
        {
            for (int x = 0; x < width; x++, i++, s++)
            {
                *((int*)croppedData.Scan0 + i) = srcPixels[s];
            }
        }

        bitmap.UnlockBits(originalData);
        cropped.UnlockBits(croppedData);

        return cropped;
    }
}

Cropping 3440x1440 to 1000x1000

Method Ns
My Method 1108
My Method(Span) 1141
Graphics 9975
Clone() 21514

Cropping 3440x1440 to 256x256

Method Ns
My Method 131
My Method(Span) 95
Graphics 1289
Clone() 19680

Cropping 3440x1440 to 1440x1440

Method Ns
My Method 2237
My Method(Span) 2592
Graphics 9999
Clone() 25925
Addio
  • 89
  • 1
  • 6
1

Cropping an image is very easy in C#. However, doing the stuff how are you going to manage the cropping of your image will be a little harder.

Sample below is the way how to crop an image in C#.

var filename = @"c:\personal\images\horizon.png";
var img = Image.FromFile(filename);
var rect = new Rectangle(new Point(0, 0), img.Size);
var cloned = new Bitmap(img).Clone(rect, img.PixelFormat);
var bitmap = new Bitmap(cloned, new Size(50, 50));
cloned.Dispose();
Mike
  • 161
  • 1
  • 1
  • 7
1

There is a C# wrapper for that which is open source, hosted on Codeplex called Web Image Cropping

Register the control

<%@ Register Assembly="CS.Web.UI.CropImage" Namespace="CS.Web.UI" TagPrefix="cs" %>

Resizing

<asp:Image ID="Image1" runat="server" ImageUrl="images/328.jpg" />
<cs:CropImage ID="wci1" runat="server" Image="Image1" 
     X="10" Y="10" X2="50" Y2="50" />

Cropping in code behind - Call Crop method when button clicked for example;

wci1.Crop(Server.MapPath("images/sample1.jpg"));

Cem
  • 889
  • 2
  • 10
  • 22
0

Only this sample working without problem:

var crop = new Rectangle(0, y, bitmap.Width, h);
var bmp = new Bitmap(bitmap.Width, h);
var tempfile = Application.StartupPath+"\\"+"TEMP"+"\\"+Path.GetRandomFileName();


using (var gr = Graphics.FromImage(bmp))
{
    try
    {
        var dest = new Rectangle(0, 0, bitmap.Width, h);
        gr.DrawImage(image,dest , crop, GraphicsUnit.Point);
        bmp.Save(tempfile,ImageFormat.Jpeg);
        bmp.Dispose();
    }
    catch (Exception)
    {


    }

}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
0

This is another way. In my case I have:

  • 2 numeric updown controls (called LeftMargin and TopMargin)
  • 1 Picture box (pictureBox1)
  • 1 button that I called generate
  • 1 image on C:\imagenes\myImage.gif

Inside the button I have this code:

Image myImage = Image.FromFile(@"C:\imagenes\myImage.gif");
Bitmap croppedBitmap = new Bitmap(myImage);
croppedBitmap = croppedBitmap.Clone(
            new Rectangle(
                (int)LeftMargin.Value, (int)TopMargin.Value,
                myImage.Width - (int)LeftMargin.Value,
                myImage.Height - (int)TopMargin.Value),
            System.Drawing.Imaging.PixelFormat.DontCare);
pictureBox1.Image = croppedBitmap;

I tried it in Visual studio 2012 using C#. I found this solution from this page

sikander
  • 2,286
  • 16
  • 23
0

here it is working demo on github

https://github.com/SystematixIndore/Crop-SaveImageInCSharp

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication1.WebForm1" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
 <link href="css/jquery.Jcrop.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.Jcrop.js"></script>
</head>
<body>
  <form id="form2" runat="server">
  <div>
    <asp:Panel ID="pnlUpload" runat="server">
      <asp:FileUpload ID="Upload" runat="server" />
      <br />
      <asp:Button ID="btnUpload" runat="server" OnClick="btnUpload_Click" Text="Upload" />
      <asp:Label ID="lblError" runat="server" Visible="false" />
    </asp:Panel>
    <asp:Panel ID="pnlCrop" runat="server" Visible="false">
      <asp:Image ID="imgCrop" runat="server" />
      <br />
      <asp:HiddenField ID="X" runat="server" />
      <asp:HiddenField ID="Y" runat="server" />
      <asp:HiddenField ID="W" runat="server" />
      <asp:HiddenField ID="H" runat="server" />
      <asp:Button ID="btnCrop" runat="server" Text="Crop" OnClick="btnCrop_Click" />
    </asp:Panel>
    <asp:Panel ID="pnlCropped" runat="server" Visible="false">
      <asp:Image ID="imgCropped" runat="server" />
    </asp:Panel>
  </div>
  </form>
    <script type="text/javascript">
  jQuery(document).ready(function() {
    jQuery('#imgCrop').Jcrop({
      onSelect: storeCoords
    });
  });

  function storeCoords(c) {
    jQuery('#X').val(c.x);
    jQuery('#Y').val(c.y);
    jQuery('#W').val(c.w);
    jQuery('#H').val(c.h);
  };

</script>
</body>
</html>

C# code logic for upload and crop.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using SD = System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace WebApplication1
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        String path = HttpContext.Current.Request.PhysicalApplicationPath + "images\\";
        protected void Page_Load(object sender, EventArgs e)
        {

        }
        protected void btnUpload_Click(object sender, EventArgs e)
        {
            Boolean FileOK = false;
            Boolean FileSaved = false;

            if (Upload.HasFile)
            {
                Session["WorkingImage"] = Upload.FileName;
                String FileExtension = Path.GetExtension(Session["WorkingImage"].ToString()).ToLower();
                String[] allowedExtensions = { ".png", ".jpeg", ".jpg", ".gif" };
                for (int i = 0; i < allowedExtensions.Length; i++)
                {
                    if (FileExtension == allowedExtensions[i])
                    {
                        FileOK = true;
                    }
                }
            }

            if (FileOK)
            {
                try
                {
                    Upload.PostedFile.SaveAs(path + Session["WorkingImage"]);
                    FileSaved = true;
                }
                catch (Exception ex)
                {
                    lblError.Text = "File could not be uploaded." + ex.Message.ToString();
                    lblError.Visible = true;
                    FileSaved = false;
                }
            }
            else
            {
                lblError.Text = "Cannot accept files of this type.";
                lblError.Visible = true;
            }

            if (FileSaved)
            {
                pnlUpload.Visible = false;
                pnlCrop.Visible = true;
                imgCrop.ImageUrl = "images/" + Session["WorkingImage"].ToString();
            }
        }

        protected void btnCrop_Click(object sender, EventArgs e)
        {
            string ImageName = Session["WorkingImage"].ToString();
            int w = Convert.ToInt32(W.Value);
            int h = Convert.ToInt32(H.Value);
            int x = Convert.ToInt32(X.Value);
            int y = Convert.ToInt32(Y.Value);

            byte[] CropImage = Crop(path + ImageName, w, h, x, y);
            using (MemoryStream ms = new MemoryStream(CropImage, 0, CropImage.Length))
            {
                ms.Write(CropImage, 0, CropImage.Length);
                using (SD.Image CroppedImage = SD.Image.FromStream(ms, true))
                {
                    string SaveTo = path + "crop" + ImageName;
                    CroppedImage.Save(SaveTo, CroppedImage.RawFormat);
                    pnlCrop.Visible = false;
                    pnlCropped.Visible = true;
                    imgCropped.ImageUrl = "images/crop" + ImageName;
                }
            }
        }

        static byte[] Crop(string Img, int Width, int Height, int X, int Y)
        {
            try
            {
                using (SD.Image OriginalImage = SD.Image.FromFile(Img))
                {
                    using (SD.Bitmap bmp = new SD.Bitmap(Width, Height))
                    {
                        bmp.SetResolution(OriginalImage.HorizontalResolution, OriginalImage.VerticalResolution);
                        using (SD.Graphics Graphic = SD.Graphics.FromImage(bmp))
                        {
                            Graphic.SmoothingMode = SmoothingMode.AntiAlias;
                            Graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
                            Graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
                            Graphic.DrawImage(OriginalImage, new SD.Rectangle(0, 0, Width, Height), X, Y, Width, Height, SD.GraphicsUnit.Pixel);
                            MemoryStream ms = new MemoryStream();
                            bmp.Save(ms, OriginalImage.RawFormat);
                            return ms.GetBuffer();
                        }
                    }
                }
            }
            catch (Exception Ex)
            {
                throw (Ex);
            }
        }
    }
}
Dev-Systematix
  • 439
  • 6
  • 26
0

Assuming you mean that you want to take an image file (JPEG, BMP, TIFF, etc) and crop it then save it out as a smaller image file, I suggest using a third party tool that has a .NET API. Here are a few of the popular ones that I like:

LeadTools
Accusoft Pegasus Snowbound Imaging SDK

JohnFx
  • 34,542
  • 18
  • 104
  • 162