Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App Engine: Calculating the dimensions of thumbnails to be generated by serving thumbnails from the blobstore

I'm currently using the blobstore to generate thumbnails for images, however, I like to store the dimensions of the thumbnail in the img tag, as it is good practise and helps speed up rendering and makes a partially loaded page look a bit nicer.

How would I calculate the dimensions of a thumbnail generated by the blobstore, knowing only the dimensions of the original image?

My previous attempts haven't been very accurate, most of the time being off by a pixel or two (probably due to rounding).

I understand that fetching the thumbnail and than using the images API to check the dimensions would work, but I think that is inefficient.

Here is the code I use to calculate it at the moment, however, it is occasionally off by one pixel, causing the browser to slightly stretch the image, causing resize artefacts as well as being less performant.

from __future__ import division
def resized_size(original_width, original_height, width, height):
    original_ratio = float(original_width) / float(original_height)
    resize_ratio = float(width) / float(height)
    if original_ratio >= resize_ratio:
        return int(width), int(round(float(width) / float(original_ratio)))
    else:
        return int(round(float(original_ratio) * float(height))), int(height)

Accuracy is very important!

like image 603
Noah McIlraith Avatar asked Oct 12 '22 15:10

Noah McIlraith


1 Answers

I see the problem. The reason is that C's rint is being used to calculate the dimensions. Python does not have an equivalent rint implementation as it was taken out by Rossum in 1.6:

http://markmail.org/message/4di24iqm7zhc4rwc

Your only recourse right now is to implement your own rint in python.

rint by default does a "round to even" vs pythons round which does something else. Here is a simple implementation (no edge case handling for +inf -inf, etc.)

import math

def rint(x):
  x_int = int(x)
  x_rem = x - x_int  # this is problematic
  if (x_int % 2) == 1:
    return round(x)
  else:
    if x_rem <= 0.5:
      return math.floor(x)
    else:
      return math.ceil(x)

The above code is how it should be implemented in theory. The problem lies with x_rem. x - x_int should get the fractional componenet but instead you can get the fraction + delta. So you can attempt to add thresholding if you want

import math

def rint(x):
  x_int = int(x)
  x_rem = x - x_int
  if (x_int % 2) == 1:
    return round(x)
  else:
    if x_rem - 0.5 < 0.001:
      return math.floor(x)
    else:
      return math.ceil(x)

Over here. I hard coded a 0.001 threshold. Thresholding itself is problematic. I guess you really need to play around with the rint implementation and fit it to your application and see what works best. Good Luck!

like image 188
Clint Avatar answered Oct 20 '22 12:10

Clint