2

I've been using numpy indexing for a while now. But I've only ever had to select basic shapes such as rectangles or discs

However, I now need to be able to select more arbitrary shapes and I can't find a good way of doing this. Ideally, I would like to be able to give a list of corners and for all of the indices contained within those corners to be selected. We can assume the given shape is convex

For example, given an array filled with zeroes of shape (10, 10), by trying to set the values within the corners ((2,2), (6,3), (4,8) and (7,9)) to 1 this would return a mask like such

  [[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
   [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
   [0., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
   [0., 0., 1., 1., 1., 1., 0., 0., 0., 0.],
   [0., 0., 1., 1., 1., 1., 1., 1., 1., 0.],
   [0., 0., 0., 1., 1., 1., 1., 1., 1., 0.],
   [0., 0., 0., 1., 1., 1., 1., 1., 1., 1.],
   [0., 0., 0., 0., 0., 0., 1., 1., 1., 1.],
   [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
   [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]

Now one of the problems is that there generally is no unique solution to this problem, but taking a plausible one is good enough for me. I can't think of a way of doing this using numpy however, as only basic slicings and clear mathematical equations seem to be supported.

Has anyone ever run into such a challenge? Do I have to resort to more traditional python for loops?

Skum
  • 506
  • 5
  • 19

1 Answers1

2

Admittedly an ugly solution, but how about generating a binary mask for selection from your polygon via OpenCV and using that one?

import cv2
import numpy as np

corners = np.asarray([(2,2), (6,3), (4,8), (7,9)])

target = np.zeros([10,10])
mask = cv2.fillPoly(np.zeros_like(target, dtype=np.uint8), [corners], 255).astype(bool)

target[mask] = 1

generates:

>>> target
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 1., 1., 0., 0.]], dtype=float32)

Note: I'm using the corners in the order you gave them. For OpenCV, points in a polygon are interpreted in-order (hence the difference in shape between my output and yours). Reorder the corners accordingly to obtain exactly the shape you need (e.g., clockwise).

Note (2): I'm interpreting your corners as (x, y), not as (row,col), since it wasn't specified in the question and OpenCV uses (x,y) convention for points (while numpy uses (row,col)).


To generate the output you want, swap the corner's coordinates and rearrange them as follows:

corners = np.asarray([(2,2), (6,3), (7,9), (4,8) ])[:,(1,0)]

With this (and the code above) you obtain:

>>> target
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 0., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 1., 1., 0.],
       [0., 0., 0., 1., 1., 1., 1., 1., 1., 0.],
       [0., 0., 0., 1., 1., 1., 1., 1., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 1., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)
GPhilo
  • 18,519
  • 9
  • 63
  • 89