Reputation: 143
I have a set of x,y coordinates representing a contour as follows:
array([[[ 0, 0]],
[[ 0, 3]],
[[ 1, 3]],
[[ 2, 4]],
...,
[[1189, 5]],
[[1188, 5]],
[[1183, 0]]], dtype=int32)
How do I find the TYPOLOGICAL top-left,top-right,bottom-right and bottom-left extreme points?
clarification following a comment below: The points sought after are the 'obvious' extreme points / corners of a 4 edges Quadrilateral such as a square, rectangle, chevron or trapezoid. Note that the data is organic and that 2 points which may seem, visibly, on the same horizontal / vertical line may not be, e.g. (10,60),(9,58)
I have followed the following logic so far (example for top-left and top-right):
# slice k smallest y value elements
l = c[np.argsort(c.reshape(-1,2), axis=0)[:k][:,1]]
#ARBITRARAY - only examinie items whose y value is below 20
l = l[l[:,:,1]<20]
#sort by X value
l = sorted(l, key =lambda x:x[0])
minX = l[0][0]
maxX = l[len(l)-1][0]
However this rather imperfect and inelegant. Example image attached
Upvotes: 2
Views: 4152
Reputation: 4378
EDIT: based on your precisions on this answer's comments, here is the updated version :
import numpy as np
def find_corners(polygon):
# topmost,leftmost ───────────►xxxxxxxxxx◄────────── topmost,rightmost
# xxxx xx
# leftmost,topmost ──────►xxx xx
# x xx◄────── rightmost,topmost
# x x
# leftmost,bottommost ──────►xx x
# xx xx◄────── rightmost,bottommost
# x xx
# bottommost,leftmost ────────►xxxxxxxxxxxxxxx◄──────── bottommost,rightmost
# topmost is minimum Y
# leftmost is minimum X
# rightmost is maximum X, or minimum -X
# bottommost is maximum Y, or minimum -Y
# X is `pt[0][0]`, Y is `pt[0][1]`
# going clock-wise :
topmost_then_rightmost_point = min(polygon, key=lambda pt: ( pt[0][1], -pt[0][0]))[0]
rightmost_then_topmost_point = min(polygon, key=lambda pt: (-pt[0][0], pt[0][1]))[0]
rightmost_then_bottommost_point = min(polygon, key=lambda pt: (-pt[0][0], -pt[0][1]))[0]
bottommost_then_rightmost_point = min(polygon, key=lambda pt: (-pt[0][1], -pt[0][0]))[0]
bottommost_then_leftmost_point = min(polygon, key=lambda pt: (-pt[0][1], pt[0][0]))[0]
leftmost_then_bottommost_point = min(polygon, key=lambda pt: ( pt[0][0], -pt[0][1]))[0]
leftmost_then_topmost_point = min(polygon, key=lambda pt: ( pt[0][0], pt[0][1]))[0]
topmost_then_leftmost_point = min(polygon, key=lambda pt: ( pt[0][1], pt[0][0]))[0]
top_left = topmost_then_leftmost_point
top_right = topmost_then_rightmost_point
bottom_right = bottommost_then_rightmost_point
bottom_left = bottommost_then_leftmost_point
return tuple(top_left), tuple(top_right), tuple(bottom_right), tuple(bottom_left)
# example from OP :
a = np.array([[[ 0, 0]],
[[ 0, 3]],
[[ 1, 3]],
[[ 2, 4]],
[[1189, 5]],
[[1188, 5]],
[[1183, 0]]], dtype=np.int32)
assert find_corners(a) == ((0, 0), (1183, 0), (1189, 5), (1188, 5))
# another example, based on the polygon drawn in the function's comments :
# v---------------------------------------------v
# x x
# x x
#
# x xx
# x
# x
# x x
# x
# x x
# ^---------------------------------------------^
# using the coordinates you get when putting it in a text editor (starting at line=Y=1, column=X=1) you get clock-wise :
b = np.array(
[
[[27, 1]],
[[30, 4]],
[[31, 4]],
[[31, 7]],
[[30, 8]],
[[29, 9]],
[[16, 9]],
[[15, 7]],
[[14, 6]],
[[13, 5]],
[[13, 4]],
[[15, 2]],
[[17, 2]],
[[18, 1]],
], dtype=np.int32)
assert find_corners(b) == ((18, 1), (27, 1), (29, 9), (16, 9))
Upvotes: 3