Home > Software design >  Image Zoom Using SciPy - wrong dimensions
Image Zoom Using SciPy - wrong dimensions

Time:11-21

The code from answer to this question produces an error for some values of zoom factor.

As mentioned in comments by @kg_sYy, "The rounding in int(np.round(h * zoom_factor)) seems to sometimes cause the resulting image to be 1 pixel smaller than target. The calculation then gets -1 as diff and you get image pixel size 1 for output. Changing to np.ceil() instead of np.round() seems to fix it." However, changing to np.ceil() does not fix the error.

As an example, zoom_factor = 1.1317 and image shape (331, 331, 3) result in zoomed_img being one pixel in size.

What other rounding function could fix the problem and why does it occur?

CodePudding user response:

Why it becomes a single pixel

The code you have linked in question makes the assumption that:

out might still be slightly larger than img due to rounding, so trim off any extra pixels at the edges

And then proceeds to do trimming without checking anything. The zoomed image becomes 1 pixel whenever out is actually smaller than img (due to bad rounding, as the comment mentions). Then these following calculations fail:

trim_top = ((out.shape[0] - h) // 2)
trim_left = ((out.shape[1] - w) // 2)

Since the new image is actually smaller than (h, w), the trim_top and trim_left are actually negative numbers (-1, empirically).

So the final trim is actually:

out = out[-1: h - 1, -1: w - 1]

This is nothing but the bottom right pixel at [-1, -1].

How to fix

  • Ensure that out is equal or greater than img. Using ceil() should handle this. (See if you can find any image size and zoom factor for which out is still smaller than img.)
  • Check whether out is actually greater than img before trying to trim it.
  • Handle a possible round off when doing the trim.
def clipped_zoom(img, zoom_factor, **kwargs):
    h, w = img.shape[:2]

    zoom_tuple = (zoom_factor,) * 2   (1,) * (img.ndim - 2)

    # Zooming out
    if zoom_factor < 1:

        # Bounding box of the zoomed-out image within the output array
        zh = int(np.round(h * zoom_factor))
        zw = int(np.round(w * zoom_factor))
        top = (h - zh) // 2
        left = (w - zw) // 2

        # Zero-padding
        out = np.zeros_like(img)
        out[top:top zh, left:left zw] = zoom(img, zoom_tuple, **kwargs)

    # Zooming in
    elif zoom_factor > 1:

        # Bounding box of the zoomed-in region within the input array
        zh = int(np.ceil(h / zoom_factor))
        zw = int(np.ceil(w / zoom_factor))

        top = (h - zh) // 2
        left = (w - zw) // 2

        out = zoom(img[top: top   zh, left: left   zw], zoom_tuple, **kwargs)

        # >>> Check out against img before trying to trim
        # >>> trim safely, accounting for rounding

        if out.shape[0] > h:
            h_diff = out.shape[0] - h
            trim_top = h_diff // 2
            trim_bottom = h_diff - trim_top
            out = out[trim_top: out.shape[0] - trim_bottom, :]

        if out.shape[1] > w:
            w_diff = out.shape[1] - w
            trim_left = w_diff // 2
            trim_right = w_diff - trim_left
            out = out[:, trim_left: out.shape[1] - trim_right]     

    # If zoom_factor == 1, just return the input array
    else:
        out = img
    return out
  • Related