2

I am working on a program in python that makes use of a function very similar to the addWeighted function in openCV. The difference is that it doesn't actually add the numpy arrays representing the images, instead, it takes whichever pixel is brighter at any particular coordinate and uses that value.

What I have been finding, however, is that despite the fact that these functions do very similar things, the addWeighted function is much faster. So my question is, how can I modify my current solution to be equally as fast? Is there a way I can use the multiprocessing module, or something similar?

Here is the code:

image = np.zeros(image_1.shape)
for row_index, row in enumerate(image_1):
     for col_index, col in enumerate(row):
          pixel_1 = image_1[row_index, col_index]
          pixel_2 = image_2[row_index, col_index]
          sum_1 = int(pixel_1[0]) + int(pixel_1[1]) + int(pixel_1[2])
          sum_2 = int(pixel_2[0]) + int(pixel_2[1]) + int(pixel_2[2])

          if sum_2 > sum_1:
               image[row_index, col_index] = pixel_2
          else:
               image[row_index, col_index] = pixel_1

Where image_1 and image_2 are both numpy arrays representing images, both with the same shape (720, 1280, 3).

Victor Odouard
  • 1,345
  • 3
  • 15
  • 28
  • Moral of the story: don't loop over pixels when you have numpy unless there is no other choice. There is almost always a faster method. Loops are slow in Python compared to a language like C. Numpy is built in C and Fortran so it will internally use those snappy operations while the standard Python loops will be substantially slower. – alkasm Jul 17 '17 at 16:46

1 Answers1

2

One vectorized approach would be -

mask = image_2.astype(int).sum(-1) > image_1.astype(int).sum(-1)
out = np.where(mask[...,None], image_2, image_1)

Steps :

  • Convert to int dtypes, sum along the last axis and perform element-wise comparisons. This would give us a mask.

  • Use np.where with this mask, extended to the same no. of dims as input arrays to do the choosing. This employs the concept of NumPy broadcasting to do the choosing in a vectorized manner. So, that's worth a good look.

Note: Alternatively, we can also use keepdims=True to keep the no. of dims while summing and thus avoid extending dims in the next step.

Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Wow, I tested that and it works quite well! The only thing is, I'm not sure I totally understand the code. What concepts are you using? Is there anywhere that I can read about them? – Victor Odouard Jul 17 '17 at 05:47
  • 1
    Your computer is very good at doing operations on multiple data points, like a numpy array. It is much more efficient to do this rather than loop through every data point in the numpy array. That is basically what @Divakar did here beautifully. – Ofer Sadan Jul 17 '17 at 05:50
  • @user2457666 It uses `broadcasting`, that I missed out on mentioning earlier. Added link. – Divakar Jul 17 '17 at 05:51
  • Thanks! I looked into it and it's starting to come together in my head. My last question is what does the `[...,None]` part do and why is it necessary? – Victor Odouard Jul 17 '17 at 06:52
  • @user2457666 This might help, specially the last section - https://stackoverflow.com/a/40383002/ – Divakar Jul 17 '17 at 07:38