user16731999
user16731999

Reputation:

calculate inner angles of a polygon

I have the following array of arrays:

[[[623 284]]

 [[526 256]]

 [[532 189]]

 [[504 166]]

 [[323 175]]

 [[276 219]]

 [[119 221]]

 [[  1 272]]

 [[  0 473]]

 [[615 479]]]

These are my points (coordinates) of polygon. What I need to do, is to iterate through this array, taking each point for calculating every inner angle in polygon. I have this function for calculating angle between 3 points: getAngle((px1, py1), (px2, py2), (px3, py3)). Basically I want to put this function in some loop which will take points repectively, like:

getAngle((623, 284), (526, 256), (532, 189)),
getAngle((526, 256), (532, 189), (504, 166)),
getAngle((532, 189), (504, 166), (323, 175)),

and so on until the end... Which loop it suppose to be and how to implement it?

Upvotes: 0

Views: 1431

Answers (4)

user1196549
user1196549

Reputation:

What about a simple

for i in range(len(A) - 2):
    getAngle(A[0][i], A[0][i+1], A[0][i+2])

Erratum:

for i in range(len(A) - 2):
    getAngle(A[i][0], A[i+1][0], A[i+2][0])

Upvotes: 1

Christoph Rackwitz
Christoph Rackwitz

Reputation: 15354

Since you have points (as a numpy array) and you want angles (signed angles), here's a complete solution.

  • calculate vectors
  • use the cross product: cross product

This uses whole-array operations.

Note the very valid remark by Yves Daoust in the comments. Acute angles aren't handled properly by this demonstration but they can be with an additional check of the vectors and correction to the angle.

import numpy as np

points = np.array([
 [[623, 284]],
 [[526, 256]],
 [[532, 189]],
 [[504, 166]],
 [[323, 175]],
 [[276, 219]],
 [[119, 221]],
 [[  1, 272]],
 [[  0, 473]],
 [[615, 479]]])
# funny shape because OpenCV. it's a Nx1 vector of 2-channel elements
# fix that up, remove the silly dimension
points.shape = (-1, 2)

# the vectors are differences of coordinates
# a points into the point, b out of the point
a = points - np.roll(points, 1, axis=0)
b = np.roll(a, -1, axis=0) # same but shifted

# we'll need to know the length of those vectors
alengths = np.linalg.norm(a, axis=1)
blengths = np.linalg.norm(b, axis=1)

# we need only the length of the cross product,
# and we work in 2D space anyway (not 3D),
# so the cross product can't result in a vector, just its z-component
crossproducts = np.cross(a, b) / alengths / blengths

angles = np.arcsin(crossproducts)
angles_degrees = angles / np.pi * 180

print("angles in degrees:")
print(angles_degrees)

# this is just for printing/displaying, not useful in code
print("point and angle:")
print(np.hstack([points, angles_degrees.reshape((-1, 1))]))

angles in degrees:
[-76.24798  79.01601 -55.71665 -42.24728 -40.2652   42.38197 -22.64432 -66.34078 -89.72609 -88.20969]
point and angle:
[[623.      284.      -76.24798]
 [526.      256.       79.01601]
 [532.      189.      -55.71665]
 [504.      166.      -42.24728]
 [323.      175.      -40.2652 ]
 [276.      219.       42.38197]
 [119.      221.      -22.64432]
 [  1.      272.      -66.34078]
 [  0.      473.      -89.72609]
 [615.      479.      -88.20969]]

some drawing:

import cv2 as cv

canvas = np.zeros((600, 700, 3)) # floats, range 0..1

cv.polylines(canvas, [points], isClosed=True, color=(1,1,1))

for i,angle in enumerate(angles_degrees):
    cv.circle(canvas, center=tuple(points[i]), radius=5, color=(0,0,1), thickness=cv.FILLED)
    cv.putText(
        canvas,
        f"{angle:+.1f}",
        org=tuple(points[i]),
        fontFace=cv.FONT_HERSHEY_SIMPLEX,
        fontScale=0.75,
        color=(0,1,1),
        thickness=2)

cv.imshow("canvas", canvas)
cv.waitKey(-1)
cv.destroyWindow("canvas")

drawing

Upvotes: 2

I'mahdi
I'mahdi

Reputation: 24049

try this:

lst = [[[623,284]] , [[526, 256]] , [[532, 189]]]

tuple(map((lambda x : tuple(x[0])), lst))

output:

((623, 284), (526, 256), (532, 189))

EDIT as your comment:

lst = [[[623,284]], [[526, 256]], [[532, 189]], [[504, 166]], [[323, 175]], [[276, 219]], [[119, 221]], [[  1, 272]], [[  0, 473]] , [[615, 479]]]


for l in range(0,len(lst)-2):
    f , s , t = (tuple(map((lambda x : tuple(x[0])), lst[l:l+3])))
    getAngle(f , s, t)

Upvotes: -2

igrinis
igrinis

Reputation: 13626

more_itertools is a good library for stuff like this:

import more_itertools

points = [[623,284], [526, 256], [532, 189], [504, 166], [323, 175], [276, 219], [119, 221], [  1, 272], [  0, 473]]
for triplet in more_itertools.windowed(points, n=3, step=3):
    getAngle(*triplet)

Upvotes: 0

Related Questions