import os import sys import json import glob import torch import zipfile import argparse import numpy as np from tqdm import tqdm sys.path.insert(1, os.path.join(sys.path[0], '..')) from utils.utils_calib import FramebyFrameCalib from model.metrics import calc_iou_part, calc_iou_whole_with_poly, calc_reproj_error, calc_proj_error def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--root_dir", type=str, required=True) parser.add_argument("--split", type=str, required=True) parser.add_argument("--pred_file", type=str, required=True) args = parser.parse_args() return args def get_homographies(file_paths): npy_files = [] for file_path in file_paths: directory_path = os.path.join(os.path.join(args.root_dir, "Annotations/80_95"), file_path) if os.path.exists(directory_path): files = os.listdir(directory_path) npy_files.extend([os.path.join(directory_path, file) for file in files if file.endswith('.npy')]) npy_files = sorted(npy_files) return npy_files def make_file_name(file): file = "TS-WorldCup/" + file.split("TS-WorldCup/")[-1] splits = file.split('/') side = splits[3] match = splits[4] image = splits[5] frame = image.split('_homography.npy')[0] return side + '-' + match + '-' + frame + '.json' def pan_tilt_roll_to_orientation(pan, tilt, roll): """ Conversion from euler angles to orientation matrix. :param pan: :param tilt: :param roll: :return: orientation matrix """ Rpan = np.array([ [np.cos(pan), -np.sin(pan), 0], [np.sin(pan), np.cos(pan), 0], [0, 0, 1]]) Rroll = np.array([ [np.cos(roll), -np.sin(roll), 0], [np.sin(roll), np.cos(roll), 0], [0, 0, 1]]) Rtilt = np.array([ [1, 0, 0], [0, np.cos(tilt), -np.sin(tilt)], [0, np.sin(tilt), np.cos(tilt)]]) rotMat = np.dot(Rpan, np.dot(Rtilt, Rroll)) return rotMat def get_sn_homography(cam_params: dict, batch_size=1): # Extract relevant camera parameters from the dictionary pan_degrees = cam_params['cam_params']['pan_degrees'] tilt_degrees = cam_params['cam_params']['tilt_degrees'] roll_degrees = cam_params['cam_params']['roll_degrees'] x_focal_length = cam_params['cam_params']['x_focal_length'] y_focal_length = cam_params['cam_params']['y_focal_length'] principal_point = np.array(cam_params['cam_params']['principal_point']) position_meters = np.array(cam_params['cam_params']['position_meters']) pan = pan_degrees * np.pi / 180. tilt = tilt_degrees * np.pi / 180. roll = roll_degrees * np.pi / 180. rotation = np.array([[-np.sin(pan) * np.sin(roll) * np.cos(tilt) + np.cos(pan) * np.cos(roll), np.sin(pan) * np.cos(roll) + np.sin(roll) * np.cos(pan) * np.cos(tilt), np.sin(roll) * np.sin(tilt)], [-np.sin(pan) * np.cos(roll) * np.cos(tilt) - np.sin(roll) * np.cos(pan), -np.sin(pan) * np.sin(roll) + np.cos(pan) * np.cos(roll) * np.cos(tilt), np.sin(tilt) * np.cos(roll)], [np.sin(pan) * np.sin(tilt), -np.sin(tilt) * np.cos(pan), np.cos(tilt)]], dtype='float') rotation = np.transpose(pan_tilt_roll_to_orientation(pan, tilt, roll)) def convert_homography_SN_to_WC14(H): T = np.eye(3) T[0, -1] = 105 / 2 T[1, -1] = 68 / 2 meter2yard = 1.09361 S = np.eye(3) S[0, 0] = meter2yard S[1, 1] = meter2yard H_SN = S @ (T @ H) return H_SN def get_homography_by_index(homography_file): homography = np.load(homography_file) homography = homography / homography[2:3, 2:3] return homography if __name__ == "__main__": args = parse_args() missed = 0 iou_part_list, iou_whole_list = [], [] rep_err_list, proj_err_list = [], [] with open(args.root_dir + args.split + '.txt', 'r') as file: # Read lines from the file and remove trailing newline characters seqs = [line.strip() for line in file.readlines()] homographies = get_homographies(seqs) prediction_archive = zipfile.ZipFile(args.pred_file, 'r') cam = FramebyFrameCalib(1280, 720, denormalize=True) for h_gt in tqdm(homographies): file_name = h_gt.split('/')[-1].split('.')[0] pred_name = make_file_name(h_gt) if pred_name not in prediction_archive.namelist(): missed += 1 continue homography_gt = get_homography_by_index(h_gt) final_dict = prediction_archive.read(pred_name) final_dict = json.loads(final_dict.decode('utf-8')) keypoints_dict = final_dict['kp'] lines_dict = final_dict['lines'] keypoints_dict = {int(key): value for key, value in keypoints_dict.items()} lines_dict = {int(key): value for key, value in lines_dict.items()} cam.update(keypoints_dict, lines_dict) final_dict = cam.heuristic_voting_ground(refine_lines=True) #homography_pred, ret = cam.get_homography_from_ground_plane(use_ransac=20, inverse=True, refine_lines=True) if final_dict is None: #if homography_pred is None: missed += 1 continue homography_pred = final_dict["homography"] homography_pred = convert_homography_SN_to_WC14(homography_pred) iou_p = calc_iou_part(homography_pred, homography_gt) iou_w, _, _ = calc_iou_whole_with_poly(homography_pred, homography_gt) rep_err = calc_reproj_error(homography_pred, homography_gt) proj_err = calc_proj_error(homography_pred, homography_gt) iou_part_list.append(iou_p) iou_whole_list.append(iou_w) rep_err_list.append(rep_err) proj_err_list.append(proj_err) print(f'Completeness: {1-missed/len(homographies)}') print('IOU Part') print(f'mean: {np.mean(iou_part_list)} \t median: {np.median(iou_part_list)}') print('\nIOU Whole') print(f'mean: {np.mean(iou_whole_list)} \t median: {np.median(iou_whole_list)}') print('\nReprojection Err.') print(f'mean: {np.mean(rep_err_list)} \t median: {np.median(rep_err_list)}') print('\nProjection Err. (meters)') print(f'mean: {np.mean(proj_err_list) * 0.9144} \t median: {np.median(proj_err_list) * 0.9144}')