oneimage

Crop an image in C#

Crop images in C# with pure .NET code: a rectangle, a centered square, a fixed aspect ratio, smart crops, trimmed borders and user-picked crops in ASP.NET Core.

To crop an image in C#, load it, call Crop(x, y, width, height) with the rectangle you want to keep, and save. With OneImage that is one line, and it runs on Windows, Linux, macOS and in containers with no native libraries. This page covers the common crops: a fixed rectangle, a centered square, a fixed aspect ratio with a chosen focus, trimming borders, cutting tiles, and a user-picked crop from an upload. Every sample was run against OneImage 1.6.0 on .NET 10.

TerminalNuGet
dotnet add package RecursiveAI.OneImage

OneImage targets .NET 10. The examples use a 3000×3002 photograph (photo.jpg) and a phone-style photo (portrait.jpg) that is stored as 1200×800 with an EXIF tag saying it should be shown rotated, so it appears as 800×1200.

Crop a rectangle

Program.cs.NET 10
using OneImage;

// Keep the 1200x800 region whose top-left corner is at (100, 80).
Image.Load("photo.jpg").Crop(100, 80, 1200, 800).Save("crop.jpg");

This writes a 1200×800 JPEG. Coordinates start at the top-left corner, and the rectangle must lie inside the image. One that doesn’t throws an ArgumentOutOfRangeException (“Crop rectangle lies outside the image.”), so clamp coordinates that come from users, as in the upload example below. Crop is lazy like every OneImage operation: tiles outside the rectangle are never computed, and decoders that support it decode only the rectangle.

Crop to a centered square

Program.cs.NET 10
using OneImage;

Image photo = Image.Load("portrait.jpg");   // 800x1200 once upright

int side = Math.Min(photo.Width, photo.Height);
int x = (photo.Width - side) / 2;
int y = (photo.Height - side) / 2;

photo.Crop(x, y, side, side).Save("square.jpg");   // 800x800 from the middle

The result is 800×800 from the middle of the photo. Width and Height are the upright size, because Load applies the EXIF orientation first. That matters for every crop. Coordinates computed against the stored, sideways layout of a phone photo cut the wrong region. Our page on EXIF orientation in C# explains what the tag does.

Crop to an aspect ratio and size

For banners, cards and thumbnails you usually want a fixed output size and don’t care about the source’s exact pixels. Resize with Fit.Cover to fill the box and crop what overflows, all in one step:

Program.cs.NET 10
using OneImage;

Image photo = Image.Load("photo.jpg");

// Crop to 16:9 and resize to 1280x720 in one step, keeping the center.
photo.Resize(width: 1280, height: 720, fit: Fit.Cover).Save("wide-center.jpg");

// Keep the top instead, or let OneImage pick the busiest area.
photo.Resize(width: 1280, height: 720, fit: Fit.Cover, anchor: Anchor.Top).Save("wide-top.jpg");
photo.Resize(width: 1280, height: 720, fit: Fit.Cover, anchor: Anchor.Smart).Save("wide-smart.jpg");

All three are 1280×720. anchor: chooses which part survives: Center (the default), one of the eight edges and corners, or Smart, which picks the busiest region of the picture. When a portrait photo goes into a landscape box, Anchor.Top keeps the top of the picture instead of the middle. To get the result at the source resolution instead, compute the rectangle as in the square example. For small copies with private metadata removed, Image.Thumbnail(path, width, height, fit: Fit.Cover) does the same crop. See resizing images in C#.

Trim white borders

Program.cs.NET 10
using OneImage;

// Remove the uniform border around a scan or screenshot.
Image trimmed = Image.Load("scan.png").Trim();
Console.WriteLine($"{trimmed.Width}x{trimmed.Height}");
trimmed.Save("trimmed.png");

Our 1000×700 test scan, a logo on a white page, came out at 561×261, exactly the logo’s painted area. Trim removes a uniform border. By default it compares every pixel against the top-left pixel’s color, with a tolerance of 10 levels per channel. Trim(threshold: 30) loosens the tolerance for noisy scans, and background: names the border color. It is the one geometry operation that reads pixels straight away, because it has to find the border.

Cut an image into tiles

Program.cs.NET 10
using OneImage;

Image photo = Image.Load("photo.jpg");   // loading decodes nothing yet
int tileWidth = photo.Width / 2;
int tileHeight = photo.Height / 2;

for (int row = 0; row < 2; row++)
{
    for (int col = 0; col < 2; col++)
    {
        photo.Crop(col * tileWidth, row * tileHeight, tileWidth, tileHeight)
             .Save($"tile-{row}-{col}.png");
    }
}

This writes four 1500×1501 PNGs. One loaded Image feeds every crop, since images are immutable, and each save computes only its own region.

Crop an uploaded image in ASP.NET Core

Avatar and cover-photo pickers let the user drag a square in the browser and send its coordinates with the file. Browsers show photos upright: CSS image-orientation defaults to from-image. Because OneImage also loads them upright, the browser’s coordinates line up without extra work:

Program.csASP.NET Core · .NET 10
using OneImage;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// The browser sends the square the user picked, in the upright image's pixels.
app.MapPost("/avatar", async (IFormFile file, int x, int y, int size) =>
{
    try
    {
        await using Stream upload = file.OpenReadStream();
        Image photo = Image.Load(upload);   // upright, like the preview the user saw

        // Keep the square inside the image instead of failing on a stray pixel.
        x = Math.Clamp(x, 0, photo.Width - 1);
        y = Math.Clamp(y, 0, photo.Height - 1);
        size = Math.Min(size, Math.Min(photo.Width - x, photo.Height - y));
        if (size < 1)
        {
            return Results.BadRequest("Empty selection.");
        }

        Image avatar = photo.Crop(x, y, size, size)
                            .Resize(width: 256, height: 256)
                            .StripMetadata();

        var webp = new MemoryStream();
        await avatar.SaveAsync(webp, WebPFormat.Instance);
        webp.Position = 0;
        return Results.File(webp, "image/webp");
    }
    catch (ImageException ex) // not an image, damaged, or over the size limits
    {
        return Results.BadRequest(ex.Message);
    }
}).DisableAntiforgery();

app.Run();

We posted a phone photo stored sideways (1200×800, displayed 800×1200) with ?x=100&y=300&size=500. The endpoint returned a 256×256 WebP of that region of the upright picture, with no EXIF block. A selection hanging off the bottom-right corner was clamped to the 100 px that remained and still returned 200. A non-image upload got a 400 with “The data is not in a recognized image format.” Size limits are on by default and checked from the file headers, so an oversized upload fails with an ImageException before it is decoded.

Cropping in System.Drawing, ImageSharp and SkiaSharp

For everything else OneImage does, see the overview.