Resampling a numpy array representing an image
Based on your description, you want scipy.ndimage.zoom
.
Bilinear interpolation would be order=1
, nearest is order=0
, and cubic is the default (order=3
).
zoom
is specifically for regularly-gridded data that you want to resample to a new resolution.
As a quick example:
import numpy as np
import scipy.ndimage
x = np.arange(9).reshape(3,3)
print 'Original array:'
print x
print 'Resampled by a factor of 2 with nearest interpolation:'
print scipy.ndimage.zoom(x, 2, order=0)
print 'Resampled by a factor of 2 with bilinear interpolation:'
print scipy.ndimage.zoom(x, 2, order=1)
print 'Resampled by a factor of 2 with cubic interpolation:'
print scipy.ndimage.zoom(x, 2, order=3)
And the result:
Original array:
[[0 1 2]
[3 4 5]
[6 7 8]]
Resampled by a factor of 2 with nearest interpolation:
[[0 0 1 1 2 2]
[0 0 1 1 2 2]
[3 3 4 4 5 5]
[3 3 4 4 5 5]
[6 6 7 7 8 8]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with bilinear interpolation:
[[0 0 1 1 2 2]
[1 2 2 2 3 3]
[2 3 3 4 4 4]
[4 4 4 5 5 6]
[5 5 6 6 6 7]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with cubic interpolation:
[[0 0 1 1 2 2]
[1 1 1 2 2 3]
[2 2 3 3 4 4]
[4 4 5 5 6 6]
[5 6 6 7 7 7]
[6 6 7 7 8 8]]
Edit: As Matt S. pointed out, there are a couple of caveats for zooming multi-band images. I'm copying the portion below almost verbatim from one of my earlier answers:
Zooming also works for 3D (and nD) arrays. However, be aware that if you zoom by 2x, for example, you'll zoom along all axes.
data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape
This yields:
Original:
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
[[ 9 10 11]
[12 13 14]
[15 16 17]]
[[18 19 20]
[21 22 23]
[24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)
In the case of multi-band images, you usually don't want to interpolate along the "z" axis, creating new bands.
If you have something like a 3-band, RGB image that you'd like to zoom, you can do this by specifying a sequence of tuples as the zoom factor:
print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))
This yields:
Zoomed by 2x along the last two axes:
[[[ 0 0 1 1 2 2]
[ 1 1 1 2 2 3]
[ 2 2 3 3 4 4]
[ 4 4 5 5 6 6]
[ 5 6 6 7 7 7]
[ 6 6 7 7 8 8]]
[[ 9 9 10 10 11 11]
[10 10 10 11 11 12]
[11 11 12 12 13 13]
[13 13 14 14 15 15]
[14 15 15 16 16 16]
[15 15 16 16 17 17]]
[[18 18 19 19 20 20]
[19 19 19 20 20 21]
[20 20 21 21 22 22]
[22 22 23 23 24 24]
[23 24 24 25 25 25]
[24 24 25 25 26 26]]]
Numpy Resize/Rescale Image
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 the
INTER_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.
Resample and resize numpy array
Instead of passing a single number to the zoom
parameter, give a sequence:
scipy.ndimage.zoom(x, zoom=(1.5, 2.), order=1)
#array([[0, 0, 1, 1, 2, 2],
# [2, 2, 3, 3, 4, 4],
# [4, 4, 5, 5, 6, 6],
# [6, 6, 7, 7, 8, 8]])
With the sequences (2., 2.75)
and (2., 3.5)
you will get output arrays with shapes (6, 8)
and (6, 10)
, respectively.
numpy: Resize 3d array with interpolation in 2d
I think you can do what you want with scipy.ndimage.map_coordinates
:
import numpy as np
import scipy.ndimage.interpolation
def resize_batch(image_batch, new_width, new_height):
image_batch = np.asarray(image_batch)
shape = list(image_batch.shape)
shape[1] = new_width
shape[2] = new_height
ind = np.indices(shape, dtype=float)
ind[1] *= (image_batch.shape[1] - 1) / float(new_width - 1)
ind[2] *= (image_batch.shape[2] - 1) / float(new_height - 1)
return scipy.ndimage.interpolation.map_coordinates(image_batch, ind, order=1)
print(resize_batch(np.zeros([10, 20, 30]), 60, 15).shape)
# (10, 60, 15)
print(resize_batch(np.zeros([10, 20, 30, 3]), 60, 15).shape)
# (10, 60, 15, 3)
EDIT:
Here are a couple of other versions. This one uses only NumPy operations without SciPy, computing bilinear interpolation "by hand":
import numpy as np
def resize_batch_np(image_batch, new_width, new_height):
dtype = image_batch.dtype
n, width, height = image_batch.shape[:3]
extra_dims = image_batch.ndim - 3
w = np.linspace(0, width - 1, new_width, dtype=dtype)[:, np.newaxis]
h = np.linspace(0, height - 1, new_height, dtype=dtype)
nn = np.arange(n, dtype=np.int32)[:, np.newaxis, np.newaxis]
ii_1 = w.astype(np.int32)
ii_2 = (ii_1 + 1).clip(max=width - 1)
w_alpha = w - ii_1
w_alpha = w_alpha.reshape(w_alpha.shape + (1,) * extra_dims)
w_alpha_1 = 1 - (w_alpha)
jj_1 = h.astype(np.int32)
jj_2 = (jj_1 + 1).clip(max=height - 1)
h_alpha = h - jj_1
h_alpha = h_alpha.reshape(h_alpha.shape + (1,) * extra_dims)
h_alpha_1 = 1 - (h_alpha)
out_11 = image_batch[nn, ii_1, jj_1]
out_12 = image_batch[nn, ii_1, jj_2]
out_21 = image_batch[nn, ii_2, jj_1]
out_22 = image_batch[nn, ii_2, jj_2]
return ((out_11 * h_alpha_1 + out_12 * h_alpha) * w_alpha_1 +
(out_21 * h_alpha_1 + out_22 * h_alpha) * w_alpha)
And this other one does the same but with Numba:
import numpy as np
import numba as nb
@nb.njit(parallel=True)
def resize_batch_nb(image_batch, new_width, new_height):
dtype = image_batch.dtype
n, width, height = image_batch.shape[:3]
extra_dims = image_batch.ndim - 3
w = np.empty(new_width, dtype=dtype)
for i in range(new_width):
w[i] = (width - 1) * i / (new_width - 1)
h = np.empty(new_height, dtype=dtype)
for i in range(new_height):
h[i] = (height - 1) * i / (new_height - 1)
ii_1 = w.astype(np.int32)
ii_2 = np.minimum(ii_1 + 1, width - 1)
w_alpha = w - ii_1
w_alpha_1 = 1 - (w_alpha)
jj_1 = h.astype(np.int32)
jj_2 = np.minimum(jj_1 + 1, height - 1)
h_alpha = h - jj_1
h_alpha_1 = 1 - (h_alpha)
out = np.empty((n, new_width, new_height) + image_batch.shape[3:], dtype=dtype)
for idx in nb.prange(n):
for i in nb.prange(new_width):
for j in nb.prange(new_height):
out_11 = image_batch[idx, ii_1[i], jj_1[j]]
out_12 = image_batch[idx, ii_1[i], jj_2[j]]
out_21 = image_batch[idx, ii_2[i], jj_1[j]]
out_22 = image_batch[idx, ii_2[i], jj_2[j]]
out_1 = out_11 * h_alpha_1[j] + out_12 * h_alpha[j]
out_2 = out_21 * h_alpha_1[j] + out_22 * h_alpha[j]
out[idx, i, j] = out_1 * w_alpha_1[i] + out_2 * w_alpha[i]
return out
The result is the same as before:
import numpy as np
np.random.seed(100)
image_batch = np.random.rand(100, 200, 300, 3).astype(float)
new_width = 60
new_height = 80
out = resize_batch(image_batch, new_width, new_height)
out_np = resize_batch_np(image_batch, new_width, new_height)
out_nb = resize_batch_nb(image_batch, new_width, new_height)
print(np.allclose(out, out_np))
# True
print(np.allclose(out, out_nb))
# True
But the performance improves significantly:
%timeit resize_batch(image_batch, new_width, new_height)
# 211 ms ± 9.36 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit resize_batch_np(image_batch, new_width, new_height)
# 106 ms ± 1.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit resize_batch_nb(image_batch, new_width, new_height)
# 48.3 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Is there a better way to resize an image that is in the form of a numpy array?
To avoid having to convert between tf.tensor
and np.ndarray
tensor data types, you can use skimage
(scikit-image) to resize the image directly on np.ndarray
objects using the following code segment:
import skimage.transform
kwargs = dict(output_shape=self._size, mode='edge', order=1, preserve_range=True)
im = skimage.transform.resize(im, **kwargs).astype(im.dtype)
To install skimage
, follow the installation instructions here: https://pypi.org/project/scikit-image/. Hope this helps!
Use cv2.imshow() to display an np.array
opencv has BGR channel ordering, PIL and matplotlib use RGB order
try not to mix different libraries with different paradigms
Numpy Upsample np.array / increase size of np.array by adding mean value of cosecuitive elements
np.interp
:
a = np.array([1,2,3,4,5])
ur = 2 # upsample rate
np.interp(np.arange((len(a)-1)*ur+1)/ur, xp=np.arange(len(a)), fp=a)
# output: array([1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5.])
Related Topics
Split an Integer into Digits to Compute an Isbn Checksum
What Is an 'Endpoint' in Flask
How to Redirect Stderr in Python
Python Method Name with Double-Underscore Is Overridden
Python: Nameerror: Global Name 'Foobar' Is Not Defined
How to Install Pip for Python 2.6
Same Output in Different Workers in Multiprocessing
Get Class Labels from Keras Functional Model
I am Sending Commands Through Serial Port in Python But They Are Sent Multiple Times Instead of One
Reading Data from a CSV File in Python
Smtplib Sends Blank Message If the Message Contain Certain Characters
How to Get a List of Column Names in SQLite
How to Get All of the Output from My .Exe Using Subprocess and Popen