Nilesh Moradiya
Nilesh Moradiya

Reputation: 721

C# crop image from center

I am developing application using .NET(4.5) MVC(4.0) C#(5.0). i want to generate image thumbnail from image that i already have. Now requirement is like it should generate thumbnail of maximum square portion from center of image without stretching not whole image except image is square size.

as per example my original image size :578x700 i want to generate thumbnail for placeholder size :200x150, 185x138, 140x140, 89x66, 80x80, 45x45, 28x28

i had create my below code but didn't get exact result. here is my core method that generate thumbnail

    public string GenerateThumbnailFromImage(string imageFilePath, int thumbWidth, int thumbHeight)
    {
        try
        {
            //Check if file exist
            if (File.Exists(imageFilePath))
            {
                //bool preserveAspectRatio = true;
                string oldFilePath = imageFilePath;
                string folderPath = Path.GetDirectoryName(imageFilePath);
                string filename = Path.GetFileNameWithoutExtension(imageFilePath);

                //Rename file with thumbnail size
                filename = filename + "_" + thumbWidth.ToString() + Path.GetExtension(imageFilePath);
                imageFilePath = Path.Combine(folderPath, filename);


                using (Image image = Image.FromFile(oldFilePath))
                {
                    decimal originalWidth = image.Width;
                    decimal originalHeight = image.Height;
                    decimal requiredThumbWidth = thumbWidth;
                    decimal requiredThumbHeight = thumbHeight;
                    decimal startXPosition = 0;
                    decimal startYPosition = 0;
                    decimal screenWidth = originalWidth;
                    decimal screenHeight = originalHeight;
                    decimal ar = thumbWidth < thumbHeight 
                                     ? originalWidth / originalHeight
                                     : originalHeight / originalWidth;

                    //Define Starting Position for thumbnail generation
                    if (originalWidth > originalHeight)
                        startXPosition = (originalWidth - originalHeight) / 2;
                    else if (originalHeight > originalWidth)
                        startYPosition = (originalHeight - originalWidth) / 2;

                    if (thumbWidth>thumbHeight)
                    {
                        requiredThumbWidth = thumbWidth;
                        requiredThumbHeight = requiredThumbWidth*ar;
                    }
                    else if (thumbHeight>thumbWidth)
                    {
                        requiredThumbHeight = thumbHeight;
                        requiredThumbWidth = requiredThumbHeight*ar;
                    }
                    else
                    {
                        requiredThumbWidth = thumbWidth;
                        requiredThumbHeight = thumbWidth;
                    }

                    using (var bmp = new Bitmap((int)requiredThumbWidth, (int)requiredThumbHeight))
                    {
                        Graphics gr = Graphics.FromImage(bmp);
                        gr.SmoothingMode = SmoothingMode.HighQuality;
                        gr.CompositingQuality = CompositingQuality.HighQuality;
                        gr.InterpolationMode = InterpolationMode.High;
                        var rectDestination = new Rectangle(0, 0, (int)requiredThumbWidth, (int)requiredThumbHeight);

                        gr.DrawImage(image, rectDestination, (int)startXPosition, (int)startYPosition, (int)screenWidth, (int)screenHeight, GraphicsUnit.Pixel);
                        bmp.Save(imageFilePath);

                        return filename;
                    }
                }
            }
            return null;
        }
        catch (Exception ex)
        {
            GlobalUtil.HandleAndLogException(ex, this);
            throw ex;
        }
        finally
        {

        }
    }

Upvotes: 7

Views: 17448

Answers (7)

Reza Taba
Reza Taba

Reputation: 1914

I am using SkiaSharp. But it's not that different if you use other systems.

Enter widthIn and heightIn as your desired thumbnail's sides.

