Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image Resize in C# - Algorith to determine resize dimensions (height and width)

I need to scale down an image that has a height or width greater than a predefined pixel value.

I wrote some code that takes a look at the original image, checks to see if either the width, the height, or the height and width are greater than the Max Width/Max Height settings.

I now need to figure out what dimensions to resize to based on the max of the latter value.

For example: If the image is 900h x 300w and the MAX Height is 700h I will need to resize the height to 700 and the width to ???? <-- this is what I need to calculate..

Creating and saving the image file is simple, and outside the scope of this post:

// First I get the max height and width allowed:

int resizeMaxHeight =  int.Parse(Utility.GetConfigValue("ResizeMaxHeight")); // in config: 700px
int resizeMaxWidth =  int.Parse(Utility.GetConfigValue("ResizeMaxWidth"));  //  in config: 500px

// Save original: 
try
{
    filebase.SaveAs(savedFileName);
}
catch (System.IO.DirectoryNotFoundException ex)
{
    Logger.Instance.LogException(ex, 0, "FileTransfer");
}

// Determin original dimensions:
Image image = System.Drawing.Image.FromFile(Server.MapPath(savedFileName));

int resizeHeight, resizeWidth;
bool doResize = true;

// both height and width are greater than the allowed height and width:
if (image.Width > resizeMaxWidth && image.Height > resizeMaxHeight)
{
    if (image.Height > image.Width) 
        resizeHeight = resizeMaxHeight;
    else
        resizeWidth = resizeMaxWidth;
}
else if (image.Width > resizeMaxWidth)
{
    // width is too great, but height is ok
    resizeWidth = resizeMaxWidth;
}
else if (image.Height > resizeMaxHeight)
{
    // height is too great, but width is ok
    resizeHeight = resizeMaxHeight;
}
else
{
    // image is ok size, don't resize:
    doResize = false;
}

Create thumbnail: This is what I'm working now... not complete:

if (doResize)
{
    ImageUtilities.ResizeImage(image, resizeWidth, resizeHeight);
}
like image 412
Shawn J. Molloy Avatar asked Mar 07 '11 17:03

Shawn J. Molloy


People also ask

How do I resize an image in code?

One of the simplest ways to resize an image in the HTML is using the height and width attributes on the img tag. These values specify the height and width of the image element. The values are set in px i.e. CSS pixels.

Can you resize images in CSS?

The background-size CSS property lets you resize the background image of an element, overriding the default behavior of tiling the image at its full size by specifying the width and/or height of the image. By doing so, you can scale the image upward or downward as desired.


4 Answers

The solution posted by Nathaniel actually fails if the image height is larger than the image width. The following example yields the correct result :

private Size ResizeFit(Size originalSize, Size maxSize)
{
    var widthRatio = (double)maxSize.Width / (double)originalSize.Width;
    var heightRatio = (double) maxSize.Height/(double) originalSize.Height;
    var minAspectRatio = Math.Min(widthRatio, heightRatio);
    if (minAspectRatio > 1)
        return originalSize;
    return new Size((int)(originalSize.Width*minAspectRatio), (int)(originalSize.Height*minAspectRatio));
}
like image 128
jbc Avatar answered Oct 18 '22 16:10

jbc


Here are two ways to make this calculation. Depending upon how you think about the problem, one may seem more intuitive than the other. They are mathematically equivalent to several decimal places.

Both are safe for Math.Round, but only ConstrainVerbose produces results that are always less than maxWidth/maxHeight.

SizeF ConstrainConcise(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
    // Downscale by the smallest ratio (never upscale)
    var scale = Math.Min(1, Math.Min(maxWidth / (float)imageWidth, maxHeight / (float) imageHeight));
    return new SizeF(scale * imageWidth, scale * imageHeight);
}

SizeF ConstrainVerbose(int imageWidth, int imageHeight, int maxWidth, int maxHeight){
    // Coalculate the aspect ratios of the image and bounding box
    var maxAspect = (float) maxWidth / (float) maxHeight;
    var aspect =  (float) imageWidth / (float) imageHeight;
    // Bounding box aspect is narrower
    if (maxAspect <= aspect && imageWidth > maxWidth)
    {
        // Use the width bound and calculate the height
        return new SizeF(maxWidth, Math.Min(maxHeight, maxWidth / aspect));
    }
    else if (maxAspect > aspect && imageHeight > maxHeight)
    {
        // Use the height bound and calculate the width
        return new SizeF(Math.Min(maxWidth, maxHeight * aspect), maxHeight);
    }else{
        return new SizeF(imageWidth, imageHeight);
    }
}

