Eshaka
Eshaka

Reputation: 994

How to deal with nparrays faster

I have two images loaded in to two numpy array. I want to get the difference of them and remove the values less than 50 and set the rest of it to 255 with a final result of back and white image.

def count(base, image):
    x, y, z = base.shape
    sheet = np.zeros(base.shape)
    for i in range(x):
        for j in range(y):
            temp = 0
            for k in range(z):
                if base[i, j, k] > image[i, j, k]:
                    t = base[i, j, k] - image[i, j, k]
                    if t > 50:
                        temp = 255
                else:
                    t = image[i, j, k] - base[i, j, k]
                    if t > 50:
                        temp = 255
            sheet[i, j] = [temp, temp, temp]

    array = sheet[:, :, 0]

this code does what i need it to do. but as you see i have used the most simplest for loop for this function and with the images being in the size of 2000*2000, it takes a long time to process. i need a way to rewrite this in a faster way.

thank you

Upvotes: 0

Views: 65

Answers (2)

Paul Panzer
Paul Panzer

Reputation: 53119

Vectorizing your code looks straight-forward, except for one gotcha: Your data appear to be unsigned ints (uint8 by the look of it) which require a bit of extra attention since they frequently underflow with unexpected results. For example, the obvious np.abs(image-base)>50 to detect differences greater than 50 does not work, in fact np.abs is a nop on unsigned data. A careful translation would look more like

sheet = np.array([[0,0,0],[255,255,255]], 'u1')[((np.maximum(base, image)-np.minimum(base, image))>50).any(2).view('u1')]

or

sheet = np.array([[0,0,0],[255,255,255]], 'u1')[(np.abs(np.subtract(image, base, dtype='i2'))>50).any(2).view('u1')]

This

  1. correctly computes the subpixel wise difference,

    • the first version mimics your if/else clause

    • the second forces a signed result type 'i2' or int16 for the difference

  2. detects those greater 50,

  3. marks pixels with at least one such subpixel (any(2)),

  4. converts the resulting boolean mask into indices (.view('u1')) 0 and 1

  5. and uses those to index into a template array.

Upvotes: 3

Francisco Rivera
Francisco Rivera

Reputation: 119

Most of the operations work just as they would on scalars on NumPy arrays of the same size. I re-wrote the function as,

def count_new(base, image, thresh=50):
    # these are the pixel absolute value differences
    differences = np.abs(base - image)

    # whether the difference exceeds the threshold
    big_difference = differences > thresh

    # whether each pixel has at least one big difference
    big_difference = big_difference.any(axis=2)

    # return 255 where big differences, and 0 everywhere else
    return 255 * big_difference.astype(int)

Hopefully the comments make the intent of each line clear. Also checked this gives the same as the previous out-put

x = np.random.randint(256, size=(10, 11, 3))
y = np.random.randint(256, size=(10, 11, 3))
assert((count(x,y) == count_new(x,y)).all())

which shows it does.

Upvotes: 1

Related Questions