Numpy Resize/Rescale Image

303,687

Solution 1

Yeah, you can install opencv (this is a library used for image processing, and computer vision), and use the cv2.resize function. And for instance use:

import cv2
import numpy as np

img = cv2.imread('your_image.jpg')
res = cv2.resize(img, dsize=(54, 140), interpolation=cv2.INTER_CUBIC)

Here img is thus a numpy array containing the original image, whereas res is a numpy array containing the resized image. An important aspect is the interpolation parameter: there are several ways how to resize an image. Especially since you scale down the image, and the size of the original image is not a multiple of the size of the resized image. Possible interpolation schemas are:

  • INTER_NEAREST - a nearest-neighbor interpolation
  • INTER_LINEAR - a bilinear interpolation (used by default)
  • INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
  • INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
  • INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood

Like with most options, there is no "best" option in the sense that for every resize schema, there are scenarios where one strategy can be preferred over another.

Solution 2

While it might be possible to use numpy alone to do this, the operation is not built-in. That said, you can use scikit-image (which is built on numpy) to do this kind of image manipulation.

Scikit-Image rescaling documentation is here.

For example, you could do the following with your image:

from skimage.transform import resize
bottle_resized = resize(bottle, (140, 54))

This will take care of things like interpolation, anti-aliasing, etc. for you.

Solution 3

For people coming here from Google looking for a fast way to downsample images in numpy arrays for use in Machine Learning applications, here's a super fast method (adapted from here ). This method only works when the input dimensions are a multiple of the output dimensions.

The following examples downsample from 128x128 to 64x64 (this can be easily changed).

Channels last ordering

# large image is shape (128, 128, 3)
# small image is shape (64, 64, 3)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((output_size, bin_size, 
                                   output_size, bin_size, 3)).max(3).max(1)

Channels first ordering

# large image is shape (3, 128, 128)
# small image is shape (3, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((3, output_size, bin_size, 
                                      output_size, bin_size)).max(4).max(2)

For grayscale images just change the 3 to a 1 like this:

Channels first ordering

# large image is shape (1, 128, 128)
# small image is shape (1, 64, 64)
input_size = 128
output_size = 64
bin_size = input_size // output_size
small_image = large_image.reshape((1, output_size, bin_size,
                                      output_size, bin_size)).max(4).max(2)

This method uses the equivalent of max pooling. It's the fastest way to do this that I've found.

Solution 4

One-line numpy solution for downsampling (by 2):

smaller_img = bigger_img[::2, ::2]

And upsampling (by 2):

bigger_img = smaller_img.repeat(2, axis=0).repeat(2, axis=1)

(this asssumes HxWxC shaped image. h/t to L. Kärkkäinen in the comments above. note this method only allows whole integer resizing (e.g., 2x but not 1.5x))

Solution 5

If anyone came here looking for a simple method to scale/resize an image in Python, without using additional libraries, here's a very simple image resize function:

#simple image scaling to (nR x nC) size
def scale(im, nR, nC):
  nR0 = len(im)     # source number of rows 
  nC0 = len(im[0])  # source number of columns 
  return [[ im[int(nR0 * r / nR)][int(nC0 * c / nC)]  
             for c in range(nC)] for r in range(nR)]

Example usage: resizing a (30 x 30) image to (100 x 200):

import matplotlib.pyplot as plt

def sqr(x):
  return x*x

def f(r, c, nR, nC):
  return 1.0 if sqr(c - nC/2) + sqr(r - nR/2) < sqr(nC/4) else 0.0

# a red circle on a canvas of size (nR x nC)
def circ(nR, nC):
  return [[ [f(r, c, nR, nC), 0, 0] 
             for c in range(nC)] for r in range(nR)]

plt.imshow(scale(circ(30, 30), 100, 200))

Output: scaled image

This works to shrink/scale images, and works fine with numpy arrays.

Share:
303,687

Related videos on Youtube

Brian Hamill
Author by

Brian Hamill

Updated on September 13, 2021