public async Task<string?> CropImageAndSave(string filePath, int widthIn, int heightIn)
{
    // get the image
    using SKImage sKImage = SKImage.FromEncodedData(filePath);

    // check if the given sides are not larger than the image size
    if (widthIn < sKImage.Width && heightIn < sKImage.Height)
    {
        // find the center
        int centerX = sKImage.Width / 2;
        int centerY = sKImage.Height / 2;

        // find the start points
        int startX = centerX - widthIn / 2;
        int startY = centerY - heightIn / 2;

        // crop the image
        SKImage croppedImage = sKImage.Subset(SKRectI.Create(startX, startY, widthIn, heightIn));
        using SKData sKData = croppedImage.Encode(SKEncodedImageFormat.Jpeg, 100);

        // (await) SAVE the image in your desired location

        // then
        return $"Your image is cropped; Width: {widthIn}, Height: {heightIn}";
    }
    else return null;
}

Upvotes: 0

Phil Wong
Phil Wong

Reputation: 41

Also using the ImageFactory NuGet package, I recommend using the Resize Crop feature.

See examples of this here

using ImageProcessor;
using ImageProcessor.Imaging;
using System.Drawing;
using System.IO;

public static byte[] ResizeAndCrop(byte[] image, int width, int height)
    {
        using (var ms = new MemoryStream())
        {
            using (var imgf = new ImageFactory(true))
                imgf
                    .Load(image)
                    .Resize(new ResizeLayer(new Size(width, height), ResizeMode.Crop))
                    .Save(ms);
            return ms.ToArray();
        }
    }

This will take any image in its byte[] format and crop from the center.

This is set by the anchorPosition parameter in the ResizeLayer which has a deafult value of AnchorPosition.Center

new ResizeLayer(new Size(width, height), ResizeMode.Crop/*, AnchorPosition.Center*/)

Upvotes: 3

morethanyell
morethanyell

Reputation: 326

In your NuGet package, add this ImageFactory solution by James South. Everything you need is there. I wish I could buy James more beer.

Sample usage:

using (ImageFactory imgf = new ImageFactory(preserveExifData: true)) {
    imgf
        .Load(img)
        .Crop(rect)
        .Format(new JpegFormat { Quality = 100 })
        .Save(destination)
}

// given that :
// 'img' is your Image object, could be an Image.FromFile() object or the likes
// 'rect' is the size of your crop and that you have already did the math
// new JpegFormat { Quality = 70 } is part of the package
// and 'destination' is the destination path of your new image in your disk

Upvotes: 4

Gabriel Calegari
Gabriel Calegari

Reputation: 341

public Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
    var ratioX = (double)maxWidth / image.Width;
    var ratioY = (double)maxHeight / image.Height;
    var ratio = Math.Min(ratioX, ratioY);

    var newWidth = (int)(image.Width * ratio);
    var newHeight = (int)(image.Height * ratio);

    var newImage = new Bitmap(maxWidth, maxWidth);
    using (var graphics = Graphics.FromImage(newImage))
    {
        // Calculate x and y which center the image
        int y = (maxHeight/2) - newHeight / 2;
        int x = (maxWidth / 2) - newWidth / 2;

        // Draw image on x and y with newWidth and newHeight
        graphics.DrawImage(image, x, y, newWidth, newHeight);
    }

    return newImage;
}

Upvotes: 3

h3n
h3n

Reputation: 5248

You need to get the ratio of the destination size against the actual size. Scale the shorter side until it touches the actual image size. Crop it starting from the center and scale it to the desired size.

