#opencv
Вопрос:
У меня есть следующий массив массивов:
[[[623 284]]
[[526 256]]
[[532 189]]
[[504 166]]
[[323 175]]
[[276 219]]
[[119 221]]
[[ 1 272]]
[[ 0 473]]
[[615 479]]]
Это мои точки (координаты) многоугольника. Что мне нужно сделать, так это перебрать этот массив, взяв каждую точку для вычисления каждого внутреннего угла в многоугольнике. У меня есть эта функция для вычисления угла между 3 точками: getAngle((px1, py1), (px2, py2), (px3, py3))
.
В принципе, я хочу поместить эту функцию в некоторый цикл, который будет отбирать точки выборочно, например:
getAngle((623, 284), (526, 256), (532, 189)),
getAngle((526, 256), (532, 189), (504, 166)),
getAngle((532, 189), (504, 166), (323, 175)),
и так далее до самого конца…
Какой это должен быть цикл и как его реализовать?
Комментарии:
1. вместо того, чтобы повторять, вы можете попытаться написать своего рода рекурсивную функцию, которая перемещает своего рода 3-узел кадра вниз по строке при вычислении текущего значения с тремя активными узлами
2. это похоже на массив numpy .
Ответ №1:
Поскольку у вас есть точки (в виде массива чисел), и вам нужны углы (углы со знаком), вот полное решение.
- вычисление векторов
- используйте перекрестное изделие:
При этом используются операции со всем массивом.
Обратите внимание на очень верное замечание Ива Дауста в комментариях. Острые углы не обрабатываются должным образом этой демонстрацией, но они могут быть с дополнительной проверкой векторов и коррекцией угла.
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]]
какой-то рисунок:
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")
Комментарии:
1. Хорошая идея-предварительно вычислить попарные различия. Но имейте в виду, что использование перекрестного продукта возвращает угол только в двух квадрантах.
2. @Кристоф Раквиц, всегда помнит, что на этом сайте вы должны ответить на вопрос ОП, не думайте о вопросе и отвечайте, как вам нравится, если вы считаете, что ваш ответ хорош, создайте новый вопрос и ответьте сами.
3. @ChristophRackwitz, ты молодец, и твой ответ очень хорош, но не для этого вопроса
4. вопрос явно говорил о том, что цель была достигнута. как индексировать массив и писать цикл на python, это описано в каждом учебнике. из всех «причудливых» ответов я выбрал свой, чтобы показать что-то мощное (numpy и математика). Я думаю, что более ценно сообщать людям информацию, которую они не ожидали, но попросили бы, если бы знали. в любом случае, если вам интересно, я здесь ничего не снижал.
Ответ №2:
more_itertools
это хорошая библиотека для подобных вещей:
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)
Ответ №3:
Как насчет простого
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])
Комментарии:
1.
IndexError: index 1 is out of bounds for axis 0 with size 1
2. @user16731999: да, просто нужно поменять местами индексации.
3. это самое простое решение, следовательно, должно быть предпочтительнее других, которые добавляют ненужную сложность
Ответ №4:
попробуйте это:
lst = [[[623,284]] , [[526, 256]] , [[532, 189]]]
tuple(map((lambda x : tuple(x[0])), lst))
выход:
((623, 284), (526, 256), (532, 189))
ОТРЕДАКТИРУЙТЕ как свой комментарий:
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)
Комментарии:
1. Мне нужно вызвать функцию внутри цикла foor, а не создавать кортеж из массива.
2. это дает мне ошибку, что функции getAngle нужно 3 аргумента, но есть только один
3. @user16731999 попробуйте сейчас, я добавлю одну строку
4. @user16731999, хорошо, подожди секунду, я понимаю
5. прежде всего, я отредактировал ваш код как
for l in range(0,len(lst)-2,3):
потому, что он не работал. Во-вторых, да, это работает, но не так, как я хочу. он повторяется через КАЖДЫЙ кортеж, поэтому он берет((623, 284), (526, 256), (532, 189))
, потом((504, 166), (323, 175), (276, 219))
, потом((119, 221), (1, 272), (0, 473))
и останавливается. Я описал в своем вопросе, как я хочу, чтобы это повторялось.