Comments

  • Brian Hamill
    Brian Hamill over 2 years

    I would like to take an image and change the scale of the image, while it is a numpy array.

    For example I have this image of a coca-cola bottle: bottle-1

    Which translates to a numpy array of shape (528, 203, 3) and I want to resize that to say the size of this second image: bottle-2

    Which has a shape of (140, 54, 3).

    How do I change the size of the image to a certain shape while still maintaining the original image? Other answers suggest stripping every other or third row out, but what I want to do is basically shrink the image how you would via an image editor but in python code. Are there any libraries to do this in numpy/SciPy?

    • ShpielMeister
      ShpielMeister over 6 years
      can you show code for your numpy array?
    • sascha
      sascha over 6 years
    • Paul Panzer
      Paul Panzer over 6 years
      @sascha Deprecated, according to the page you linked.
    • Brian Hamill
      Brian Hamill over 6 years
      @ShpielMeister I cannot get IntelliJ to print out the numpy array fully, for some reason when outputs are large it puts ... in all the time, so I can only see part of the array output in the console
  • Brian Hamill
    Brian Hamill over 6 years
    I've just tried out this code and it works! Just one change is that dsize should be dsize=(54, 140) as it takes x then y, where as a numpy array shows shape as y then x (y is number of rows and x is number of columns)
  • Brian Hamill
    Brian Hamill over 6 years
    Thank you! This answer also works! Although I'm getting some issue with the anti_aliasing flag, it looks like it has been removed from the most recent version of 0.13.1
  • MiniQuark
    MiniQuark over 5 years
    Unfortunately, imresize() is deprecated, it will be removed in SciPy 1.3.0
  • sziraqui
    sziraqui about 5 years
    This returns image as float ndarray even if your original image is uint8
  • NOhs
    NOhs about 5 years
    Welcome to StackOverflow. Great that you want to help others by answering their questions. However, I do not see how your answer adds value compared to the existing answer that already uses cv2 and uses a proper resize function instead of reimplementing a "sub-optimal" resize function that does worse than nearest neighbour interpolation.
  • Darth Egregious
    Darth Egregious about 5 years
    This is a nice technique because it works with any number of channels. I tried this with rgb data combined with a depth point cloud data and it preserved the relationship like I wanted.
  • Eduardo Pignatelli
    Eduardo Pignatelli about 5 years
    I try to avoid cv2, it swaps dimensions and loads in BGR channel format. I prefer skimage.io.imread('image.jpg') and skimage.transform.resize(img). scikit-image.org/docs/dev/install.html
  • Tronic
    Tronic about 5 years
    large_image[:, ::2, ::2] returns the image with resolution halved.
  • Waylon Flinn
    Waylon Flinn about 5 years
    @LasseKärkkäinen but it doesn't downsample, it merely selects every other pixel. The difference is that the final function 'max' can be changed to select or compute pixels in slightly better ways (using 'min' or 'mean' for instance). Your method is useful (and faster), if that doesn't matter.
  • Decker
    Decker almost 5 years
    @EduardoPignatelli I avoid skimage.transform.resize because you don't have control over the interpolation algorithm it uses. But, that may not be important, depending on people's use cases.
  • Tapio
    Tapio over 4 years
    @Decker skimage.transform.resize provides some control via the 'order'-parameter. order=0 is nearest neighbour, 1=bi-linear, 2=bi-quadratic, 3=bi-cubic etc. No area mean or lanczos interpolation however.
  • Decker
    Decker over 4 years
    @TapioFriberg ahh yes, I stand corrected; I see the algorithms defined under the documentation for skimage.transform.warp's 'order' parameter. At some point it might be helpful to update the docs to include references for the types, "Bi-quartic", for example, isn't defined anywhere else in the documentation, (as of Dec 10 2019) - a one-liner might be beneficial to future users.
  • Deshwal
    Deshwal over 4 years
    @Decker won't it affect the image when the swapping is done from RGB to BGR?
  • Deshwal
    Deshwal over 4 years
    @DarthEgregious, jakevdp -> it made my random noise data into single color when I resized (137,236,3) array to (64,64) like the method you've described. Is this normal because it looks like it has lost all the information?
  • Darth Egregious
    Darth Egregious over 4 years
    Shouldn't it be (64,64,3)
  • rayzinnz
    rayzinnz about 4 years
    @L.Kärkkäinen what is the opposite of this to double resolution?
  • Tronic
    Tronic about 4 years
    @rayzinnz np.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
  • random_dsp_guy
    random_dsp_guy almost 4 years
    May not work based on this answer: stackoverflow.com/questions/37872171/…
  • HockeyStick
    HockeyStick over 3 years
    Could replacing .max(4).max(2) with .mean(4).mean(2) work as a fast method for downsampling with linear interpolation?
  • Waylon Flinn
    Waylon Flinn over 3 years
    @HockeyStick it should, but I remember it being significantly slower in my tests.
  • Porter Child
    Porter Child about 3 years
    I think the nested list comprehensions are hurting readability
  • darda
    darda about 3 years
    The question specifically states the image is a numpy array; you can't use Pillow on that.
  • Safi
    Safi over 2 years
    @sziraqui preserve_range=True can preserve the range skimage.transform.resize(..., , preserve_range=True)
  • Jiadong
    Jiadong over 2 years
    Personally, always not recommended using OpenCV...
  • Patrice Carbonneau
    Patrice Carbonneau over 2 years
    Great solutions to manipulate segmentation label masks where you really don't need/want the conversion to float64 that happens under the hood in skimage.
  • flawr
    flawr about 2 years
    As an alternative to repeat you an also use np.kron(..., np.ones((2,2,1))) (for a HxWxC image), but I'm not sure which one is faster.