Here is the code:



 public static Image ResizeImage(Image imgToResize, Size destinationSize)
        {
            var originalWidth = imgToResize.Width;
            var originalHeight = imgToResize.Height;

            //how many units are there to make the original length
            var hRatio = (float)originalHeight/destinationSize.Height;
            var wRatio = (float)originalWidth/destinationSize.Width;

            //get the shorter side
            var ratio = Math.Min(hRatio, wRatio);

            var hScale = Convert.ToInt32(destinationSize.Height * ratio);
            var wScale = Convert.ToInt32(destinationSize.Width * ratio);

            //start cropping from the center
            var startX = (originalWidth - wScale)/2;
            var startY = (originalHeight - hScale)/2;

            //crop the image from the specified location and size
            var sourceRectangle = new Rectangle(startX, startY, wScale, hScale);

            //the future size of the image
            var bitmap = new Bitmap(destinationSize.Width, destinationSize.Height);

            //fill-in the whole bitmap
            var destinationRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

            //generate the new image
            using (var g = Graphics.FromImage(bitmap))
            {
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.DrawImage(imgToResize, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
            }

            return bitmap;

        }


Call it like this:


var thumbImage = ImageHelper.ResizeImage(image, new Size(45, 45));
thumbImage.Save(thumbFullPath);

Upvotes: 18

Kashif Imran
Kashif Imran

Reputation: 159

Try this:

bool SaveCroppedImage(Image image, int targetWidth, int targetHeight, string filePath)
{
    ImageCodecInfo jpgInfo = ImageCodecInfo.GetImageEncoders().Where(codecInfo => codecInfo.MimeType == "image/jpeg").First();
    Image finalImage = image;
    System.Drawing.Bitmap bitmap = null;
    try
    {
        int left = 0;
        int top = 0;
        int srcWidth = targetWidth;
        int srcHeight = targetHeight;
        bitmap = new System.Drawing.Bitmap(targetWidth, targetHeight);
        double croppedHeightToWidth = (double)targetHeight / targetWidth;
        double croppedWidthToHeight = (double)targetWidth / targetHeight;

        if (image.Width > image.Height)
        {
            srcWidth = (int)(Math.Round(image.Height * croppedWidthToHeight));
            if (srcWidth < image.Width)
            {
                srcHeight = image.Height;
                left = (image.Width - srcWidth) / 2;
            }
            else
            {
                srcHeight = (int)Math.Round(image.Height * ((double)image.Width / srcWidth));
                srcWidth = image.Width;
                top = (image.Height - srcHeight) / 2;
            }
        }
        else
        {
            srcHeight = (int)(Math.Round(image.Width * croppedHeightToWidth));
            if (srcHeight < image.Height)
            {
                srcWidth = image.Width;
                top = (image.Height - srcHeight) / 2;
            }
            else
            {
                srcWidth = (int)Math.Round(image.Width * ((double)image.Height / srcHeight));
                srcHeight = image.Height;
                left = (image.Width - srcWidth) / 2;
            }
        }
        using (Graphics g = Graphics.FromImage(bitmap))
        {
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighQuality;
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(left, top, srcWidth, srcHeight), GraphicsUnit.Pixel);
        }
        finalImage = bitmap;
    }
    catch { }
    try
    {
        using (EncoderParameters encParams = new EncoderParameters(1))
        {
            encParams.Param[0] = new EncoderParameter(Encoder.Quality, (long)100);
            //quality should be in the range [0..100] .. 100 for max, 0 for min (0 best compression)
            finalImage.Save(filePath, jpgInfo, encParams);
            return true;
        }
    }
    catch { }
    if (bitmap != null)
    {
        bitmap.Dispose();
    }
    return false;
}

Upvotes: 8

Colin Steel
Colin Steel

Reputation: 1055

Done this a few times before, the trick is to fit the Height of the image first, the rescale the Width to the proportion that you had to reduce the Height, then repeat from the Width if it still doesnt fit by the Width, and reducing the newer scaled Height by that additional proportion. That way you have a thumbnail that always fits, possibly some whitespace in the X or Y, but the image is still to the same proportions, not stretched.

int originalHeight;
int originalWidth;
int imageHeight;
int imageWidth;
int requiredHeight;
int requiredWidth;
double scale;

if(originalHeight > requiredHeight)
{
    scale = requiredHeight / originalHeight;
    imageHeight = requiredHeight;
    imageWidth = originalHeight * scale;
}

if(imageWidth > requiredWidth)
{
    scale = requiredWidth / imageWidth;
    imageWidth = requiredWidth;
    imageHeight = imageHeight * scale;
}

And then drawing that Image into a new Bitmap of this size using Graphics object

Upvotes: 1

Related Questions