0

I have a numpy array (of an image), the 3rd dimension is of length 3. An example of my array is below. I am attempting to iterate it so I access/print the last dimension of the array. But each of the techniques below accesses each individual value in the 3d array rather than the whole 3d array.

How can I iterate this numpy array at the 3d array level?

My array:

src = cv2.imread('./myimage.jpg') 
# naive/shortened example of src contents (shape=(1, 3, 3))
[[[117 108  99]
  [115 105  98]
  [ 90  79  75]]]

When iterating my objective is print the following values each iteration:

[117 108 99] # iteration 1
[115 105 98] # iteration 2
[ 90 79 75] # iteration 3

# Attempt 1 to iterate
for index,value in np.ndenumerate(src):
    print(src[index]) # src[index] and value = 117 when I was hoping it equals [117 108  99]

# Attempt 2 to iterate
for index,value in enumerate(src):
    print(src[index]) # value = is the entire row
Sanil Khurana
  • 1,129
  • 9
  • 20
sazr
  • 24,984
  • 66
  • 194
  • 362
  • Something like this? https://stackoverflow.com/q/16468717/3923163 – cwalvoort Oct 15 '19 at 01:50
  • Does your entire `src` array need to be nested for any particular reason? Because something similar like your 2nd try would work if you just do one level of flattening on `src`. Ex. `for index,value in enumerate(src[0]): print(index, ':', value)` – Jethro Cao Oct 15 '19 at 02:31
  • What are you trying to do when you're iterating? In numpy there is generally a better solution than an explicit for loop. – AMC Oct 15 '19 at 02:51
  • @AlexanderCécile my objective is to remove all 3d tuples (colours) whose value does not fall between an upper and lower threshold (ie, remove outliers). Note I don't want to null or blank out elements that don't meet this criteria, I want to remove them. If I were to use something like `cv2.inRange(src, lower_thresh, upper_thresh)` it would work but make all those who don't meet the criteria `(0,0,0)` (black) when I want that element removed. – sazr Oct 15 '19 at 03:11
  • That's useful information! Could you update your post to include this, plus the thresholds themselves? – AMC Oct 15 '19 at 03:16

2 Answers2

0

Solution

You could use any of the following two methods. However, Method-2 is more robust and the justification for that has been shown in the section: Detailed Solution below.

import numpy as np

src = [[117, 108,  99], [115, 105,  98], [ 90,  79,  75]]
src = np.array(src).reshape((1,3,3))

Method-1

for row in src[0,:]:
    print(row)

Method-2

Robust method.

for e in np.transpose(src, [2,0,1]):
    print(e)

Output:

[117 108  99]
[115 105  98]
[90 79 75]

Detailed Solution

Let us make an array of shape (3,4,5). So, if we iterate over the 3rd dimension, we should find 5 items, each with a shape of (3,4). You could achieve this by using numpy.transpose as shown below:

src = np.arange(3*4*5).reshape((3,4,5))
for e in np.transpose(src, [2,0,1]):
    print(row)

Output:

[[ 0  5 10 15]
 [20 25 30 35]
 [40 45 50 55]]
[[ 1  6 11 16]
 [21 26 31 36]
 [41 46 51 56]]
[[ 2  7 12 17]
 [22 27 32 37]
 [42 47 52 57]]
[[ 3  8 13 18]
 [23 28 33 38]
 [43 48 53 58]]
[[ 4  9 14 19]
 [24 29 34 39]
 [44 49 54 59]]

Here the array src is:

array([[[ 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, 27, 28, 29],
        [30, 31, 32, 33, 34],
        [35, 36, 37, 38, 39]],

       [[40, 41, 42, 43, 44],
        [45, 46, 47, 48, 49],
        [50, 51, 52, 53, 54],
        [55, 56, 57, 58, 59]]])
CypherX
  • 7,019
  • 3
  • 25
  • 37
0

General advice: When working with numpy, explicit python loops should be a last resort. Numpy is an extremely powerful tool which covers most use cases. Learn how to use it properly! If it helps, you can think of numpy as almost its own mini-language within a language.

Now, onto the code. I chose here to keep only the subarrays whose values are all below 100, but of course this is completely arbitrary and serves only to demonstrate the code.

import numpy as np

arr = np.array([[[117, 108, 99], [115, 105, 98], [90, 79, 75]], [[20, 3, 99], [101, 250, 30], [75, 89, 83]]])

cond_mask = np.all(a=arr < 100, axis=2)

arr_result = arr[cond_mask]

Let me know if you have any questions about the code :)

AMC
  • 2,642
  • 7
  • 13
  • 35
  • thanks that works however in `np.all()` I need to perform 2 checks (have 2 conditions), ie `arr > lower and arr < upper`, is it possible to do that using this function? – sazr Oct 15 '19 at 04:46
  • Yes! All() operates on boolean arrays, and `arr > lower & arr upper` is a boolean array. Notice that I replaced `and` with `&`, a necessary change since `and` operates on booleans, not arrays of booleans. If it isn’t clear to you why `arr > lower` returns an array you should read up on broadcasting, one of the most important concepts in numpy, [here](https://numpy.org/devdocs/user/theory.broadcasting.html). – AMC Oct 15 '19 at 05:54
  • Oops, forgot to tag you @sazr. – AMC Oct 15 '19 at 19:52