Scale Image to Fit a Bounding Box

Scale image to fit a bounding box

Note: Even though this is the accepted answer, the answer below is more accurate and is currently supported in all browsers if you have the option of using a background image.

Edit 2: In the modern age, using object-fit might be an even better solution: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit

No, there is no CSS only way to do this in both directions. You could add

.fillwidth {
min-width: 100%;
height: auto;
}

To the an element to always have it 100% width and automatically scale the height to the aspect ratio, or the inverse:

.fillheight {
min-height: 100%;
width: auto;
}

to always scale to max height and relative width. To do both, you will need to determine if the aspect ratio is higher or lower than it's container, and CSS can't do this.

The reason is that CSS does not know what the page looks like. It sets rules beforehand, but only after that it is that the elements get rendered and you know exactly what sizes and ratios you're dealing with. The only way to detect that is with JavaScript.


Although you're not looking for a JS solution I'll add one anyway if someone might need it. The easiest way to handle this with JavaScript is to add a class based on the difference in ratio. If the width-to-height ratio of the box is greater than that of the image, add the class "fillwidth", else add the class "fillheight".

$('div').each(function() {
var fillClass = ($(this).height() > $(this).width())
? 'fillheight'
: 'fillwidth';
$(this).find('img').addClass(fillClass);
});
.fillwidth { 
width: 100%;
height: auto;
}
.fillheight {
height: 100%;
width: auto;
}

div {
border: 1px solid black;
overflow: hidden;
}

.tower {
width: 100px;
height: 200px;
}

.trailer {
width: 200px;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tower">
<img src="http://placekitten.com/150/150" />
</div>
<div class="trailer">
<img src="http://placekitten.com/150/150" />
</div>

Resize Image to fit in bounding box

Find which is smaller: MaxWidth / w or MaxHeight / h
Then multiply w and h by that number

Explanation:

You need to find the scaling factor which makes the image fit.

To find the scaling factor, s, for the width, then s must be such that:
s * w = MaxWidth.
Therefore, the scaling factor is MaxWidth / w.

Similarly for height.

The one that requires the most scaling (smaller s) is the factor by which you must scale the whole image.

Scale image to completely fill bounding box

What you're asking for is pretty easy. Calculate the different scaling factors for the width and the height, then pick the larger one for your actual scale factor. Multiply your input size by the scale, and crop whichever one comes out too large.

scale = max(maxwidth/oldwidth, maxheight/oldheight)
scaledwidth = oldwidth * scale
scaledheight = oldheight * scale
if scaledheight > maxheight:
croptop = (scaledheight - maxheight) / 2
cropbottom = (scaledheight - maxheight) - croptop
if scaledwidth > maxwidth:
cropleft = (scaledwidth - maxwidth) / 2
cropright = (scaledwidth - maxwidth) - cropleft

Resize bounding box according to image

My problem was solved within this post by a user named @lenik.

Before applying the scale factor to the ground truth box g's pixel coordinates, you must firstly subtract the zero offset so that x1, y1 becomes 0, 0. This allows scaling to work properly.

Thus, the coordinates of any random point (x,y) after the transformation can be calculated as:

x_new = (x - x1) * IMG_SIZE / (x2 - x1)
y_new = (y - y1) * IMG_SIZE / (y2 - y1)

In code and in relation to my problem, the solution is as follows:

def next_state(init_input, b_prime, g):
"""
Returns the observable region of the next state.

Formats the next state's observable region, defined
by b_prime, to be of dimension (224, 224, 3). Adding 16
additional pixels of context around the original bounding box.
The ground truth box must be reformatted according to the
new observable region.

:param init_input:
The initial input volume of the current episode.

:param b_prime:
The subsequent state's bounding box.

:param g:
The ground truth box of the target object.
"""

# Determine the pixel coordinates of the observable region for the following state
context_pixels = 16
x1 = max(b_prime[0] - context_pixels, 0)
y1 = max(b_prime[1] - context_pixels, 0)
x2 = min(b_prime[2] + context_pixels, IMG_SIZE)
y2 = min(b_prime[3] + context_pixels, IMG_SIZE)

# Determine observable region
observable_region = cv2.resize(init_input[y1:y2, x1:x2], (224, 224), interpolation=cv2.INTER_AREA)

# Resize ground truth box
g[0] = int((g[0] - x1) * IMG_SIZE / (x2 - x1)) # x1
g[1] = int((g[1] - y1) * IMG_SIZE / (y2 - y1)) # y1
g[2] = int((g[2] - x1) * IMG_SIZE / (x2 - x1)) # x2
g[3] = int((g[3] - y1) * IMG_SIZE / (y2 - y1)) # y2

return observable_region, g

Resize bounding boxes in tensorflow, for different size input images

You should be scaling your bounding box coordinates with respect to their input size : (x, y) --> (x/w, y/h), like most of the object detection algorithms do.

This way,

  • you can dynamically change your input size (but keeping the same aspect ratio) without needing to change the bounding box labels.
  • and since the outputs are scaled between [0-1] its easier for the the network to predict those scores, instead of absolute values.

You should also experiment,

  • with predefined set of bounding boxes with certain height-aspect ratios, and using the offsets from them as your labels.

You may want to look at how current state of art algorithms do their bounding box logic. An yolo v2 example

Resize image and it's bounding box in x,y,w,h format

As you know the actual (W,H) and new image format (newW,newH) then

newX = (newW/W) * x

and do the same for other value...

Edit:

newY = (newH/H) * y

And the same for other value.

How to re scale information of an image coordinates to work on a scaled version of similar image

Your re-scaling process doesn't take into account the zero padded area on the top. Remove the top zero pads before multiplying with the scale ratio and you should be able to get the proper result.

Here is an example code for all 3 cases where bounding box is the points that corresponds to the result from YOLO.

def boundBox_restore(boundingbox, ori_size=(ori_image_width,ori_image_height), resized_size=(resized_image_size,resized_image_size)):

h, w = ori_size
sh, sw = resized_size

scale_ratio = w / sw

ox,oy,ow,oh = boundingbox

# aspect ratio of image
aspect = w/h # if on Python 2, you might need to cast as a float: float(w)/h

# compute scaling and pad sizing
if aspect > 1: # horizontal image
new_w = sw
new_h = np.round(new_w/aspect).astype(int)
pad_vert = (sh-new_h)/2
pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
pad_left, pad_right = 0, 0
elif aspect < 1: # vertical image
new_h = sh
new_w = np.round(new_h*aspect).astype(int)
pad_horz = (sw-new_w)/2
pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
pad_top, pad_bot = 0, 0
else: # square image
new_h, new_w = sh, sw
pad_left, pad_right, pad_top, pad_bot = 0, 0, 0, 0


# remove pad
ox = ox - pad_left
oy = oy - pad_top

# rescale
ox = ox * scale_ratio
oy = oy * scale_ratio
ow = ow * scale_ratio
oh = oh * scale_ratio


return (ox,oy,oh,ow)


Related Topics



Leave a reply



Submit