Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pad a numpy array with random values within a given range?

I want to be able to border an array (really, an image file) with a set of random values. The numpy.pad() function does not have any mode to do this. Is there a shorthand way to achieve this or would I have to create a function from scratch?

like image 548
Nick Nagy Avatar asked Sep 26 '26 12:09

Nick Nagy


2 Answers

I think you need to create a padding function yourself to pass to np.pad. This one pads with random integers.

def random_pad(vec, pad_width, *_, **__):
    vec[:pad_width[0]] = np.random.randint(20, 30, size=pad_width[0])
    vec[vec.size-pad_width[1]:] = np.random.randint(30,40, size=pad_width[1])

You can use this with np.pad like this:

In [13]: img = np.arange(12).reshape(3, 4)

In [14]: img
Out[14]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [15]: np.pad(img, ((2,3), (1,4)), mode=random_pad)
Out[15]: 
array([[26, 21, 22, 24, 21, 37, 37, 37, 39],
       [26, 25, 23, 29, 20, 39, 38, 30, 31],
       [26,  0,  1,  2,  3, 37, 31, 32, 36],
       [29,  4,  5,  6,  7, 30, 32, 33, 37],
       [24,  8,  9, 10, 11, 33, 34, 33, 37],
       [26, 36, 36, 36, 30, 32, 36, 38, 31],
       [29, 33, 34, 38, 35, 31, 33, 37, 33],
       [23, 37, 33, 33, 34, 32, 37, 33, 35]])
like image 188
kuppern87 Avatar answered Sep 29 '26 01:09

kuppern87


It may not be the most space-efficient, but one way to do this is to create a new array and place your existing array in the center of that.

>>> import numpy as np
>>> np.random.seed(444)
>>> arr = np.zeros((4, 5))  # your image array
>>> newsize = tuple(i + 2 for i in arr.shape)
>>> new = np.random.randint(low=0, high=50, size=newsize)
>>> new[1:-1, 1:-1] = arr
>>> new
array([[ 3, 48, 23,  8,  3, 39, 12],
       [47,  0,  0,  0,  0,  0, 15],
       [34,  0,  0,  0,  0,  0,  0],
       [ 6,  0,  0,  0,  0,  0,  6],
       [39,  0,  0,  0,  0,  0, 13],
       [ 2, 15,  9, 34,  9, 24, 25]])

This is assuming your image is grayscale (2d) rather than a 3-dimensional MxNx4 RGBA array. In that case, you'd want new[1:-1, 1:-1, 1:-1].

You can also do this by passing a callable to np.pad(), but there is a caveat (see below):

from functools import partial

def _pad_random(vector, pad_width, iaxis, kwargs, low, high):
    a, b = np.random.randint(low, high, size=2)
    vector[:pad_width[0]] = a
    vector[-pad_width[1]:] = b
    return vector

pad_random = partial(_pad_random, low=0, high=50)

Usage:

>>> np.pad(arr, 1, pad_random)
array([[23., 19.,  6., 47., 17.,  7., 26.],
       [26.,  0.,  0.,  0.,  0.,  0., 37.],
       [39.,  0.,  0.,  0.,  0.,  0., 39.],
       [39.,  0.,  0.,  0.,  0.,  0., 42.],
       [28.,  0.,  0.,  0.,  0.,  0., 47.],
       [11., 32., 37.,  2., 38., 30., 44.]])

Caveat: it looks like, when you pass a function to the mode parameter of np.pad(), this function is called multiple times. Here's an example straight from the docs with a few print calls sprinkled in:

>>> def pad_with(vector, pad_width, iaxis, kwargs):
...     pad_value = kwargs.get('padder', 10)
...     print(vector[:pad_width[0]])
...     print(vector[-pad_width[1]:])
...     vector[:pad_width[0]] = pad_value
...     vector[-pad_width[1]:] = pad_value
...     return vector
...
>>> arr = np.arange(6).reshape(3, 2)

>>> np.pad(arr, 1, pad_with)
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[0]
[10]
[10]
[0]
[0]
[0]
[0]
[0]
[0]
[10]
[10]
array([[10, 10, 10, 10],
       [10,  0,  1, 10],
       [10,  2,  3, 10],
       [10,  4,  5, 10],
       [10, 10, 10, 10]])

So, as long as the images aren't too big, it may be much more time efficient to use the first approach above.

like image 21
Brad Solomon Avatar answered Sep 29 '26 03:09

Brad Solomon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!