kai06046
kai06046

Reputation: 292

Efficiently creating a checkerboard pattern of 0s and 255s

I had wrote a function to generate a example numpy array by for loop. But it is too slow for larger dimension.

import numpy as np
mat = np.zeros((5, 5))

def func(im):
    im = im.copy()
    h,w = im.shape
    for i in range(h):
        for j in range(w):
            if (i + j) % 2 == 0:
                im[i, j] = 255
    return im
print(func(mat))
#    array([[ 255.,    0.,  255., ...,  255.,    0.,  255.],
#        [   0.,  255.,    0., ...,    0.,  255.,    0.],
#        [ 255.,    0.,  255., ...,  255.,    0.,  255.],
#        ..., 
#        [ 255.,    0.,  255., ...,  255.,    0.,  255.],
#        [   0.,  255.,    0., ...,    0.,  255.,    0.],
#        [ 255.,    0.,  255., ...,  255.,    0.,  255.]])

Upvotes: 1

Views: 1789

Answers (3)

Divakar
Divakar

Reputation: 221674

We will start with a zero intialized array. So, that will be : np.zeros((heigh, width)). Next up, we would have few approaches to fill it with such a pattern of alternating 255s.

Approach #1 : Use open range arrays with np.ogrid to simulate those iterators and directly translate the comparisons in a vectorized manner leveraging broadcasting -

I,J = np.ogrid[:h,:w]
im[(I+J)%2==0] = 255

Approach #2 : Taking a closer look, it seem you are setting every other element in each row to 255, starting from the first element for first row and second element for the second row and back to first for the third row and so on. Thus, we can use slicing as well and should be pretty efficient -

im[::2,::2] = 255
im[1::2,1::2] = 255

Runtime test

Approaches -

def func(im):
    h,w = im.shape
    for i in range(h):
        for j in range(w):
            if (i + j) % 2 == 0:
                im[i, j] = 255
    return im

def app1(im):
    h,w = im.shape    
    I,J = np.ogrid[:h,:w]
    im[(I+J)%2==0] = 255
    return im

def app2(im):
    im[::2,::2] = 255
    im[1::2,1::2] = 255
    return im

Verify -

In [74]: im = np.random.randint(0,255,(1000,1000))

In [75]: im1 = im.copy()
    ...: im2 = im.copy()
    ...: im3 = im.copy()
    ...: 

In [76]: func(im1)
    ...: app1(im2)
    ...: app2(im3)
    ...: 
Out[76]: 
array([[255, 133, 255, ...,  14, 255,  41],
       [235, 255, 191, ..., 255,  40, 255],
       [255, 151, 255, ...,  51, 255,  18],
       ..., 
       [ 50, 255, 177, ..., 255, 193, 255],
       [255, 245, 255, ..., 114, 255,  27],
       [223, 255, 148, ..., 255, 200, 255]])

In [77]: print np.allclose(im1,im2)
    ...: print np.allclose(im1,im3)
    ...: 
True
True

Timings -

In [78]: %timeit func(im)
10 loops, best of 3: 106 ms per loop

In [79]: %timeit app1(im)
100 loops, best of 3: 14 ms per loop

In [80]: %timeit app2(im)
1000 loops, best of 3: 415 µs per loop

In [82]: 106/0.415 # Speedup with approach #2 over original one
Out[82]: 255.42168674698797

Upvotes: 4

Christoph Engwer
Christoph Engwer

Reputation: 831

You can use the fancy indexing properties of numpy arrays.

An example that does exactly what you need is:

import numpy as np
arr = np.zeros((10, 10))
a = np.arange(0, 10, 2)
b = np.arange(1, 10, 2)
arr[a[:, None], a[None, :]] = 255
arr[b[:, None], b[None, :]] = 255

print(arr)
array([[ 255.,    0.,  255., ...,    0.,  255.,    0.],
       [   0.,  255.,    0., ...,  255.,    0.,  255.],
       [ 255.,    0.,  255., ...,    0.,  255.,    0.],
       ..., 
       [   0.,  255.,    0., ...,  255.,    0.,  255.],
       [ 255.,    0.,  255., ...,    0.,  255.,    0.],
       [   0.,  255.,    0., ...,  255.,    0.,  255.]])

Upvotes: 2

Nuageux
Nuageux

Reputation: 1686

You can also use tile for repeating patterns such as yours.

import numpy as np
n = 5
x = np.tile(np.array([[0,255],[255,0]]),(n,n))

Upvotes: 1

Related Questions