How to “smart resize” a displayed image to original aspect ratio

旧时模样 提交于 2019-12-02 19:35:42

If I'm interpreting your spec correctly, you want a result that is no larger than the one the end-user laid out originally; you want one of the two dimensions to shrink, and the other to stay the same. In other words, the new size should fill the designer space in one direction while shortening the size in the other direction to retain the original aspect ratio.

original_ratio = original_width / original_height
designer_ratio = designer_width / designer_height
if original_ratio > designer_ratio
    designer_height = designer_width / original_ratio
else
    designer_width = designer_height * original_ratio

Often you'll be working with integer coordinates, but the divisions to produce the ratios above need to be floating point. Here's a rearrangement of the formula to be more integer friendly. Make sure your integers have the range to handle the maximum width*height.

if original_width * designer_height > designer_width * original_height
    designer_height = (designer_width * original_height) / original_width
else
    designer_width = (designer_height * original_width) / original_height
Jonathan Azulay

Here's a solution I came up with when having to deal with this problem. Turned out pretty short and straightforward imo, just wanted to share it.

Handy "formulas": ratio = W / H W = H * ratio H = W / ratio

  1. Calculate the ratio.
  2. Calculate the height of the image if you would set the width to the new size width (maximum allowed width).
  3. Calculate the width of the image if you would set the height to the new size height (maximum allowed height).
  4. See which of the two sizes does not override the max size in any dimension. If the ratio is not 1 one of them will always be wrong and one will always be correct.

In Javascript

// Returns array with new width and height
function proportionalScale(originalSize, newSize)
{
    var ratio = originalSize[0] / originalSize[1];

    var maximizedToWidth = [newSize[0], newSize[0] / ratio];
    var maximizedToHeight = [newSize[1] * ratio, newSize[1]];

    if (maximizedToWidth[1] > newSize[1]) { return maximizedToHeight; }
    else { return maximizedToWidth; }
}

originalSize and newSize is an array [0] = width, [1] = height

I also wanted to know this and all I saw were endless examples of scaling width OR height but could leave the other overflowing.

  • Resize width AND height without the need for a loop
  • Doesn't exceed the images original dimensions

.

private void ResizeImage(Image img, double maxWidth, double maxHeight)
{
  double srcWidth = img.Source.Width;
  double srcHeight = img.Source.Height;

  double resizeWidth = srcWidth;
  double resizeHeight = srcHeight;

  double aspect = resizeWidth / resizeHeight;

  if (resizeWidth > maxWidth)
  {
    resizeWidth = maxWidth;
    resizeHeight = resizeWidth / aspect;
  }
  if (resizeHeight > maxHeight)
  {
    aspect = resizeWidth / resizeHeight;
    resizeHeight = maxHeight;
    resizeWidth = resizeHeight * aspect;
  }

  img.Width = resizeWidth;
  img.Height = resizeHeight;
}

Took the suggestion above and made it scale up/down within max height / width. Here the python code for it and also added support for rotating things while keeping within limites:

def _resize(image, dimensions, rotate=None): """ Resizes an image to be as close as possible to specified dimensions. Image is a django image-model-field.

    Will both scale up and down the image to meet this while keeping the proportions
    in width and height

"""

if image and os.path.isfile(image.path):

    im = pil.open(image.path)
    logging.debug('resizing image from %s x %s --> %s x %s ' % (im.size[0], im.size[1], dimensions[0], dimensions[1]))

    if rotate:
        logging.debug('first rotating image %s' % rotate)
        im = im.rotate(90)

    srcWidth = Decimal(im.size[0])
    srcHeight = Decimal(im.size[1])

    resizeWidth = srcWidth
    resizeHeight = srcHeight

    aspect = resizeWidth / resizeHeight # Decimal

    logging.debug('resize aspect is %s' % aspect)

    if resizeWidth > dimensions[0] or resizeHeight > dimensions[1]:
        # if width or height is bigger we need to shrink things
        if resizeWidth > dimensions[0]:
            resizeWidth = Decimal(dimensions[0])
            resizeHeight = resizeWidth / aspect

        if resizeHeight > dimensions[1] :
            aspect = resizeWidth / resizeHeight
            resizeHeight = Decimal(dimensions[1])
            resizeWidth = resizeHeight * aspect

    else:
        # if both width and height are smaller we need to increase size
        if resizeWidth < dimensions[0]:
            resizeWidth = Decimal(dimensions[0])
            resizeHeight = resizeWidth / aspect

        if resizeHeight > dimensions[1] :
            aspect = resizeWidth / resizeHeight
            resizeHeight = Decimal(dimensions[1])
            resizeWidth = resizeHeight * aspect

    im = im.resize((resizeWidth, resizeHeight), pil.ANTIALIAS)

    logging.debug('resized image to %s %s' % im.size)
    im.save(image.path)

