Identificação do campo
A delimitação do campo é feita por meio de ArUcos distribuídos nas 4 extremidades do retângulo que forma a área de jogo. Esses ArUcos são identificados pelo método cv2.aruco.detectMarkers.
Para obter um campo retangular que independa da posição da câmera, é calculada uma matrix M de transformação de perspectiva (pelo método cv2.getPerspectiveTransform) que transpõe os pontos do campo para uma nova referência. Nessa referência os ArUcos são as extremidades de um retângulo e é nela que são feitos todos os cálculos sobre o movimento da bola.
Por último a imagem é resultante é transformada novamente para a perspectiva original apenas para
Na imagem e vídeos abaixo é possível perceber esse processo. No vídeo o processo se torna mais visível conforme a posição de um dos ArUcos é alterada, o que também afeta a transformação realizada, mas não o tamanho do campo.
Código que implementa a delimitação do campo
field_delimited = False
# Transformation matrix for the homography
M = None
max_width = None
max_height = None
# Relevant corners for the homography
corners, ids = None, None
top_left_corner = None
bottom_right_corner = None
top_right_corner = None
bottom_left_corner = None
# Configuring the ArUco detector
aruco_dict = cv2.aruco.getPredefinedDictionary(cv2.aruco.DICT_6X6_250)
parameters = cv2.aruco.DetectorParameters()
detector = cv2.aruco.ArucoDetector(aruco_dict, parameters)
ax.set_title("Identifying the ArUcos", color='gray', fontsize=10)
while not field_delimited:
succes, frame = cap.read()
# Undistorting the frame from the camera
dst = cv2.undistort(frame, cameraMatrix, dist, None, newCameraMatrix)
frame = dst[roi[1]:roi[1]+roi[3], roi[0]:roi[0]+roi[2]]
output_frame = dst.copy()
# First we make the image monochromatic
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Then we detect the arucos
corners, ids, rejectedImgPoints = detector.detectMarkers(gray)
frame_markers = cv2.aruco.drawDetectedMarkers(frame.copy(), corners, ids)
corners = np.array(corners, dtype=int)
# We now check if the amount of detected ArUcos is the expected one
# If it is not, the detection failed
if len(corners) == 4:
field_delimited = True
# The top left corner is identified by the Aruco with ID = 0
top_left_corner_id = np.where(ids == 0)[0]
# The bottom right corner is identified by the Aruco with ID = 2
bottom_right_corner_id = np.where(ids == 2)[0]
# The bottom right corner is identified by the Aruco with ID = 3
top_right_corner_id = np.where(ids == 3)[0]
# The bottom right corner is identified by the Aruco with ID = 1
bottom_left_corner_id = np.where(ids == 1)[0]
top_left_corner = corners[top_left_corner_id][0][0][0]
bottom_right_corner = corners[bottom_right_corner_id][0][0][0]
top_right_corner = corners[top_right_corner_id][0][0][0]
bottom_left_corner = corners[bottom_left_corner_id][0][0][0]
width_top = np.hypot(
top_left_corner[0] - top_right_corner[0],
top_left_corner[1] - top_right_corner[1])
width_bottom = np.hypot(
bottom_left_corner[0] - bottom_right_corner[0],
bottom_left_corner[1] - bottom_right_corner[1])
max_width = max(int(width_top), int(width_bottom))
height_left = np.hypot(
top_left_corner[0] - bottom_left_corner[0],
top_left_corner[1] - bottom_left_corner[1])
height_right = np.hypot(
top_right_corner[0] - bottom_right_corner[0],
top_right_corner[1] - bottom_right_corner[1])
max_height = max(int(height_left), int(height_right))
input_pts = np.float32([top_left_corner,
bottom_left_corner,
bottom_right_corner,
top_right_corner])
output_pts = np.float32([[0, 0],
[0, max_height - 1],
[max_width - 1, max_height - 1],
[max_width - 1, 0]])
pixel_density = max_height/field_lenght_m
# Compute the perspective transform M
M = cv2.getPerspectiveTransform(input_pts,output_pts)
output_frame = cv2.cvtColor(frame_markers, cv2.COLOR_BGR2RGB)
# Compute the perspective transform IM to transforme the image back
_, IM = cv2.invert(M)