sandy101
sandy101

Reputation: 3427

How do I crop an image using C#?

How do I crop an image using C#?

Upvotes: 273

Views: 335418

Answers (15)

Daniel LeCheminant
Daniel LeCheminant

Reputation: 51121

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

Upvotes: 251

ChrisJJ
ChrisJJ

Reputation: 2302

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.

Upvotes: 80

Addio
Addio

Reputation: 137

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

Upvotes: 2

IntellyDev
IntellyDev

Reputation: 131

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");
            }
        }
    }

Upvotes: 10

Nick
Nick

Reputation: 5908

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

Upvotes: 304

GruMu
GruMu

Reputation: 73

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
     }
}

Upvotes: 5

Dev-Systematix
Dev-Systematix

Reputation: 449

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

Upvotes: 0

Mateen Ulhaq
Mateen Ulhaq

Reputation: 27271

If you're using AForge.NET:

using(var croppedBitmap = new Crop(new Rectangle(10, 10, 10, 10)).Apply(bitmap))
{
    // ...
}

Upvotes: 4

Ricardo Calvimonte
Ricardo Calvimonte

Reputation: 1

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

Upvotes: 0

user2757577
user2757577

Reputation: 47

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)
    {


    }

}

Upvotes: 0

Cem
Cem

Reputation: 889

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"));

Upvotes: 1

Mike
Mike

Reputation: 161

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();

Upvotes: 1

PsychoCoder
PsychoCoder

Reputation: 10765

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

Upvotes: 7

JohnFx
JohnFx

Reputation: 34907

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

Upvotes: 0

Guffa
Guffa

Reputation: 700720

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.

Upvotes: 5

Related Questions