Add dataloaders (v1/v2), analysis scripts, and documentation for working with UAV-GeoLoc (World-UAV). Co-authored-by: Cursor <cursoragent@cursor.com>
309 lines
13 KiB
Python
309 lines
13 KiB
Python
from torch.utils.data import DataLoader
|
|
from dataclasses import dataclass,field
|
|
from eval import eval
|
|
import os
|
|
import torch
|
|
from torchvision import transforms as T
|
|
from dataset.World import AerialDatasetEvalGroup, AerialDatasetEvalVanilia
|
|
from models import model
|
|
import glob
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.patches as patches
|
|
import numpy as np
|
|
import argparse
|
|
from tqdm import tqdm
|
|
|
|
def get_parser():
|
|
parser = argparse.ArgumentParser(description="Configuration for training the model")
|
|
|
|
# Model Configurations
|
|
parser.add_argument('--mode', type=str, default='group', help='Model architecture')
|
|
parser.add_argument('--model_path', type=str, default='./world', help='Path to save model checkpoints')
|
|
|
|
# Group Config
|
|
parser.add_argument('--group_arch', type=str, default='groupdinonet', help='Group architecture')
|
|
parser.add_argument('--group_config', type=str, default='none', help='Group configuration')
|
|
|
|
# Backbone Config
|
|
parser.add_argument('--backbone_arch', type=str, default='dinov2_vits14', help='Backbone architecture')
|
|
parser.add_argument('--pretrain_flag', type=bool, default=True, help='Flag to use pre-trained weights')
|
|
|
|
# Agg Config
|
|
parser.add_argument('--agg_arch', type=str, default='multiconvap', help='Aggregation architecture')
|
|
parser.add_argument('--agg_in_channels', type=int, default=384, help='Input channels for aggregation')
|
|
parser.add_argument('--agg_out_channels', type=int, default=384, help='Output channels for aggregation')
|
|
parser.add_argument('--agg_s1', type=int, default=1, help='Aggregation s1 parameter')
|
|
parser.add_argument('--agg_s2', type=int, default=1, help='Aggregation s2 parameter')
|
|
parser.add_argument('--agg_LPN', type=bool, default=False, help='Use LPN for aggregation')
|
|
|
|
# Dataset Paths
|
|
parser.add_argument('--dataset_root_dir', type=str, default='/media/guan/新加卷/EdgeBing/TestData/test_40_midref_rot0/', help='Root directory of the dataset')
|
|
#'/media/Shen/Data/RingoData/WorldLoc/TestData/vpair test_40_midref_rot0'
|
|
# Checkpoint Config
|
|
parser.add_argument('--checkpoint_path', type=str, default=None, help='Path to start from a checkpoint')
|
|
parser.add_argument('--save_dir_path', type=str, default='/media/Shen/Data/RingoData/DenseUAV/test/db.txt', help='Root directory of the dataset')
|
|
|
|
# Training Parameters
|
|
parser.add_argument('--num_workers', type=int, default=0 if os.name == 'nt' else 4, help='Number of workers for data loading')
|
|
parser.add_argument('--device', type=str, default='cuda' if torch.cuda.is_available() else 'cpu', help='Device for training')
|
|
parser.add_argument('--cudnn_benchmark', type=bool, default=True, help='Use cudnn benchmark for performance')
|
|
parser.add_argument('--cudnn_deterministic', type=bool, default=False, help='Make cudnn deterministic')
|
|
|
|
# Training Settings
|
|
parser.add_argument('--mixed_precision', type=bool, default=True, help='Use mixed precision training')
|
|
parser.add_argument('--custom_sampling', type=bool, default=True, help='Use custom sampling')
|
|
parser.add_argument('--seed', type=int, default=1, help='Random seed')
|
|
parser.add_argument('--epochs', type=int, default=30, help='Number of epochs to train')
|
|
parser.add_argument('--batch_size', type=int, default=1, help='Batch size')
|
|
parser.add_argument('--verbose', type=bool, default=True, help='Verbose output during training')
|
|
parser.add_argument('--gpu_ids', type=tuple, default=(1,), help='GPU IDs for training')
|
|
|
|
# Optimizer Config
|
|
parser.add_argument('--clip_grad', type=float, default=100.0, help='Clip gradients (None or float)')
|
|
parser.add_argument('--decay_exclude_bias', type=bool, default=False, help='Exclude bias from decay')
|
|
parser.add_argument('--grad_checkpointing', type=bool, default=False, help='Use gradient checkpointing')
|
|
|
|
# Loss Config
|
|
parser.add_argument('--label_smoothing', type=float, default=0.1, help='Label smoothing factor')
|
|
|
|
# Learning Rate
|
|
parser.add_argument('--lr', type=float, default=0.001, help='Learning rate')
|
|
parser.add_argument('--scheduler', type=str, default='cosine', help='Learning rate scheduler')
|
|
parser.add_argument('--warmup_epochs', type=float, default=0.1, help='Warmup epochs for learning rate')
|
|
parser.add_argument('--lr_end', type=float, default=0.0001, help='End learning rate for polynomial scheduler')
|
|
|
|
return parser
|
|
|
|
def parse_config():
|
|
parser = get_parser()
|
|
args = parser.parse_args()
|
|
|
|
# Build the config dictionaries dynamically based on parsed args
|
|
group_config = {
|
|
"group_arch": args.group_arch,
|
|
"group_config": {args.group_config}
|
|
}
|
|
|
|
backbone_config = {
|
|
"backbone_arch": args.backbone_arch,
|
|
"pretrain_flag": args.pretrain_flag
|
|
}
|
|
|
|
agg_config = {
|
|
"agg_arch": args.agg_arch,
|
|
"agg_config": {
|
|
"in_channels": args.agg_in_channels,
|
|
"out_channels": args.agg_out_channels,
|
|
"s1": args.agg_s1,
|
|
"s2": args.agg_s2,
|
|
"LPN": args.agg_LPN
|
|
}
|
|
}
|
|
|
|
config = {
|
|
"mode": args.mode,
|
|
"model_path": args.model_path,
|
|
"group": group_config,
|
|
"backbone": backbone_config,
|
|
"agg": agg_config,
|
|
"dataset_root_dir": args.dataset_root_dir,
|
|
"checkpoint_path": args.checkpoint_path,
|
|
"save_dir_path":args.save_dir_path,
|
|
"num_workers": args.num_workers,
|
|
"device": args.device,
|
|
"cudnn_benchmark": args.cudnn_benchmark,
|
|
"cudnn_deterministic": args.cudnn_deterministic,
|
|
"mixed_precision": args.mixed_precision,
|
|
"custom_sampling": args.custom_sampling,
|
|
"seed": args.seed,
|
|
"epochs": args.epochs,
|
|
"batch_size": args.batch_size,
|
|
"verbose": args.verbose,
|
|
"gpu_ids": args.gpu_ids,
|
|
"clip_grad": args.clip_grad,
|
|
"decay_exclude_bias": args.decay_exclude_bias,
|
|
"grad_checkpointing": args.grad_checkpointing,
|
|
"label_smoothing": args.label_smoothing,
|
|
"lr": args.lr,
|
|
"scheduler": args.scheduler,
|
|
"warmup_epochs": args.warmup_epochs,
|
|
"lr_end": args.lr_end
|
|
}
|
|
|
|
return config
|
|
|
|
|
|
#-------------------------------------------------------------------------------------------#
|
|
# Test Config
|
|
#-------------------------------------------------------------------------------------------#
|
|
config = parse_config()
|
|
|
|
if not os.path.exists(config['save_dir_path']):
|
|
os.mkdir(config['save_dir_path'])
|
|
|
|
# test angle
|
|
angle_list = list(range(0, 1))
|
|
|
|
|
|
IMAGENET_MEAN_STD = {'mean': [0.485, 0.456, 0.406],
|
|
'std': [0.229, 0.224, 0.225]}
|
|
eval_transform = T.Compose([
|
|
T.Resize((224, 224), interpolation=T.InterpolationMode.BILINEAR),
|
|
T.ToTensor(),
|
|
T.Normalize(mean=IMAGENET_MEAN_STD["mean"], std=IMAGENET_MEAN_STD["std"]),
|
|
])
|
|
|
|
|
|
if config["mode"] == "vanilia":
|
|
model = model.BackboneGlobal(config['backbone']['backbone_arch'],
|
|
config['backbone']['pretrain_flag'],
|
|
config['agg']['agg_arch'],
|
|
config['agg']['agg_config'])
|
|
else:
|
|
model = model.GrounpDinoGlobal(config['group']['group_arch'],
|
|
config['agg']['agg_arch'],
|
|
config['agg']['agg_config'])
|
|
|
|
|
|
for angle in tqdm(angle_list):
|
|
if config["mode"] == "vanilia":
|
|
eva_dataset_query = AerialDatasetEvalVanilia(data_dir=config['dataset_root_dir'],
|
|
mode='query',
|
|
angle=angle,
|
|
transforms=eval_transform)
|
|
|
|
eval_dataloader_query = DataLoader(eva_dataset_query,
|
|
batch_size=config['batch_size'],
|
|
num_workers=config['num_workers'],
|
|
shuffle=not config['custom_sampling'],
|
|
pin_memory=True)
|
|
|
|
eva_dataset_db = AerialDatasetEvalVanilia(data_dir=config['dataset_root_dir'],
|
|
mode='DB',
|
|
transforms=eval_transform)
|
|
|
|
eval_dataloader_db = DataLoader(eva_dataset_db,
|
|
batch_size=config['batch_size'],
|
|
num_workers=config['num_workers'],
|
|
shuffle=not config['custom_sampling'],
|
|
pin_memory=True)
|
|
else:
|
|
|
|
eva_dataset_query = AerialDatasetEvalGroup(data_dir=config["dataset_root_dir"],
|
|
mode='query',
|
|
angle=angle,
|
|
transforms=eval_transform)
|
|
|
|
eval_dataloader_query = DataLoader(eva_dataset_query,
|
|
batch_size=config['batch_size'],
|
|
num_workers=config['num_workers'],
|
|
shuffle=not config['custom_sampling'],
|
|
pin_memory=True)
|
|
|
|
eva_dataset_db = AerialDatasetEvalGroup(data_dir=config["dataset_root_dir"],
|
|
mode='DB',
|
|
transforms=eval_transform)
|
|
|
|
eval_dataloader_db = DataLoader(eva_dataset_db,
|
|
batch_size=config['batch_size'],
|
|
num_workers=config['num_workers'],
|
|
shuffle=not config['custom_sampling'],
|
|
pin_memory=True)
|
|
|
|
|
|
|
|
|
|
# model = model.GrounpGlobal(config.group['group_arch'],
|
|
# config.agg['agg_arch'],
|
|
# config.agg['agg_config'])
|
|
|
|
|
|
|
|
model_state_dict = torch.load(config['checkpoint_path'], map_location='cuda:0')
|
|
model.load_state_dict(model_state_dict, strict=False)
|
|
|
|
model = model.to(config['device'])
|
|
|
|
|
|
|
|
# pos_gt = eval_dataloader_db.dataset.get_gt_npy()
|
|
pos_gt = eval_dataloader_db.dataset.get_gt()
|
|
|
|
result, predictions, really_pos_gt = eval.evaluate(config, model, eval_dataloader_query, eval_dataloader_db, pos_gt, mode=config["mode"],LPN=config['agg']['agg_config']['LPN'])
|
|
print(config['checkpoint_path'])
|
|
print('top 1: ', round(result[0]*100,2), 'top 5: ', round(result[1]*100,2), 'top 10: ', round(result[2]*100,2)) #vanilia
|
|
save_result_txt = config['save_dir_path'] + '/' + str(angle) + '.txt'
|
|
with open(save_result_txt, 'w') as f_w:
|
|
info = 'top 1: '+ str(round(result[0]*100,2)) + ' top 5: ' +str(round(result[1]*100,2)) + ' top 10: ' + str(round(result[2]*100,2))
|
|
f_w.write(info + '\n')
|
|
f_w.close()
|
|
|
|
|
|
# vis and save retrieval results
|
|
# save_vis_dir = config.dataset_root_dir + '/' + 'vis' + '/'
|
|
# if not os.path.exists(save_vis_dir):
|
|
# os.makedirs(save_vis_dir)
|
|
|
|
# temp_path = os.path.join(config.dataset_root_dir, 'reference_images')
|
|
# DB_path = sorted(glob.glob(f'{temp_path}/{"*.png"}'))
|
|
|
|
# # save top 1 flase or wrong
|
|
# with open(config.save_pred_txt, 'w') as f:
|
|
# for i in range(predictions.shape[0]):
|
|
# query_path = eval_dataloader_query.dataset.getitem(i)
|
|
# if np.any(np.in1d(predictions[i,0], really_pos_gt[i][1])):
|
|
# num = 1
|
|
# else:
|
|
# num = 0
|
|
# pred_path = DB_path[predictions[i,0]]
|
|
# info = query_path + ' ' + pred_path + ' ' + str(num) + '\n'
|
|
# f.write(info)
|
|
|
|
|
|
# for i in range(predictions.shape[0]):
|
|
# query_path = eval_dataloader_query.dataset.getitem(i)
|
|
# fig, axs = plt.subplots(2, 6, figsize=(15, 5))
|
|
# query_img = plt.imread(query_path)
|
|
# for j in range(2):
|
|
# for k in range(6):
|
|
# if j == 0 and k == 0:
|
|
# axs[j, k].imshow(query_img)
|
|
# axs[j, k].axis('off') # 不显示坐标轴
|
|
# elif j==0 and k != 0:
|
|
# if np.any(np.in1d(predictions[i,k], really_pos_gt[i][1] )):
|
|
|
|
# db_img_path = DB_path[predictions[i,k]]
|
|
# db_img = plt.imread(db_img_path)
|
|
# axs[j, k].imshow(db_img)
|
|
# # 创建一个矩形框
|
|
# rect = patches.Rectangle((10, 10), 2, 2, linewidth=10, edgecolor='blue', facecolor='none')
|
|
|
|
# # 将矩形框添加到图像上,根据图像尺寸调整框的大小
|
|
# rect.set_transform(axs[j, k].transData) # 将框的坐标系设置为数据坐标系
|
|
# axs[j, k].add_patch(rect)
|
|
# axs[j,k].axis('off') # 不显示坐标轴
|
|
# else:
|
|
# db_img_path = DB_path[predictions[i,k]]
|
|
# db_img = plt.imread(db_img_path)
|
|
# axs[j, k].imshow(db_img)
|
|
# # 创建一个矩形框
|
|
# rect = patches.Rectangle((10, 10), 2, 2, linewidth=10, edgecolor='red', facecolor='none')
|
|
|
|
# # 将矩形框添加到图像上,根据图像尺寸调整框的大小
|
|
# rect.set_transform(axs[j, k].transData) # 将框的坐标系设置为数据坐标系
|
|
# axs[j, k].add_patch(rect)
|
|
# axs[j, k].axis('off') # 不显示坐标轴
|
|
# if j ==1:
|
|
# try:
|
|
# db_img_path = DB_path[really_pos_gt[i][1][k]]
|
|
# db_img = plt.imread(db_img_path)
|
|
# axs[j, k].imshow(db_img)
|
|
# axs[j, k].axis('off') # 不显示坐标轴
|
|
# except:
|
|
# break
|
|
|
|
# save_one_path = save_vis_dir + str(i) + '.png'
|
|
# plt.savefig(save_one_path, dpi=300)
|
|
|
|
|