Numpy Resize/Rescale Image
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 interpolationINTER_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 theINTER_NEAREST
method.INTER_CUBIC
- a bicubic interpolation over 4x4 pixel neighborhoodINTER_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))
This works to shrink/scale images, and works fine with numpy arrays.
Related videos on Youtube
Brian Hamill
Updated on September 13, 2021Comments
-
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-2Which 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 over 6 yearscan you show code for your numpy array?
-
sascha over 6 years
-
Paul Panzer over 6 years@sascha Deprecated, according to the page you linked.
-
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 over 6 yearsI've just tried out this code and it works! Just one change is that
dsize
should bedsize=(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 over 6 yearsThank 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 over 5 yearsUnfortunately, imresize() is deprecated, it will be removed in SciPy 1.3.0
-
sziraqui about 5 yearsThis returns image as float ndarray even if your original image is uint8
-
NOhs about 5 yearsWelcome 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 about 5 yearsThis 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 about 5 yearsI try to avoid cv2, it swaps dimensions and loads in BGR channel format. I prefer
skimage.io.imread('image.jpg')
andskimage.transform.resize(img)
. scikit-image.org/docs/dev/install.html -
Tronic about 5 yearslarge_image[:, ::2, ::2] returns the image with resolution halved.
-
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 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 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 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 over 4 years@Decker won't it affect the image when the swapping is done from RGB to BGR?
-
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 over 4 yearsShouldn't it be (64,64,3)
-
rayzinnz about 4 years@L.Kärkkäinen what is the opposite of this to double resolution?
-
Tronic about 4 years@rayzinnz
np.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
-
random_dsp_guy almost 4 yearsMay not work based on this answer: stackoverflow.com/questions/37872171/…
-
HockeyStick over 3 yearsCould replacing
.max(4).max(2)
with.mean(4).mean(2)
work as a fast method for downsampling with linear interpolation? -
Waylon Flinn over 3 years@HockeyStick it should, but I remember it being significantly slower in my tests.
-
Porter Child about 3 yearsI think the nested list comprehensions are hurting readability
-
darda about 3 yearsThe question specifically states the image is a numpy array; you can't use Pillow on that.
-
Safi over 2 years@sziraqui preserve_range=True can preserve the range skimage.transform.resize(..., , preserve_range=True)
-
Jiadong over 2 yearsPersonally, always not recommended using OpenCV...
-
Patrice Carbonneau over 2 yearsGreat 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 about 2 yearsAs an alternative to repeat you an also use
np.kron(..., np.ones((2,2,1)))
(for aHxWxC
image), but I'm not sure which one is faster.