5

I have 2D numpy array which is a mask from an image. Each cell has 0 or 1 value. So I would like to find top:left,right, bottom:left,right in an array where value is 1.

For example input array:

[00000]
[01110]
[01100]
[00000]

Expected output: (1,1), (1,3), (2,1), (2,2)

funie200
  • 3,688
  • 5
  • 21
  • 34
eugenn
  • 1,638
  • 6
  • 23
  • 38

4 Answers4

5

Using np.argwhere and itertools.product:

import numpy as np
from itertools import product

def corners(np_array):
    ind = np.argwhere(np_array)
    res = []
    for f1, f2 in product([min,max], repeat=2):
        res.append(f1(ind[ind[:, 0] == f2(ind[:, 0])], key=lambda x:x[1]))
    return res
corners(arr)

Output:

[array([1, 1], dtype=int64),
 array([2, 1], dtype=int64),
 array([1, 3], dtype=int64),
 array([2, 2], dtype=int64)]
Chris
  • 29,127
  • 3
  • 28
  • 51
  • Nice one, had completely forgotten about `itertools.product`! – Asmus May 15 '19 at 16:44
  • hi Chris, do you have a more efficient way of answering this question? thanks https://stackoverflow.com/questions/29630052/ordering-coordinates-from-top-left-to-bottom-right – mattsmith5 Apr 01 '21 at 05:58
2

You can use the numpy.amax operation for finding max values in a multi_dimensional array. As below

def corners_v2(np_array):
    max_values = np.amax(np_array)
    result = np.where(np_array == np.amax(np_array))
    x1 = np.min(result[0])
    x2 = np.max(result[0])
    y1 = np.min(result[1])
    y2 = np.max(result[1])
    return x1, y1, x2, y2
1
xy=np.array([[0,0,0,0,0],[0,1,1,1,0],[0,1,1,0,0],[0,0,0,0,0]])
x,y=np.where(xy==1)
tl_i=np.argmin(x)
tl=[x[tl_i],y[tl_i]]
tr_i=np.argmax(y)
tr=[x[tr_i],y[tr_i]]
bl_i=np.argmax(x)
bl=[x[bl_i],y[bl_i]]
br_i=len(x)-1-np.argmax(np.flip(x))
br=[x[br_i],y[br_i]]
Aly Hosny
  • 827
  • 5
  • 13
0

Use transpose and nonzero from numpy, like:

im=np.array([[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,0,0],
[0,0,0,0,0]])

print(np.transpose(np.nonzero(im)))

yields:

array([[1, 1],
       [1, 2],
       [1, 3],
       [2, 1],
       [2, 2]])

Update: Still not perfect, but as long as the mask is continuous within its rows, you could evaluate np.diff() to get an idea where the 0->1 and 1->0 transitions are:

leftedge=np.transpose(np.nonzero(np.diff(im,prepend=0)==1))
rightedge=np.transpose(np.nonzero(np.diff(im,append=0)==-1))

top_left     = leftedge[0]
bottom_left  = leftedge[-1]
bottom_right = rightedge[-1]
top_right    = rightedge[0]

pts=[list(x) for x in [top_left,top_right,bottom_left,bottom_right]]

yields: [[1, 1], [1, 3], [2, 1], [2, 2]]

I'd suggest to use Chris' answer instead.

Asmus
  • 5,117
  • 1
  • 16
  • 21