Brute force unit-test here

like image 44
Lilith River Avatar answered Oct 18 '22 15:10

Lilith River


You can avoid calculating the aspect ratio (and using doubles) using a few integer tricks..

// You have the new height, you need the new width
int orgHeight = 1200;
int orgWidth = 1920;

int newHeight = 400;
int newWidth = (newHeight * orgWidth) / orgHeight; // 640

or...

// You have the new width, you need the new height.
int orgWidth = 1920;
int orgHeight = 1200;

int newWidth = 800;
int newHeight = (newWidth * orgHeight) / orgWidth; // 500

The following example will resize an image to any desired rectangle (desWidth and desHeight) and center the image within that rectangle.

static Image ResizeImage(Image image, int desWidth, int desHeight)
{
    int x, y, w, h;

    if (image.Height > image.Width)
    {
        w = (image.Width * desHeight) / image.Height;
        h = desHeight;
        x = (desWidth - w) / 2;
        y = 0;
    }
    else
    {
        w = desWidth;
        h = (image.Height * desWidth) / image.Width;
        x = 0;
        y = (desHeight - h) / 2;
    }

    var bmp = new Bitmap(desWidth, desHeight);

    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(image, x, y, w, h);
    }

    return bmp;
}
like image 32
Andy S Avatar answered Oct 18 '22 16:10

Andy S


I did something similar for Bitmaps, but idea is same:

1. get image height and width
2. get current screen resolution
3. calculate aspect ratio (ASR) from image size

Handle following cases:

4. if ASR >=1 and image width > image height
    if image width > screen width {}
        if image height > screen height {}
        else if image width > screen width {}
    else {}
   else
    if image height > screen height {}
    else if image width > screen width {}
    else {}

//SCREEN_SIZE is configurable; Defs.SCREEN_SIZE = 100; // and boolPixelAR is true;

Try following code:

            // PERCENTAGE OF IMAGE -> TODO: Configurable? IMAZE ZOOM / SCREEN PERCENTAGE
            Double HScale = __bmp.Width;// *Defs.SCREEN_SIZE / 100;
            Double VScale = __bmp.Height;// *Defs.SCREEN_SIZE / 100;
            Double __aspectRatio;
            Double __screenRatio = _currentScreenSize.Width / _currentScreenSize.Height;

            // PERCENTAGE OF SCREEN
            if (!_boolPixelAR) {
                HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
            }
            else {
                __aspectRatio = HScale / VScale;
                if( __aspectRatio >= 1)
                    if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                        VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                        HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;

                        if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                            //__aspectRatio = VScale / HScale;
                            HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                            VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                        }
                    }
                    else if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                        //__aspectRatio = VScale / HScale;
                        HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                        VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                    } 
                    else {
                        //Do nothing... Just set Zoom.
                        HScale = HScale * Defs.SCREEN_SIZE / 100;
                        VScale = VScale * Defs.SCREEN_SIZE / 100;
                    }
                else 
                    if (VScale > _currentScreenSize.Height) {                  // Long Edge is HEIGHT. For 100%, VScale = HEIGHT
                        //__aspectRatio = VScale / HScale;
                        HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                        VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
                    }
                    else if (HScale >= _currentScreenSize.Width) {  // Long Edge is WIDTH. For 100%, HScale = WIDTH
                        VScale = ((VScale * _currentScreenSize.Width) / HScale) * Defs.SCREEN_SIZE / 100;
                        HScale = _currentScreenSize.Width * Defs.SCREEN_SIZE / 100;
                    } 
                    else {
                        //Do nothing... Just set Zoom.
                        HScale = HScale * Defs.SCREEN_SIZE / 100;
                        VScale = VScale * Defs.SCREEN_SIZE / 100;
                    }

                ////__aspectRatio = VScale / HScale;
                //HScale = ((HScale * _currentScreenSize.Height) / VScale) * Defs.SCREEN_SIZE / 100;
                //VScale = _currentScreenSize.Height * Defs.SCREEN_SIZE / 100;
            }

            Bitmap scaledBmp = GraphicsFactory.ResizeImage(
                                        __bmp,
                                        Convert.ToInt32(HScale),
                                        Convert.ToInt32(VScale));
like image 21
ukhardy Avatar answered Oct 18 '22 17:10

ukhardy