else:
    # no action, due to no image or no image in path
    pass

return image

Because you want to maximize showing as much as possible the scaled image (of the original) in your window, i.e. the area in your designer, you would take the larger of either the width or height of the original image, and scale that to 200. Pseudo-code (width, height are dimensions of original):

if (width > height) {
    scaledWidth = 200;
    scaledHeight = (height * 200) / width;
} else {
    scaledHeight = 200;
    scaledWidth = (width * 200) / height;
}

Calculate the new dimensions for both variants ("scale by width" and "scale by height"), then use the one that fits in the display.

Alternatively you could also calculate the the aspect ratio of the "bounding box" and compare it against the aspect ratio of the original image. Depending on which aspect ratio is larger, the height or the width needs to be scaled.

You also could restrict the resize process so that in all cases "scale by width" is done. Then, to change the size of the image, the user always has to change its width. The height will always be adjusted automatically.

Exlord

My solution to shrink and grow size in javascript based on https://stackoverflow.com/a/5654847/1055015

var scale =  function (srcWidth, srcHeight, maxWidth, maxHeight) {

      let resizeWidth  = srcWidth;
      let resizeHeight = srcHeight;

      let aspect = resizeWidth / resizeHeight;
      let scaleX = maxWidth / srcWidth;
      let scaleY = maxHeight / srcHeight;
      let scale  = Math.min(scaleX, scaleY);

      resizeWidth *= scale;
      resizeHeight *= scale;

      if (resizeWidth > maxWidth) {
        resizeWidth  = maxWidth;
        resizeHeight = resizeWidth / aspect;
      }

      if (resizeHeight > maxHeight) {
        aspect       = resizeWidth / resizeHeight;
        resizeHeight = maxHeight;
        resizeWidth  = resizeHeight * aspect;
      }

      return {
        width : resizeWidth,
        height: resizeHeight,
      };
    }

You just need to work out the scale needed for both dimensions and then take the smaller of the 2.

here is my solution,

a = aspect sw = original image width sh = original image height dw = requested max width dh = requested max height

sw and sh will contain the final resized values

code is PHP:

$a = $sw / $sh;

if($a > 1){
    //  wider image
    if($sw != $dw){
        $rt = $dw / $sw;
        $sw = $sw * $rt;
        $sh = $sh * $rt;
    }

    if($sh > $dh){
        $rt = $dh / $sh;
        $sw = $sw * $rt;
        $sh = $sh * $rt;
    }
}else{
    //  taller image
    if($sh != $dh){
        $rt = $dh / $sh;
        $sh = $sh * $rt;
        $sw = $sw * $rt;
    }

    if($sw > $dw){
        $rt = $dw / $sw;
        $sh = $sh * $rt;
        $sw = $sw * $rt;
    }
}

I used this way to resize the image to FHD way

if ( width >= height) {
  var original_ratio = width / height
  new_width = 1080 * original_ratio
  console.log("new new_width = " + Math.round(new_width) );
  console.log("new new_height = 1080");
} else {
  var original_ratio =  height / width
  new_height = 1080 * original_ratio
  console.log("new new_height = " + Math.round(new_height) );
  console.log("new new_width = 1080");
}

you can change the 1080 to the new size.

I hope it's useful for someone at least.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!