Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- .gitignore +2 -0
- lidar_postprocessing/create_range_imgs.ipynb +0 -0
- lidar_postprocessing/fill_pointcloud_gaps.py +39 -20
- lidar_postprocessing/project_water_label.py +1 -1
- localisation/VPR_eval-all-v2.py +0 -215
- localisation/VPR_eval.py +0 -199
- localisation/create_VPR_eval_dataset.ipynb +0 -132
- localisation/groundtruth_utm-single.ipynb +0 -0
- localisation/groundtruth_utm_checker-all.py +5 -4
- localisation/plot_utm_traj.ipynb +32 -10
- segmentation/show_labels-all.py +4 -3
- utils/camera.py +24 -2
- utils/lidar.py +4 -4
- visualisation/convert_imgs2video.py +0 -106
- visualisation/points2image-all.py +16 -6
- visualisation/points2image-single.ipynb +0 -0
.gitignore
CHANGED
|
@@ -217,3 +217,5 @@ __marimo__/
|
|
| 217 |
# Other
|
| 218 |
hazard_detection*
|
| 219 |
results*
|
|
|
|
|
|
|
|
|
| 217 |
# Other
|
| 218 |
hazard_detection*
|
| 219 |
results*
|
| 220 |
+
no-upload*
|
| 221 |
+
|
lidar_postprocessing/create_range_imgs.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lidar_postprocessing/fill_pointcloud_gaps.py
CHANGED
|
@@ -24,7 +24,7 @@ sequence = '20250811_113017'
|
|
| 24 |
# sequence = '20241217_113410'
|
| 25 |
condition = 'flooded'
|
| 26 |
camera_pos = 'front'
|
| 27 |
-
root_directory = f"../Datasets/FRED/{condition}/KITTI-style"
|
| 28 |
# 01000000
|
| 29 |
|
| 30 |
############ Define filenames and directories ####################################
|
|
@@ -43,13 +43,16 @@ a, b, c, d = groundplane_eqn
|
|
| 43 |
|
| 44 |
# timestamps.sort()
|
| 45 |
|
|
|
|
|
|
|
| 46 |
# idx = [0] # mutable index
|
| 47 |
# idx = [0]
|
| 48 |
-
idx = [
|
| 49 |
|
| 50 |
def show_image(i):
|
| 51 |
-
|
| 52 |
if i >= len(timestamps):
|
|
|
|
| 53 |
return
|
| 54 |
image_timestamp = timestamps[i]
|
| 55 |
try:
|
|
@@ -63,28 +66,32 @@ def show_image(i):
|
|
| 63 |
# # print(f"Nan's in pointcloud? {np.any( == np.nan)}")
|
| 64 |
# print(f"Number of zeroed points: {np.sum(np.all(pointcloud.points == 0, axis=1))}")
|
| 65 |
# print(f"invalid points in ground plane: {np.sum(pointcloud.ground_semantic[np.all(pointcloud.points == 0, axis=1)]==0)}")
|
| 66 |
-
pointcloud.points, pointcloud.ground_semantic, pointcloud.ground_inlier = pointcloud.destagger(pointcloud.points, pointcloud.ground_semantic, pointcloud.ground_inlier
|
| 67 |
groundplane_eqn = utils.fit_height_field_linear(pointcloud.points[pointcloud.ground_semantic==0,:3])
|
| 68 |
pointcloud.points, interp_flags = utils.complete_cloud(pointcloud.points, groundplane_eqn)
|
| 69 |
|
| 70 |
-
|
| 71 |
-
img_vis, uv, valid_img = image.project_points(all_points_cam, intensities_cam, cmap, valid_cam, colour_norm=255) #, beam_id, azimuth
|
| 72 |
-
semantic_labels = interp_flags.astype(int) + 1
|
| 73 |
|
| 74 |
-
|
|
|
|
|
|
|
| 75 |
|
| 76 |
-
|
| 77 |
-
(labels_norm, np.zeros(labels_norm.shape[0]), np.zeros(labels_norm.shape[0])),
|
| 78 |
-
axis=1
|
| 79 |
-
) # shape (N, 3)
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
| 83 |
|
| 84 |
-
pcd
|
| 85 |
-
o3d.
|
| 86 |
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
|
| 90 |
except Exception as e:
|
|
@@ -92,7 +99,19 @@ def show_image(i):
|
|
| 92 |
idx[0] += 1
|
| 93 |
show_image(idx[0]) # skip bad one
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
print(f"Finished all pointclouds")
|
|
|
|
| 24 |
# sequence = '20241217_113410'
|
| 25 |
condition = 'flooded'
|
| 26 |
camera_pos = 'front'
|
| 27 |
+
root_directory = f"C:/Users/conno/Documents/data/FRED/{condition}/KITTI-style/" #f"../Datasets/FRED/{condition}/KITTI-style"
|
| 28 |
# 01000000
|
| 29 |
|
| 30 |
############ Define filenames and directories ####################################
|
|
|
|
| 43 |
|
| 44 |
# timestamps.sort()
|
| 45 |
|
| 46 |
+
fig, ax = plt.subplots(figsize=(12.8, 8))
|
| 47 |
+
|
| 48 |
# idx = [0] # mutable index
|
| 49 |
# idx = [0]
|
| 50 |
+
idx = [160]
|
| 51 |
|
| 52 |
def show_image(i):
|
| 53 |
+
ax.clear()
|
| 54 |
if i >= len(timestamps):
|
| 55 |
+
plt.close(fig)
|
| 56 |
return
|
| 57 |
image_timestamp = timestamps[i]
|
| 58 |
try:
|
|
|
|
| 66 |
# # print(f"Nan's in pointcloud? {np.any( == np.nan)}")
|
| 67 |
# print(f"Number of zeroed points: {np.sum(np.all(pointcloud.points == 0, axis=1))}")
|
| 68 |
# print(f"invalid points in ground plane: {np.sum(pointcloud.ground_semantic[np.all(pointcloud.points == 0, axis=1)]==0)}")
|
| 69 |
+
pointcloud.points, pointcloud.ground_semantic, pointcloud.ground_inlier = pointcloud.destagger() #pointcloud.points, pointcloud.ground_semantic, pointcloud.ground_inlier
|
| 70 |
groundplane_eqn = utils.fit_height_field_linear(pointcloud.points[pointcloud.ground_semantic==0,:3])
|
| 71 |
pointcloud.points, interp_flags = utils.complete_cloud(pointcloud.points, groundplane_eqn)
|
| 72 |
|
| 73 |
+
pointcloud.points = pointcloud.points[interp_flags]
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
point_cam, distances_cam, intensities_cam, all_points_cam, valid_cam = pointcloud.points_ouster_to_cam() #, beam_id, azimuth
|
| 76 |
+
img_vis, uv, valid_img, _ = image.project_points(all_points_cam, intensities_cam, cmap, valid_cam, colour_norm=255) #, beam_id, azimuth
|
| 77 |
+
# semantic_labels = interp_flags.astype(int) + 1
|
| 78 |
|
| 79 |
+
# labels_norm = semantic_labels.astype(np.float64) / semantic_labels.max()
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
+
# colors = np.stack(
|
| 82 |
+
# (labels_norm, np.zeros(labels_norm.shape[0]), np.zeros(labels_norm.shape[0])),
|
| 83 |
+
# axis=1
|
| 84 |
+
# ) # shape (N, 3)
|
| 85 |
|
| 86 |
+
# pcd = o3d.geometry.PointCloud()
|
| 87 |
+
# pcd.points = o3d.utility.Vector3dVector(pointcloud.points[:,:3])
|
| 88 |
|
| 89 |
+
# pcd.colors = o3d.utility.Vector3dVector(colors)
|
| 90 |
+
# o3d.visualization.draw_geometries([pcd,])
|
| 91 |
+
ax.imshow(img_vis[:,:,::-1])
|
| 92 |
+
ax.set_title(f"{i+1}/{len(timestamps)} — {image_timestamp}.png\n(close window or press any key to continue)")
|
| 93 |
+
ax.axis("off")
|
| 94 |
+
fig.canvas.draw()
|
| 95 |
|
| 96 |
|
| 97 |
except Exception as e:
|
|
|
|
| 99 |
idx[0] += 1
|
| 100 |
show_image(idx[0]) # skip bad one
|
| 101 |
|
| 102 |
+
def on_key(event):
|
| 103 |
+
if event.key in [' ', 'right']: # space or right arrow
|
| 104 |
+
idx[0] += 1
|
| 105 |
+
show_image(idx[0])
|
| 106 |
+
elif event.key in [' ', 'left']: # space or right arrow
|
| 107 |
+
if idx[0] > 0:
|
| 108 |
+
idx[0] -= 1
|
| 109 |
+
show_image(idx[0])
|
| 110 |
+
elif event.key in ['q', 'escape']: # q or Esc → quit
|
| 111 |
+
plt.close(fig)
|
| 112 |
+
|
| 113 |
+
# while idx[0] < len(timestamps):
|
| 114 |
+
fig.canvas.mpl_connect('key_press_event', on_key)
|
| 115 |
+
show_image(idx[0])
|
| 116 |
+
plt.show()
|
| 117 |
print(f"Finished all pointclouds")
|
lidar_postprocessing/project_water_label.py
CHANGED
|
@@ -23,7 +23,7 @@ sequence = '20250811_113017'
|
|
| 23 |
# sequence = '20241217_113410'
|
| 24 |
condition = 'flooded'
|
| 25 |
camera_pos = 'front'
|
| 26 |
-
root_directory = f"D:/Datasets/FRED/{condition}/KITTI-style"
|
| 27 |
# 01000000
|
| 28 |
|
| 29 |
############ Define filenames and directories ####################################
|
|
|
|
| 23 |
# sequence = '20241217_113410'
|
| 24 |
condition = 'flooded'
|
| 25 |
camera_pos = 'front'
|
| 26 |
+
root_directory = f"C:/Users/conno/Documents/data/FRED/{condition}/KITTI-style/" # f"D:/Datasets/FRED/{condition}/KITTI-style"
|
| 27 |
# 01000000
|
| 28 |
|
| 29 |
############ Define filenames and directories ####################################
|
localisation/VPR_eval-all-v2.py
DELETED
|
@@ -1,215 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import matplotlib.pyplot as plt
|
| 3 |
-
import matplotlib.image as mpimg
|
| 4 |
-
import sys
|
| 5 |
-
sys.path.append(os.path.abspath(".")) # one level up
|
| 6 |
-
import numpy as np
|
| 7 |
-
import cv2
|
| 8 |
-
import open3d as o3d
|
| 9 |
-
from scipy.spatial.transform import Rotation
|
| 10 |
-
from utils.lidar import PointCloud
|
| 11 |
-
from utils.camera import ImageData
|
| 12 |
-
import utils.utils as utils
|
| 13 |
-
from natsort import natsorted, index_natsorted
|
| 14 |
-
import torch
|
| 15 |
-
from tqdm import tqdm
|
| 16 |
-
|
| 17 |
-
################## set device based on cuda availability #################
|
| 18 |
-
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 19 |
-
|
| 20 |
-
print('CUDA availability: ' + str(torch.cuda.is_available()))
|
| 21 |
-
|
| 22 |
-
####################### Functions for matching using numpy on CPU or Pytorch on GPU ###################
|
| 23 |
-
def getMatchIndsCPU(ft_ref,ft_qry,topK=20,metric='cosine'):
|
| 24 |
-
"""
|
| 25 |
-
metric: 'euclidean' or 'cosine'
|
| 26 |
-
"""
|
| 27 |
-
# dMat = cdist(ft_ref,ft_qry,metric)
|
| 28 |
-
|
| 29 |
-
ft_qry_norm = ft_qry / np.linalg.norm(ft_qry, axis=1, keepdims=True) # Shape (M, N)
|
| 30 |
-
ft_ref_norm = ft_ref / np.linalg.norm(ft_ref, axis=1, keepdims=True) # Shape (C, N)
|
| 31 |
-
|
| 32 |
-
# Step 2: Compute cosine similarity
|
| 33 |
-
dMat = 1 - (ft_ref_norm @ ft_qry_norm.T)
|
| 34 |
-
mInds = np.argsort(dMat,axis=0)[:topK].squeeze() # shape: K x ft_qry.shape[0]
|
| 35 |
-
return mInds, dMat
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def getMatchIndsGPU(ft_ref, ft_qry,topK=20, metric='cosine'):
|
| 39 |
-
# metric: 'euclidean' or 'cosine'
|
| 40 |
-
ft_qry_tensor = torch.Tensor(ft_qry).to(device)
|
| 41 |
-
ft_ref_tensor = torch.Tensor(ft_ref).to(device)
|
| 42 |
-
|
| 43 |
-
if metric == 'euclidean':
|
| 44 |
-
# Use torch's cdist for Euclidean distance
|
| 45 |
-
dMat = torch.cdist(ft_ref, ft_qry)
|
| 46 |
-
|
| 47 |
-
elif metric == 'cosine':
|
| 48 |
-
# # Normalize both the query and reference tensors
|
| 49 |
-
ft_qry_norm = ft_qry_tensor / ft_qry_tensor.norm(dim=1, keepdim=True)
|
| 50 |
-
ft_ref_norm = ft_ref_tensor / ft_ref_tensor.norm(dim=1, keepdim=True)
|
| 51 |
-
# Compute cosine similarity (1 - cosine similarity for distance)
|
| 52 |
-
dMat = 1 - ft_ref_norm @ ft_qry_norm.t()
|
| 53 |
-
|
| 54 |
-
# Get the indices of the top 5 closest matches
|
| 55 |
-
mInds = torch.argsort(dMat, dim=0)[:topK].squeeze()
|
| 56 |
-
|
| 57 |
-
return mInds, dMat
|
| 58 |
-
|
| 59 |
-
qry_sets = [
|
| 60 |
-
'20210909_124816_v2',
|
| 61 |
-
]
|
| 62 |
-
|
| 63 |
-
ref_sets = [
|
| 64 |
-
'20230509_115540_v2',
|
| 65 |
-
]
|
| 66 |
-
|
| 67 |
-
vpr_descs = [
|
| 68 |
-
'cosplace',
|
| 69 |
-
'boq',
|
| 70 |
-
'clique-mining',
|
| 71 |
-
'cricavpr',
|
| 72 |
-
'eigenplaces',
|
| 73 |
-
'mixvpr',
|
| 74 |
-
'megaloc',
|
| 75 |
-
'salad',
|
| 76 |
-
'supervlad',
|
| 77 |
-
]
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
img_calib_file = f"./camera_calib.txt"
|
| 81 |
-
|
| 82 |
-
dist_tolerance = 10 # metres
|
| 83 |
-
# qry_idx = 4
|
| 84 |
-
|
| 85 |
-
# User parameters
|
| 86 |
-
location = 'dalby-to-brigalow'
|
| 87 |
-
|
| 88 |
-
################ Reference filenames and directories #################################
|
| 89 |
-
ref_condition = ''
|
| 90 |
-
ref_camera_pos = 'front'
|
| 91 |
-
|
| 92 |
-
ref_timestamps = []
|
| 93 |
-
ref_utms = []
|
| 94 |
-
ref_img_filenames = []
|
| 95 |
-
ref_utm_filenames = []
|
| 96 |
-
|
| 97 |
-
for ref_set in ref_sets:
|
| 98 |
-
print(f"Loading {ref_set}")
|
| 99 |
-
|
| 100 |
-
ref_root_directory = f"../Datasets/dalby/KITTI-style/{location}"
|
| 101 |
-
ref_vpr_root = f"../Datasets/dalby/KITTI-style/{location}/vpr_ftrs/"
|
| 102 |
-
ref_image_dir = f"{ref_root_directory}/{ref_set}/{ref_camera_pos}-imgs/"
|
| 103 |
-
ref_utm_dir = f"{ref_root_directory}/{ref_set}/utm/"
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
this_ref_timestamp = [filename.split('.png')[0] for filename in natsorted(os.listdir(ref_image_dir)) if os.path.isfile(ref_image_dir+filename)]
|
| 107 |
-
ref_utms = ref_utms+[np.loadtxt(ref_utm_dir+filename) for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)][55::]
|
| 108 |
-
ref_img_filenames = [filename for filename in natsorted(os.listdir(ref_image_dir)) if os.path.isfile(ref_image_dir+filename)]
|
| 109 |
-
ref_utm_filenames = np.array([filename for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)])[:len(os.listdir(ref_utm_dir))-55]
|
| 110 |
-
ref_timestamps = ref_timestamps+this_ref_timestamp
|
| 111 |
-
|
| 112 |
-
ref_utms = np.array(ref_utms)
|
| 113 |
-
|
| 114 |
-
for vpr_desc in vpr_descs:
|
| 115 |
-
|
| 116 |
-
all_results = []
|
| 117 |
-
|
| 118 |
-
first = True
|
| 119 |
-
|
| 120 |
-
print(f"Loading references")
|
| 121 |
-
|
| 122 |
-
for ref_set in ref_sets:
|
| 123 |
-
print(f"Loading {ref_set} {vpr_desc} descriptors")
|
| 124 |
-
ref_root_directory = f"../Datasets/dalby/KITTI-style/{location}"
|
| 125 |
-
ref_vpr_root = f"../Datasets/dalby/KITTI-style/{location}/vpr_ftrs/"
|
| 126 |
-
|
| 127 |
-
ref_image_dir = f"{ref_root_directory}/{ref_set}/{ref_camera_pos}-imgs/"
|
| 128 |
-
|
| 129 |
-
ref_name_sort_idx = index_natsorted(os.listdir(ref_image_dir))
|
| 130 |
-
ref_ftr = np.load(f"{ref_vpr_root}/{ref_set}/{vpr_desc}/queries_descriptors.npy")
|
| 131 |
-
if first:
|
| 132 |
-
ref_ftrs = ref_ftr[ref_name_sort_idx]
|
| 133 |
-
first = False
|
| 134 |
-
else:
|
| 135 |
-
ref_ftrs = np.vstack((ref_ftrs, ref_ftr[ref_name_sort_idx]))
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
for qry_set in qry_sets:
|
| 139 |
-
|
| 140 |
-
################ Query filenames and directories #################################
|
| 141 |
-
qry_condition = ''
|
| 142 |
-
qry_camera_pos = 'front'
|
| 143 |
-
|
| 144 |
-
qry_root_directory = f"../Datasets/dalby/KITTI-style/{location}"
|
| 145 |
-
qry_vpr_root = f"../Datasets/dalby/KITTI-style/{location}/vpr_ftrs/"
|
| 146 |
-
qry_image_dir = f"{qry_root_directory}/{qry_set}/{qry_camera_pos}-imgs/"
|
| 147 |
-
qry_utm_dir = f"{qry_root_directory}/{qry_set}/utm/"
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
qry_timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(qry_image_dir)) if os.path.isfile(qry_image_dir+filename)]
|
| 151 |
-
qry_utms = np.array([np.loadtxt(qry_utm_dir+filename) for filename in natsorted(os.listdir(qry_utm_dir)) if os.path.isfile(qry_utm_dir+filename)])
|
| 152 |
-
qry_name_sort_idx = index_natsorted(os.listdir(qry_image_dir))
|
| 153 |
-
qry_ftrs = np.load(f"{qry_vpr_root}/{qry_set}/{vpr_desc}/queries_descriptors.npy")
|
| 154 |
-
qry_ftrs = qry_ftrs[qry_name_sort_idx]
|
| 155 |
-
|
| 156 |
-
mInds, dMat = getMatchIndsGPU(ref_ftrs,qry_ftrs,topK=1)
|
| 157 |
-
mInds = mInds.cpu().numpy()
|
| 158 |
-
in_tol = []
|
| 159 |
-
dists = []
|
| 160 |
-
valid_qry = 0
|
| 161 |
-
|
| 162 |
-
qry_utm_timestamps, qry_utm_idxs = utils.get_all_corr_files(qry_timestamps, [qry_utm_dir,])
|
| 163 |
-
ref_utm_timestamp, ref_utm_idxs = utils.get_all_corr_files(ref_timestamps, [ref_utm_dir,])
|
| 164 |
-
|
| 165 |
-
for qry_idx in tqdm(range(len(qry_timestamps))):
|
| 166 |
-
|
| 167 |
-
qry_image_timestamp = qry_timestamps[qry_idx]
|
| 168 |
-
qry_image_filename = f"{qry_image_dir}/{qry_image_timestamp}.png"
|
| 169 |
-
qry_utm = qry_utms[qry_utm_idxs[qry_idx]]
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
diffs = ref_utms - qry_utm # shape (N, 2)
|
| 173 |
-
qry_dists = np.linalg.norm(diffs, axis=1) # shape (N,)
|
| 174 |
-
if qry_dists.min() > dist_tolerance:
|
| 175 |
-
continue
|
| 176 |
-
else:
|
| 177 |
-
valid_qry += 1
|
| 178 |
-
|
| 179 |
-
ref_utm = ref_utms[ref_utm_idxs[int(mInds[qry_idx])]]
|
| 180 |
-
|
| 181 |
-
diff = ref_utm - qry_utm # shape (N, 2)
|
| 182 |
-
dist = np.linalg.norm(diff) # shape (N,)
|
| 183 |
-
dists.append(dist)
|
| 184 |
-
if dist < dist_tolerance:
|
| 185 |
-
in_tol.append(1)
|
| 186 |
-
else:
|
| 187 |
-
in_tol.append(0)
|
| 188 |
-
|
| 189 |
-
# qry_image = ImageData(qry_image_filename, img_calib_file)
|
| 190 |
-
|
| 191 |
-
# fig, ax = plt.subplots(1, 2, figsize=(19.4, 6))
|
| 192 |
-
# ax[0].clear()
|
| 193 |
-
# ax[1].clear()
|
| 194 |
-
|
| 195 |
-
# ax[0].imshow(qry_image.image[:, :, ::-1])
|
| 196 |
-
# ax[0].set_title(f"{qry_image_timestamp}.png")
|
| 197 |
-
# ax[0].axis("off")
|
| 198 |
-
|
| 199 |
-
# # Show matching reference image
|
| 200 |
-
# # ref_img_timestamp = utils.get_corr_files(ref_timestamps[int(mInds[qry_idx])], [ref_image_dir,])
|
| 201 |
-
# ref_image = ImageData(f"{ref_image_dir}/{ref_timestamps[int(mInds[qry_idx])]}.png", img_calib_file)
|
| 202 |
-
# ax[1].imshow(ref_image.image[:, :, ::-1])
|
| 203 |
-
# ax[1].set_title(f"{ref_timestamps[int(mInds[qry_idx])]}\nDist={dist:.2f}m")
|
| 204 |
-
|
| 205 |
-
# ax[1].axis("off")
|
| 206 |
-
# fig.canvas.draw()
|
| 207 |
-
|
| 208 |
-
print(f"Recall for {qry_set} using {vpr_desc}: {np.sum(np.array(in_tol))/valid_qry:.02%}")
|
| 209 |
-
all_results.append(np.sum(np.array(in_tol))/valid_qry)
|
| 210 |
-
# plt.figure()
|
| 211 |
-
# plt.plot(np.clip(dists, 0, 30))
|
| 212 |
-
# plt.ylim((0,35))
|
| 213 |
-
|
| 214 |
-
print(f"All {vpr_desc} results:")
|
| 215 |
-
print(all_results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localisation/VPR_eval.py
DELETED
|
@@ -1,199 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import matplotlib.pyplot as plt
|
| 3 |
-
import matplotlib.image as mpimg
|
| 4 |
-
import sys
|
| 5 |
-
sys.path.append(os.path.abspath(".")) # one level up
|
| 6 |
-
import numpy as np
|
| 7 |
-
import cv2
|
| 8 |
-
import open3d as o3d
|
| 9 |
-
from scipy.spatial.transform import Rotation
|
| 10 |
-
from utils.lidar import PointCloud
|
| 11 |
-
from utils.camera import ImageData
|
| 12 |
-
import utils.utils as utils
|
| 13 |
-
from natsort import natsorted, index_natsorted
|
| 14 |
-
import torch
|
| 15 |
-
from tqdm.notebook import tqdm
|
| 16 |
-
|
| 17 |
-
################## set device based on cuda availability #################
|
| 18 |
-
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
| 19 |
-
|
| 20 |
-
print('CUDA availability: ' + str(torch.cuda.is_available()))
|
| 21 |
-
|
| 22 |
-
####################### Functions for matching using numpy on CPU or Pytorch on GPU ###################
|
| 23 |
-
def getMatchIndsCPU(ft_ref,ft_qry,topK=20,metric='cosine'):
|
| 24 |
-
"""
|
| 25 |
-
metric: 'euclidean' or 'cosine'
|
| 26 |
-
"""
|
| 27 |
-
# dMat = cdist(ft_ref,ft_qry,metric)
|
| 28 |
-
|
| 29 |
-
ft_qry_norm = ft_qry / np.linalg.norm(ft_qry, axis=1, keepdims=True) # Shape (M, N)
|
| 30 |
-
ft_ref_norm = ft_ref / np.linalg.norm(ft_ref, axis=1, keepdims=True) # Shape (C, N)
|
| 31 |
-
|
| 32 |
-
# Step 2: Compute cosine similarity
|
| 33 |
-
dMat = 1 - (ft_ref_norm @ ft_qry_norm.T)
|
| 34 |
-
mInds = np.argsort(dMat,axis=0)[:topK].squeeze() # shape: K x ft_qry.shape[0]
|
| 35 |
-
return mInds, dMat
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def getMatchIndsGPU(ft_ref, ft_qry,topK=20, metric='cosine'):
|
| 39 |
-
# metric: 'euclidean' or 'cosine'
|
| 40 |
-
ft_qry_tensor = torch.Tensor(ft_qry).to(device)
|
| 41 |
-
ft_ref_tensor = torch.Tensor(ft_ref).to(device)
|
| 42 |
-
|
| 43 |
-
if metric == 'euclidean':
|
| 44 |
-
# Use torch's cdist for Euclidean distance
|
| 45 |
-
dMat = torch.cdist(ft_ref, ft_qry)
|
| 46 |
-
|
| 47 |
-
elif metric == 'cosine':
|
| 48 |
-
# # Normalize both the query and reference tensors
|
| 49 |
-
ft_qry_norm = ft_qry_tensor / ft_qry_tensor.norm(dim=1, keepdim=True)
|
| 50 |
-
ft_ref_norm = ft_ref_tensor / ft_ref_tensor.norm(dim=1, keepdim=True)
|
| 51 |
-
# Compute cosine similarity (1 - cosine similarity for distance)
|
| 52 |
-
dMat = 1 - ft_ref_norm @ ft_qry_norm.t()
|
| 53 |
-
|
| 54 |
-
# Get the indices of the top 5 closest matches
|
| 55 |
-
mInds = torch.argsort(dMat, dim=0)[:topK].squeeze()
|
| 56 |
-
|
| 57 |
-
return mInds, dMat
|
| 58 |
-
|
| 59 |
-
# User parameters
|
| 60 |
-
vpr_desc = 'salad'
|
| 61 |
-
# location = 'Holmview'
|
| 62 |
-
# location = 'Cambogan'
|
| 63 |
-
location = 'DairyCreek'
|
| 64 |
-
|
| 65 |
-
################ Query filenames and directories #################################
|
| 66 |
-
# qry_sequence = '20250820_130327'
|
| 67 |
-
# qry_sequence = '20250812_120856'
|
| 68 |
-
###
|
| 69 |
-
# qry_sequence = '20250811_113017'
|
| 70 |
-
# qry_sequence = '20250812_122621'
|
| 71 |
-
qry_condition = 'flooded'
|
| 72 |
-
# qry_sequence = '20250812_122339'
|
| 73 |
-
###
|
| 74 |
-
qry_sequence = '20250811_103318'
|
| 75 |
-
# qry_condition = 'dry'
|
| 76 |
-
qry_camera_pos = 'front'
|
| 77 |
-
qry_root_directory = f"../Datasets/FRED/{qry_condition}/KITTI-style"
|
| 78 |
-
qry_vpr_root = f"../Datasets/FRED/vpr_ftrs/{qry_condition}/KITTI-style"
|
| 79 |
-
|
| 80 |
-
qry_image_dir = f"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/"
|
| 81 |
-
qry_utm_dir = f"{qry_root_directory}/{location}_{qry_sequence}/utm/"
|
| 82 |
-
qry_timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(qry_image_dir)) if os.path.isfile(qry_image_dir+filename)]
|
| 83 |
-
qry_name_sort_idx = index_natsorted(os.listdir(qry_image_dir))
|
| 84 |
-
qry_ftrs = np.load(f"{qry_vpr_root}/{location}_{qry_sequence}/{vpr_desc}/queries_descriptors.npy")
|
| 85 |
-
qry_ftrs = qry_ftrs[qry_name_sort_idx]
|
| 86 |
-
|
| 87 |
-
################ Reference filenames and directories #################################
|
| 88 |
-
# ref_sequence = '20250812_120100'
|
| 89 |
-
# 20250812_120856
|
| 90 |
-
# ref_sequence = '20250812_122339'
|
| 91 |
-
# 20250812_122621
|
| 92 |
-
if location == 'DairyCreek': location = 'Dairy-Creek'
|
| 93 |
-
ref_sequence = '20250812_122954'
|
| 94 |
-
ref_condition = 'dry'
|
| 95 |
-
ref_camera_pos = 'front'
|
| 96 |
-
ref_root_directory = f"../Datasets/FRED/{ref_condition}/KITTI-style"
|
| 97 |
-
ref_vpr_root = f"../Datasets/FRED/vpr_ftrs/{ref_condition}/KITTI-style"
|
| 98 |
-
|
| 99 |
-
ref_image_dir = f"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/"
|
| 100 |
-
ref_utm_dir = f"{ref_root_directory}/{location}_{ref_sequence}/utm/"
|
| 101 |
-
ref_timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(ref_image_dir)) if os.path.isfile(ref_image_dir+filename)]
|
| 102 |
-
ref_name_sort_idx = index_natsorted(os.listdir(ref_image_dir))
|
| 103 |
-
ref_utms = np.array([np.loadtxt(ref_utm_dir+filename) for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)])
|
| 104 |
-
print(ref_utms.shape)
|
| 105 |
-
ref_img_filenames = [filename for filename in natsorted(os.listdir(ref_image_dir)) if os.path.isfile(ref_image_dir+filename)]
|
| 106 |
-
ref_utm_filenames = np.array([filename for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)])
|
| 107 |
-
ref_ftrs = np.load(f"{ref_vpr_root}/{location}_{ref_sequence}/{vpr_desc}/queries_descriptors.npy")
|
| 108 |
-
ref_ftrs = ref_ftrs[ref_name_sort_idx]
|
| 109 |
-
|
| 110 |
-
img_calib_file = f"./camera_calib.txt"
|
| 111 |
-
|
| 112 |
-
dist_tolerance = 10 # metres
|
| 113 |
-
# qry_idx = 4
|
| 114 |
-
|
| 115 |
-
mInds, dMat = getMatchIndsGPU(ref_ftrs,qry_ftrs,topK=1)
|
| 116 |
-
mInds = mInds.cpu().numpy()
|
| 117 |
-
in_tol = []
|
| 118 |
-
dists = []
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
plt.ion()
|
| 122 |
-
|
| 123 |
-
fig, ax = plt.subplots(1, 2, figsize=(19.4, 6))
|
| 124 |
-
|
| 125 |
-
# Dummy images to initialize artists
|
| 126 |
-
img0 = ax[0].imshow(np.zeros((10, 10, 3), dtype=np.uint8))
|
| 127 |
-
img1 = ax[1].imshow(np.zeros((10, 10, 3), dtype=np.uint8))
|
| 128 |
-
|
| 129 |
-
ax[0].axis("off")
|
| 130 |
-
ax[1].axis("off")
|
| 131 |
-
|
| 132 |
-
pressed_key = None
|
| 133 |
-
|
| 134 |
-
def on_key(event):
|
| 135 |
-
global pressed_key
|
| 136 |
-
pressed_key = event.key
|
| 137 |
-
|
| 138 |
-
cid = fig.canvas.mpl_connect("key_press_event", on_key)
|
| 139 |
-
|
| 140 |
-
for qry_idx in tqdm(range(len(qry_timestamps))):
|
| 141 |
-
|
| 142 |
-
pressed_key = None
|
| 143 |
-
|
| 144 |
-
qry_image_timestamp = qry_timestamps[qry_idx]
|
| 145 |
-
qry_image_filename = f"{qry_image_dir}/{qry_image_timestamp}.png"
|
| 146 |
-
qry_utm_timestamp = utils.get_corr_files(
|
| 147 |
-
qry_image_timestamp, [qry_utm_dir]
|
| 148 |
-
)
|
| 149 |
-
qry_utm = np.loadtxt(qry_utm_timestamp)
|
| 150 |
-
|
| 151 |
-
ref_utm_timestamp = utils.get_corr_files(
|
| 152 |
-
ref_timestamps[int(mInds[qry_idx])], [ref_utm_dir]
|
| 153 |
-
)
|
| 154 |
-
ref_utm = np.loadtxt(ref_utm_timestamp)
|
| 155 |
-
|
| 156 |
-
diff = ref_utm - qry_utm
|
| 157 |
-
dist = np.linalg.norm(diff)
|
| 158 |
-
|
| 159 |
-
dists.append(dist)
|
| 160 |
-
in_tol.append(1 if dist < dist_tolerance else 0)
|
| 161 |
-
|
| 162 |
-
# Load images
|
| 163 |
-
qry_image = ImageData(qry_image_filename, img_calib_file)
|
| 164 |
-
ref_image = ImageData(
|
| 165 |
-
f"{ref_image_dir}/{ref_timestamps[int(mInds[qry_idx])]}.png",
|
| 166 |
-
img_calib_file,
|
| 167 |
-
)
|
| 168 |
-
|
| 169 |
-
# Update image data (NO re-plotting)
|
| 170 |
-
img0.set_data(qry_image.image[:, :, ::-1])
|
| 171 |
-
img1.set_data(ref_image.image[:, :, ::-1])
|
| 172 |
-
|
| 173 |
-
ax[0].set_title(f"{qry_image_timestamp}.png")
|
| 174 |
-
ax[1].set_title(
|
| 175 |
-
f"{ref_timestamps[int(mInds[qry_idx])]}\nDist = {dist:.2f} m"
|
| 176 |
-
)
|
| 177 |
-
|
| 178 |
-
fig.canvas.draw_idle()
|
| 179 |
-
fig.canvas.flush_events()
|
| 180 |
-
|
| 181 |
-
# print("Press any key for next (mouse click also works)")
|
| 182 |
-
# key = plt.waitforbuttonpress()
|
| 183 |
-
|
| 184 |
-
# print("Press any key for next, 'q' to quit")
|
| 185 |
-
|
| 186 |
-
print("Any key = next | q = quit")
|
| 187 |
-
|
| 188 |
-
while pressed_key is None:
|
| 189 |
-
plt.pause(0.05)
|
| 190 |
-
|
| 191 |
-
if pressed_key == "q":
|
| 192 |
-
break
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
# print(f"Recall: {np.sum(np.array(in_tol))/len(qry_timestamps):.02%}")
|
| 196 |
-
# plt.figure()
|
| 197 |
-
# plt.plot(np.clip(dists, 0, 30))
|
| 198 |
-
# plt.ylim((0,35))
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localisation/create_VPR_eval_dataset.ipynb
DELETED
|
@@ -1,132 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "code",
|
| 5 |
-
"execution_count": 30,
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"outputs": [],
|
| 8 |
-
"source": [
|
| 9 |
-
"import os\n",
|
| 10 |
-
"import shutil\n",
|
| 11 |
-
"import matplotlib.pyplot as plt\n",
|
| 12 |
-
"import sys\n",
|
| 13 |
-
"from tqdm import tqdm_notebook as tqdm\n",
|
| 14 |
-
"sys.path.append(os.path.abspath(\"..\")) # one level up\n",
|
| 15 |
-
"import numpy as np\n",
|
| 16 |
-
"import utils.utils as utils\n",
|
| 17 |
-
"from natsort import natsorted\n",
|
| 18 |
-
"\n",
|
| 19 |
-
"cmap = plt.get_cmap(\"jet\")"
|
| 20 |
-
]
|
| 21 |
-
},
|
| 22 |
-
{
|
| 23 |
-
"cell_type": "code",
|
| 24 |
-
"execution_count": 31,
|
| 25 |
-
"metadata": {},
|
| 26 |
-
"outputs": [],
|
| 27 |
-
"source": [
|
| 28 |
-
"# User parameters\n",
|
| 29 |
-
"# location = 'Holmview'\n",
|
| 30 |
-
"location = 'Cambogan'\n",
|
| 31 |
-
"\n",
|
| 32 |
-
"################ Query filenames and directories #################################\n",
|
| 33 |
-
"# qry_sequence = '20250820_130327'\n",
|
| 34 |
-
"qry_sequence = '20250811_113017' # '20250812_122339' '20250811_113017'\n",
|
| 35 |
-
"qry_condition = 'flooded' # 'dry' 'flooded'\n",
|
| 36 |
-
"qry_camera_pos = 'front'\n",
|
| 37 |
-
"qry_root_directory = f\"../../Datasets/FRED/{qry_condition}/KITTI-style\"\n",
|
| 38 |
-
"\n",
|
| 39 |
-
"qry_image_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/\"\n",
|
| 40 |
-
"qry_utm_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/utm/\"\n",
|
| 41 |
-
"qry_timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(qry_image_dir)) if os.path.isfile(qry_image_dir+filename)]\n",
|
| 42 |
-
"\n",
|
| 43 |
-
"# ################ Reference filenames and directories #################################\n",
|
| 44 |
-
"# # ref_sequence = '20250812_120100'\n",
|
| 45 |
-
"# ref_sequence = '20250812_122339'\n",
|
| 46 |
-
"# ref_condition = 'dry'\n",
|
| 47 |
-
"# ref_camera_pos = 'front'\n",
|
| 48 |
-
"# ref_root_directory = f\"../Datasets/FRED/{ref_condition}/KITTI-style\"\n",
|
| 49 |
-
"\n",
|
| 50 |
-
"# ref_image_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/\"\n",
|
| 51 |
-
"# ref_utm_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/utm/\"\n",
|
| 52 |
-
"# ref_utms = np.array([np.loadtxt(ref_utm_dir+filename) for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)])\n",
|
| 53 |
-
"# ref_img_filenames = [filename for filename in natsorted(os.listdir(ref_image_dir)) if os.path.isfile(ref_image_dir+filename)]\n",
|
| 54 |
-
"# ref_utm_filenames = np.array([filename for filename in natsorted(os.listdir(ref_utm_dir)) if os.path.isfile(ref_utm_dir+filename)])\n",
|
| 55 |
-
"\n",
|
| 56 |
-
"img_calib_file = f\"./camera_calib.txt\"\n",
|
| 57 |
-
"\n",
|
| 58 |
-
"# dist_tolerance = 10 # metres\n",
|
| 59 |
-
"\n",
|
| 60 |
-
"save_folder = f\"../../Datasets/FRED/VPR-eval/{qry_condition}_{location}_{qry_sequence}/\""
|
| 61 |
-
]
|
| 62 |
-
},
|
| 63 |
-
{
|
| 64 |
-
"cell_type": "code",
|
| 65 |
-
"execution_count": 32,
|
| 66 |
-
"metadata": {},
|
| 67 |
-
"outputs": [
|
| 68 |
-
{
|
| 69 |
-
"name": "stderr",
|
| 70 |
-
"output_type": "stream",
|
| 71 |
-
"text": [
|
| 72 |
-
"C:\\Users\\malonecj\\AppData\\Local\\Temp\\ipykernel_30324\\3268383255.py:2: TqdmDeprecationWarning: This function will be removed in tqdm==5.0.0\n",
|
| 73 |
-
"Please use `tqdm.notebook.tqdm` instead of `tqdm.tqdm_notebook`\n",
|
| 74 |
-
" for i in tqdm(range(len(qry_timestamps))):\n"
|
| 75 |
-
]
|
| 76 |
-
},
|
| 77 |
-
{
|
| 78 |
-
"data": {
|
| 79 |
-
"application/vnd.jupyter.widget-view+json": {
|
| 80 |
-
"model_id": "93387ddb94d84df5adba819c16412c8f",
|
| 81 |
-
"version_major": 2,
|
| 82 |
-
"version_minor": 0
|
| 83 |
-
},
|
| 84 |
-
"text/plain": [
|
| 85 |
-
" 0%| | 0/243 [00:00<?, ?it/s]"
|
| 86 |
-
]
|
| 87 |
-
},
|
| 88 |
-
"metadata": {},
|
| 89 |
-
"output_type": "display_data"
|
| 90 |
-
}
|
| 91 |
-
],
|
| 92 |
-
"source": [
|
| 93 |
-
"os.makedirs(save_folder, exist_ok=True)\n",
|
| 94 |
-
"for i in tqdm(range(len(qry_timestamps))):\n",
|
| 95 |
-
" qry_image_timestamp = qry_timestamps[i]\n",
|
| 96 |
-
" # try:\n",
|
| 97 |
-
" qry_image_filename = f\"{qry_image_dir}/{qry_image_timestamp}.png\"\n",
|
| 98 |
-
" qry_utm_timestamp = utils.get_corr_files(qry_image_timestamp, [qry_utm_dir,])\n",
|
| 99 |
-
" qry_utm = np.loadtxt(qry_utm_timestamp)\n",
|
| 100 |
-
"\n",
|
| 101 |
-
" # qry_image = ImageData(qry_image_filename, img_calib_file)\n",
|
| 102 |
-
"\n",
|
| 103 |
-
" shutil.copy(qry_image_filename, f\"{save_folder}/@{qry_utm[0]}@{qry_utm[1]}@.png\")\n",
|
| 104 |
-
" \n",
|
| 105 |
-
"\n",
|
| 106 |
-
"\n",
|
| 107 |
-
" "
|
| 108 |
-
]
|
| 109 |
-
}
|
| 110 |
-
],
|
| 111 |
-
"metadata": {
|
| 112 |
-
"kernelspec": {
|
| 113 |
-
"display_name": "CARRSQ",
|
| 114 |
-
"language": "python",
|
| 115 |
-
"name": "python3"
|
| 116 |
-
},
|
| 117 |
-
"language_info": {
|
| 118 |
-
"codemirror_mode": {
|
| 119 |
-
"name": "ipython",
|
| 120 |
-
"version": 3
|
| 121 |
-
},
|
| 122 |
-
"file_extension": ".py",
|
| 123 |
-
"mimetype": "text/x-python",
|
| 124 |
-
"name": "python",
|
| 125 |
-
"nbconvert_exporter": "python",
|
| 126 |
-
"pygments_lexer": "ipython3",
|
| 127 |
-
"version": "3.10.15"
|
| 128 |
-
}
|
| 129 |
-
},
|
| 130 |
-
"nbformat": 4,
|
| 131 |
-
"nbformat_minor": 2
|
| 132 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localisation/groundtruth_utm-single.ipynb
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
localisation/groundtruth_utm_checker-all.py
CHANGED
|
@@ -23,7 +23,7 @@ location = 'Cambogan'
|
|
| 23 |
qry_sequence = '20250811_113017'
|
| 24 |
qry_condition = 'flooded'
|
| 25 |
qry_camera_pos = 'front'
|
| 26 |
-
qry_root_directory = f"
|
| 27 |
|
| 28 |
qry_image_dir = f"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/"
|
| 29 |
qry_utm_dir = f"{qry_root_directory}/{location}_{qry_sequence}/utm/"
|
|
@@ -34,7 +34,7 @@ qry_timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir
|
|
| 34 |
ref_sequence = '20250812_122339'
|
| 35 |
ref_condition = 'dry'
|
| 36 |
ref_camera_pos = 'front'
|
| 37 |
-
ref_root_directory = f"
|
| 38 |
|
| 39 |
ref_image_dir = f"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/"
|
| 40 |
ref_utm_dir = f"{ref_root_directory}/{location}_{ref_sequence}/utm/"
|
|
@@ -49,7 +49,8 @@ dist_tolerance = 10 # metres
|
|
| 49 |
|
| 50 |
fig, ax = plt.subplots(1, 2, figsize=(19.4, 6))
|
| 51 |
# idx = [0] # mutable index
|
| 52 |
-
idx = [183]
|
|
|
|
| 53 |
|
| 54 |
def show_image(i):
|
| 55 |
ax[0].clear()
|
|
@@ -93,7 +94,7 @@ def show_image(i):
|
|
| 93 |
ax[1].set_title(f"No Match (min dist={closest_dist:.2f}m)")
|
| 94 |
|
| 95 |
ax[1].axis("off")
|
| 96 |
-
plt.savefig('paper_figures/localization_check.pdf', format="pdf", bbox_inches='tight')
|
| 97 |
fig.canvas.draw()
|
| 98 |
|
| 99 |
def on_key(event):
|
|
|
|
| 23 |
qry_sequence = '20250811_113017'
|
| 24 |
qry_condition = 'flooded'
|
| 25 |
qry_camera_pos = 'front'
|
| 26 |
+
qry_root_directory = f"D:/Datasets/FRED/{qry_condition}/KITTI-style"
|
| 27 |
|
| 28 |
qry_image_dir = f"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/"
|
| 29 |
qry_utm_dir = f"{qry_root_directory}/{location}_{qry_sequence}/utm/"
|
|
|
|
| 34 |
ref_sequence = '20250812_122339'
|
| 35 |
ref_condition = 'dry'
|
| 36 |
ref_camera_pos = 'front'
|
| 37 |
+
ref_root_directory = f"D:/Datasets/FRED/{ref_condition}/KITTI-style"
|
| 38 |
|
| 39 |
ref_image_dir = f"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/"
|
| 40 |
ref_utm_dir = f"{ref_root_directory}/{location}_{ref_sequence}/utm/"
|
|
|
|
| 49 |
|
| 50 |
fig, ax = plt.subplots(1, 2, figsize=(19.4, 6))
|
| 51 |
# idx = [0] # mutable index
|
| 52 |
+
# idx = [183]
|
| 53 |
+
idx = [130]-
|
| 54 |
|
| 55 |
def show_image(i):
|
| 56 |
ax[0].clear()
|
|
|
|
| 94 |
ax[1].set_title(f"No Match (min dist={closest_dist:.2f}m)")
|
| 95 |
|
| 96 |
ax[1].axis("off")
|
| 97 |
+
# plt.savefig('paper_figures/localization_check.pdf', format="pdf", bbox_inches='tight')
|
| 98 |
fig.canvas.draw()
|
| 99 |
|
| 100 |
def on_key(event):
|
localisation/plot_utm_traj.ipynb
CHANGED
|
@@ -2,9 +2,19 @@
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "code",
|
| 5 |
-
"execution_count":
|
| 6 |
"metadata": {},
|
| 7 |
-
"outputs": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
"source": [
|
| 9 |
"import os\n",
|
| 10 |
"import matplotlib.pyplot as plt\n",
|
|
@@ -25,7 +35,7 @@
|
|
| 25 |
},
|
| 26 |
{
|
| 27 |
"cell_type": "code",
|
| 28 |
-
"execution_count":
|
| 29 |
"metadata": {},
|
| 30 |
"outputs": [
|
| 31 |
{
|
|
@@ -48,7 +58,8 @@
|
|
| 48 |
"qry_sequence = '20250811_113017'\n",
|
| 49 |
"qry_condition = 'flooded'\n",
|
| 50 |
"qry_camera_pos = 'front'\n",
|
| 51 |
-
"qry_root_directory = f\"../../Datasets/FRED/{qry_condition}/KITTI-style\"\n",
|
|
|
|
| 52 |
"\n",
|
| 53 |
"qry_image_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/\"\n",
|
| 54 |
"qry_utm_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/utm/\"\n",
|
|
@@ -60,7 +71,8 @@
|
|
| 60 |
"ref_sequence = '20250812_122339'\n",
|
| 61 |
"ref_condition = 'dry'\n",
|
| 62 |
"ref_camera_pos = 'front'\n",
|
| 63 |
-
"ref_root_directory = f\"../../Datasets/FRED/{ref_condition}/KITTI-style\"\n",
|
|
|
|
| 64 |
"\n",
|
| 65 |
"ref_image_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/\"\n",
|
| 66 |
"ref_utm_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/utm/\"\n",
|
|
@@ -76,12 +88,22 @@
|
|
| 76 |
},
|
| 77 |
{
|
| 78 |
"cell_type": "code",
|
| 79 |
-
"execution_count":
|
| 80 |
"metadata": {},
|
| 81 |
"outputs": [
|
| 82 |
{
|
| 83 |
"data": {
|
| 84 |
-
"image/png": "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",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
"text/plain": [
|
| 86 |
"<Figure size 1200x400 with 1 Axes>"
|
| 87 |
]
|
|
@@ -94,13 +116,13 @@
|
|
| 94 |
"plt.figure(figsize=(12,4))\n",
|
| 95 |
"plt.plot(qry_utms[:,0]-qry_utms[0,0], qry_utms[:,1]-qry_utms[0,1])\n",
|
| 96 |
"plt.plot(ref_utms[:end_ref,0]-qry_utms[0,0], ref_utms[:end_ref,1]-qry_utms[0,1], '--')\n",
|
| 97 |
-
"plt.savefig('../paper_figures/trajectory_plot.pdf', format=\"pdf\", bbox_inches='tight')"
|
| 98 |
]
|
| 99 |
}
|
| 100 |
],
|
| 101 |
"metadata": {
|
| 102 |
"kernelspec": {
|
| 103 |
-
"display_name": "
|
| 104 |
"language": "python",
|
| 105 |
"name": "python3"
|
| 106 |
},
|
|
@@ -114,7 +136,7 @@
|
|
| 114 |
"name": "python",
|
| 115 |
"nbconvert_exporter": "python",
|
| 116 |
"pygments_lexer": "ipython3",
|
| 117 |
-
"version": "3.10.
|
| 118 |
}
|
| 119 |
},
|
| 120 |
"nbformat": 4,
|
|
|
|
| 2 |
"cells": [
|
| 3 |
{
|
| 4 |
"cell_type": "code",
|
| 5 |
+
"execution_count": 1,
|
| 6 |
"metadata": {},
|
| 7 |
+
"outputs": [
|
| 8 |
+
{
|
| 9 |
+
"name": "stdout",
|
| 10 |
+
"output_type": "stream",
|
| 11 |
+
"text": [
|
| 12 |
+
"Jupyter environment detected. Enabling Open3D WebVisualizer.\n",
|
| 13 |
+
"[Open3D INFO] WebRTC GUI backend enabled.\n",
|
| 14 |
+
"[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.\n"
|
| 15 |
+
]
|
| 16 |
+
}
|
| 17 |
+
],
|
| 18 |
"source": [
|
| 19 |
"import os\n",
|
| 20 |
"import matplotlib.pyplot as plt\n",
|
|
|
|
| 35 |
},
|
| 36 |
{
|
| 37 |
"cell_type": "code",
|
| 38 |
+
"execution_count": 2,
|
| 39 |
"metadata": {},
|
| 40 |
"outputs": [
|
| 41 |
{
|
|
|
|
| 58 |
"qry_sequence = '20250811_113017'\n",
|
| 59 |
"qry_condition = 'flooded'\n",
|
| 60 |
"qry_camera_pos = 'front'\n",
|
| 61 |
+
"# qry_root_directory = f\"../../Datasets/FRED/{qry_condition}/KITTI-style\"\n",
|
| 62 |
+
"qry_root_directory = f\"U:/Research/Projects/KVFPRA9190/FRED/{qry_condition}/KITTI-style\"\n",
|
| 63 |
"\n",
|
| 64 |
"qry_image_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/{qry_camera_pos}-imgs/\"\n",
|
| 65 |
"qry_utm_dir = f\"{qry_root_directory}/{location}_{qry_sequence}/utm/\"\n",
|
|
|
|
| 71 |
"ref_sequence = '20250812_122339'\n",
|
| 72 |
"ref_condition = 'dry'\n",
|
| 73 |
"ref_camera_pos = 'front'\n",
|
| 74 |
+
"# ref_root_directory = f\"../../Datasets/FRED/{ref_condition}/KITTI-style\"\n",
|
| 75 |
+
"ref_root_directory = f\"U:/Research/Projects/KVFPRA9190/FRED/{ref_condition}/KITTI-style\"\n",
|
| 76 |
"\n",
|
| 77 |
"ref_image_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/{ref_camera_pos}-imgs/\"\n",
|
| 78 |
"ref_utm_dir = f\"{ref_root_directory}/{location}_{ref_sequence}/utm/\"\n",
|
|
|
|
| 88 |
},
|
| 89 |
{
|
| 90 |
"cell_type": "code",
|
| 91 |
+
"execution_count": 4,
|
| 92 |
"metadata": {},
|
| 93 |
"outputs": [
|
| 94 |
{
|
| 95 |
"data": {
|
| 96 |
+
"text/plain": [
|
| 97 |
+
"[<matplotlib.lines.Line2D at 0x22395cdf1c0>]"
|
| 98 |
+
]
|
| 99 |
+
},
|
| 100 |
+
"execution_count": 4,
|
| 101 |
+
"metadata": {},
|
| 102 |
+
"output_type": "execute_result"
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"data": {
|
| 106 |
+
"image/png": "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",
|
| 107 |
"text/plain": [
|
| 108 |
"<Figure size 1200x400 with 1 Axes>"
|
| 109 |
]
|
|
|
|
| 116 |
"plt.figure(figsize=(12,4))\n",
|
| 117 |
"plt.plot(qry_utms[:,0]-qry_utms[0,0], qry_utms[:,1]-qry_utms[0,1])\n",
|
| 118 |
"plt.plot(ref_utms[:end_ref,0]-qry_utms[0,0], ref_utms[:end_ref,1]-qry_utms[0,1], '--')\n",
|
| 119 |
+
"# plt.savefig('../paper_figures/trajectory_plot.pdf', format=\"pdf\", bbox_inches='tight')"
|
| 120 |
]
|
| 121 |
}
|
| 122 |
],
|
| 123 |
"metadata": {
|
| 124 |
"kernelspec": {
|
| 125 |
+
"display_name": "CADRRA",
|
| 126 |
"language": "python",
|
| 127 |
"name": "python3"
|
| 128 |
},
|
|
|
|
| 136 |
"name": "python",
|
| 137 |
"nbconvert_exporter": "python",
|
| 138 |
"pygments_lexer": "ipython3",
|
| 139 |
+
"version": "3.10.14"
|
| 140 |
}
|
| 141 |
},
|
| 142 |
"nbformat": 4,
|
segmentation/show_labels-all.py
CHANGED
|
@@ -26,7 +26,7 @@ parser.add_argument("--location", type=str, default="Cambogan", help="Location n
|
|
| 26 |
parser.add_argument("--sequence", type=str, default="20250811_113017", help="Sequence ID (e.g., 20250811_113017)")
|
| 27 |
parser.add_argument("--condition", type=str, default="flooded", help="Condition (e.g., flooded)")
|
| 28 |
parser.add_argument("--camera_pos", type=str, default="front", help="Camera position (e.g., front)")
|
| 29 |
-
parser.add_argument("--root", type=str, default="
|
| 30 |
parser.add_argument("--img_calib_file", type=str, default="./camera_calib.txt", help="Path to camera calibration file (e.g., ./camera_calib.txt)")
|
| 31 |
|
| 32 |
args = parser.parse_args()
|
|
@@ -75,7 +75,7 @@ else:
|
|
| 75 |
# sequence = '20241217_113410'
|
| 76 |
# condition = 'flooded'
|
| 77 |
# camera_pos = 'front'
|
| 78 |
-
# root_directory = f"
|
| 79 |
# # 01000000
|
| 80 |
|
| 81 |
############ Define filenames and directories ####################################
|
|
@@ -87,7 +87,8 @@ timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(ima
|
|
| 87 |
|
| 88 |
fig, ax = plt.subplots(figsize=(12.8, 8))
|
| 89 |
# idx = [0] # mutable index
|
| 90 |
-
idx = [183] # mutable index
|
|
|
|
| 91 |
|
| 92 |
def show_image(i):
|
| 93 |
ax.clear()
|
|
|
|
| 26 |
parser.add_argument("--sequence", type=str, default="20250811_113017", help="Sequence ID (e.g., 20250811_113017)")
|
| 27 |
parser.add_argument("--condition", type=str, default="flooded", help="Condition (e.g., flooded)")
|
| 28 |
parser.add_argument("--camera_pos", type=str, default="front", help="Camera position (e.g., front)")
|
| 29 |
+
parser.add_argument("--root", type=str, default="D:/Datasets/FRED/", help="Root dataset directory (e.g., D:/Datasets/FRED/)")
|
| 30 |
parser.add_argument("--img_calib_file", type=str, default="./camera_calib.txt", help="Path to camera calibration file (e.g., ./camera_calib.txt)")
|
| 31 |
|
| 32 |
args = parser.parse_args()
|
|
|
|
| 75 |
# sequence = '20241217_113410'
|
| 76 |
# condition = 'flooded'
|
| 77 |
# camera_pos = 'front'
|
| 78 |
+
# root_directory = f"D:/Datasets/FRED/{condition}/KITTI-style"
|
| 79 |
# # 01000000
|
| 80 |
|
| 81 |
############ Define filenames and directories ####################################
|
|
|
|
| 87 |
|
| 88 |
fig, ax = plt.subplots(figsize=(12.8, 8))
|
| 89 |
# idx = [0] # mutable index
|
| 90 |
+
# idx = [183] # mutable index
|
| 91 |
+
idx = [130]
|
| 92 |
|
| 93 |
def show_image(i):
|
| 94 |
ax.clear()
|
utils/camera.py
CHANGED
|
@@ -100,7 +100,7 @@ class ImageData():
|
|
| 100 |
print(f"ERROR in create_camera_matrix: {e}")
|
| 101 |
return None
|
| 102 |
|
| 103 |
-
def project_points(self, points, colours, cmap, valid_cam, colour_norm=None):
|
| 104 |
# , beam_id, azimuth
|
| 105 |
# Project to image coordinates
|
| 106 |
rvec = np.zeros(3) # No additional rotation
|
|
@@ -122,14 +122,36 @@ class ImageData():
|
|
| 122 |
else:
|
| 123 |
colour2project = colours[valid_mask]/colour_norm
|
| 124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
img_vis = self.image.copy()
|
|
|
|
| 126 |
# Draw points
|
| 127 |
for (point, c) in zip(points2project.astype(int), colour2project):
|
| 128 |
r, g, b, _ = cmap(c)
|
| 129 |
colour = (r*255, g*255, b*255)
|
| 130 |
cv2.circle(img_vis, (int(point[0]), int(point[1])), 5, colour, -1) # -1 = filled circle
|
|
|
|
| 131 |
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
def get_image_coords(self, points):
|
| 135 |
# Project to image coordinates
|
|
|
|
| 100 |
print(f"ERROR in create_camera_matrix: {e}")
|
| 101 |
return None
|
| 102 |
|
| 103 |
+
def project_points(self, points, colours, cmap, valid_cam, colour_norm=None, semantic_label=False):
|
| 104 |
# , beam_id, azimuth
|
| 105 |
# Project to image coordinates
|
| 106 |
rvec = np.zeros(3) # No additional rotation
|
|
|
|
| 122 |
else:
|
| 123 |
colour2project = colours[valid_mask]/colour_norm
|
| 124 |
|
| 125 |
+
if semantic_label:
|
| 126 |
+
# Build meta_points: x, y, z, intensity, u, v, semantic_label
|
| 127 |
+
valid_3d = points[valid_mask] # (N, 3) xyz in camera frame
|
| 128 |
+
valid_intensity = colours[valid_mask] # (N,) intensity
|
| 129 |
+
valid_uv = points2project # (N, 2) pixel coords
|
| 130 |
+
valid_labels = np.array([
|
| 131 |
+
self.label_img[int(pt[1]), int(pt[0])]
|
| 132 |
+
for pt in valid_uv
|
| 133 |
+
])
|
| 134 |
+
|
| 135 |
+
meta_points = np.column_stack([
|
| 136 |
+
valid_3d, # x, y, z
|
| 137 |
+
valid_intensity, # intensity
|
| 138 |
+
valid_uv, # u, v
|
| 139 |
+
valid_labels # semantic label
|
| 140 |
+
])
|
| 141 |
+
|
| 142 |
img_vis = self.image.copy()
|
| 143 |
+
mask_img = np.zeros((self.image.shape[0], self.image.shape[1]))
|
| 144 |
# Draw points
|
| 145 |
for (point, c) in zip(points2project.astype(int), colour2project):
|
| 146 |
r, g, b, _ = cmap(c)
|
| 147 |
colour = (r*255, g*255, b*255)
|
| 148 |
cv2.circle(img_vis, (int(point[0]), int(point[1])), 5, colour, -1) # -1 = filled circle
|
| 149 |
+
cv2.circle(mask_img, (int(point[0]), int(point[1])), 10, 255, -1)
|
| 150 |
|
| 151 |
+
if semantic_label:
|
| 152 |
+
return img_vis, image_points, valid_mask, mask_img, meta_points
|
| 153 |
+
else:
|
| 154 |
+
return img_vis, image_points, valid_mask, mask_img
|
| 155 |
|
| 156 |
def get_image_coords(self, points):
|
| 157 |
# Project to image coordinates
|
utils/lidar.py
CHANGED
|
@@ -112,7 +112,7 @@ class PointCloud:
|
|
| 112 |
|
| 113 |
return pts_cam_h[valid,:], distances, points[valid,3]
|
| 114 |
|
| 115 |
-
def destagger(self
|
| 116 |
|
| 117 |
pixel_shift_by_row = np.array(self.pixel_shift_by_row)
|
| 118 |
|
|
@@ -135,9 +135,9 @@ class PointCloud:
|
|
| 135 |
|
| 136 |
|
| 137 |
# --- reshape ---
|
| 138 |
-
pc_img = points.reshape(W, H, 4).transpose(1, 0, 2) # (64, 1024, 4)
|
| 139 |
-
lbl_img =
|
| 140 |
-
inlier_img =
|
| 141 |
|
| 142 |
rows = np.arange(H)[:, None]
|
| 143 |
cols = np.arange(W)[None, :]
|
|
|
|
| 112 |
|
| 113 |
return pts_cam_h[valid,:], distances, points[valid,3]
|
| 114 |
|
| 115 |
+
def destagger(self):
|
| 116 |
|
| 117 |
pixel_shift_by_row = np.array(self.pixel_shift_by_row)
|
| 118 |
|
|
|
|
| 135 |
|
| 136 |
|
| 137 |
# --- reshape ---
|
| 138 |
+
pc_img = self.points.reshape(W, H, 4).transpose(1, 0, 2) # (64, 1024, 4)
|
| 139 |
+
lbl_img = self.ground_semantic.reshape(W, H).T # (64, 1024)
|
| 140 |
+
inlier_img = self.ground_inlier.reshape(W, H).T
|
| 141 |
|
| 142 |
rows = np.arange(H)[:, None]
|
| 143 |
cols = np.arange(W)[None, :]
|
visualisation/convert_imgs2video.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
images_to_mp4.py — Combine a directory of images into an MP4 video.
|
| 4 |
-
|
| 5 |
-
Usage:
|
| 6 |
-
python images_to_mp4.py <image_dir> [options]
|
| 7 |
-
|
| 8 |
-
Options:
|
| 9 |
-
--fps FPS Frames per second (default: 10)
|
| 10 |
-
--output OUTPUT Output file path (default: output.mp4)
|
| 11 |
-
--pattern PATTERN Glob pattern for images (default: auto-detect)
|
| 12 |
-
--sort {name,time} Sort order for frames (default: name)
|
| 13 |
-
"""
|
| 14 |
-
|
| 15 |
-
import argparse
|
| 16 |
-
import sys
|
| 17 |
-
from pathlib import Path
|
| 18 |
-
from natsort import natsorted
|
| 19 |
-
import cv2
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".webp"}
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def collect_images(directory: Path, pattern: str | None, sort: str) -> list[Path]:
|
| 26 |
-
if pattern:
|
| 27 |
-
images = sorted(directory.glob(pattern))
|
| 28 |
-
else:
|
| 29 |
-
images = [
|
| 30 |
-
p for p in directory.iterdir()
|
| 31 |
-
if p.suffix.lower() in SUPPORTED_EXTENSIONS
|
| 32 |
-
]
|
| 33 |
-
if sort == "time":
|
| 34 |
-
images.sort(key=lambda p: p.stat().st_mtime)
|
| 35 |
-
else:
|
| 36 |
-
images.sort(key=lambda p: p.name)
|
| 37 |
-
|
| 38 |
-
return natsorted(images)
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def build_video(images: list[Path], output: Path, fps: int) -> None:
|
| 42 |
-
# Read the first frame to get dimensions
|
| 43 |
-
first = cv2.imread(str(images[0]))
|
| 44 |
-
if first is None:
|
| 45 |
-
sys.exit(f"Error: could not read image '{images[0]}'")
|
| 46 |
-
|
| 47 |
-
height, width = first.shape[:2]
|
| 48 |
-
print(f"Frame size : {width}x{height}")
|
| 49 |
-
print(f"Frame count: {len(images)}")
|
| 50 |
-
print(f"FPS : {fps}")
|
| 51 |
-
print(f"Output : {output}")
|
| 52 |
-
|
| 53 |
-
fourcc = cv2.VideoWriter_fourcc(*"avc1") # H.264
|
| 54 |
-
writer = cv2.VideoWriter(str(output), fourcc, fps, (width, height))
|
| 55 |
-
|
| 56 |
-
if not writer.isOpened():
|
| 57 |
-
# Fallback: try mp4v codec if avc1 unavailable
|
| 58 |
-
print("Warning: avc1 (H.264) unavailable, falling back to mp4v codec.")
|
| 59 |
-
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 60 |
-
writer = cv2.VideoWriter(str(output), fourcc, fps, (width, height))
|
| 61 |
-
|
| 62 |
-
if not writer.isOpened():
|
| 63 |
-
sys.exit("Error: could not open VideoWriter. Check OpenCV build & codecs.")
|
| 64 |
-
|
| 65 |
-
for i, path in enumerate(images, 1):
|
| 66 |
-
frame = cv2.imread(str(path))
|
| 67 |
-
if frame is None:
|
| 68 |
-
print(f" Warning: skipping unreadable file '{path.name}'")
|
| 69 |
-
continue
|
| 70 |
-
# Resize if a frame differs from the first frame's dimensions
|
| 71 |
-
if (frame.shape[1], frame.shape[0]) != (width, height):
|
| 72 |
-
frame = cv2.resize(frame, (width, height))
|
| 73 |
-
writer.write(frame)
|
| 74 |
-
if i % 50 == 0 or i == len(images):
|
| 75 |
-
print(f" Encoded {i}/{len(images)} frames...")
|
| 76 |
-
|
| 77 |
-
writer.release()
|
| 78 |
-
print(f"\nDone! Video saved to: {output}")
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def main():
|
| 82 |
-
parser = argparse.ArgumentParser(
|
| 83 |
-
description="Combine images in a directory into an MP4 video."
|
| 84 |
-
)
|
| 85 |
-
parser.add_argument("image_dir", help="Path to directory containing images")
|
| 86 |
-
parser.add_argument("--fps", type=int, default=10, help="Frames per second (default: 10)")
|
| 87 |
-
parser.add_argument("--output", default="output.mp4", help="Output video file (default: output.mp4)")
|
| 88 |
-
parser.add_argument("--pattern", default=None, help="Glob pattern, e.g. 'frame_*.png'")
|
| 89 |
-
parser.add_argument("--sort", choices=["name", "time"], default="name",
|
| 90 |
-
help="Sort frames by name (default) or modification time")
|
| 91 |
-
args = parser.parse_args()
|
| 92 |
-
|
| 93 |
-
directory = Path(args.image_dir)
|
| 94 |
-
if not directory.is_dir():
|
| 95 |
-
sys.exit(f"Error: '{directory}' is not a valid directory.")
|
| 96 |
-
|
| 97 |
-
images = collect_images(directory, args.pattern, args.sort)
|
| 98 |
-
if not images:
|
| 99 |
-
sys.exit(f"Error: no supported images found in '{directory}'.")
|
| 100 |
-
|
| 101 |
-
print(f"Found {len(images)} image(s) in '{directory}'")
|
| 102 |
-
build_video(images, Path(args.output), args.fps)
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
if __name__ == "__main__":
|
| 106 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
visualisation/points2image-all.py
CHANGED
|
@@ -15,17 +15,22 @@ from natsort import natsorted
|
|
| 15 |
cmap = plt.get_cmap("jet")
|
| 16 |
|
| 17 |
# User parameters
|
| 18 |
-
location = 'Cambogan'
|
| 19 |
-
sequence = '20250811_113017'
|
| 20 |
-
# sequence = '20250812_122101'
|
| 21 |
# location = 'Holmview'
|
| 22 |
# sequence = '20250820_130327'
|
| 23 |
# location = 'Mount-Cotton'
|
| 24 |
# sequence = '20241217_113410'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
condition = 'flooded'
|
| 26 |
# condition = 'dry'
|
| 27 |
camera_pos = 'front'
|
| 28 |
-
root_directory = f"
|
|
|
|
| 29 |
# 01000000
|
| 30 |
|
| 31 |
############ Define filenames and directories ####################################
|
|
@@ -43,7 +48,8 @@ timestamps = [filename.split('.png')[0] for filename in natsorted(os.listdir(ima
|
|
| 43 |
|
| 44 |
fig, ax = plt.subplots(figsize=(12.8, 8))
|
| 45 |
# idx = [0] # mutable index
|
| 46 |
-
idx = [183]
|
|
|
|
| 47 |
|
| 48 |
def show_image(i):
|
| 49 |
ax.clear()
|
|
@@ -60,7 +66,11 @@ def show_image(i):
|
|
| 60 |
|
| 61 |
|
| 62 |
point_cam, distances_cam, intensities_cam, all_points_cam, valid_cam = pointcloud.points_ouster_to_cam() #, beam_id, azimuth
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
ax.imshow(img_vis[:, :, ::-1])
|
| 66 |
ax.set_title(f"{image_timestamp}.png")
|
|
|
|
| 15 |
cmap = plt.get_cmap("jet")
|
| 16 |
|
| 17 |
# User parameters
|
| 18 |
+
# location = 'Cambogan'
|
| 19 |
+
# sequence = '20250811_113017'
|
| 20 |
+
# sequence = '20250812_122339' #'20250812_122101'
|
| 21 |
# location = 'Holmview'
|
| 22 |
# sequence = '20250820_130327'
|
| 23 |
# location = 'Mount-Cotton'
|
| 24 |
# sequence = '20241217_113410'
|
| 25 |
+
# location = 'Pullenvale'
|
| 26 |
+
# sequence = '20250916_124105'
|
| 27 |
+
location = 'DairyCreek'
|
| 28 |
+
sequence = '20250811_103318'
|
| 29 |
condition = 'flooded'
|
| 30 |
# condition = 'dry'
|
| 31 |
camera_pos = 'front'
|
| 32 |
+
# root_directory = f"C:/Users/conno/Documents/data/FRED/{condition}/KITTI-style/" #f"D:/Datasets/FRED/{condition}/KITTI-style"
|
| 33 |
+
root_directory = f"U:/Research/Projects/KVFPRA9190/FRED/{condition}/KITTI-style"
|
| 34 |
# 01000000
|
| 35 |
|
| 36 |
############ Define filenames and directories ####################################
|
|
|
|
| 48 |
|
| 49 |
fig, ax = plt.subplots(figsize=(12.8, 8))
|
| 50 |
# idx = [0] # mutable index
|
| 51 |
+
# idx = [183]
|
| 52 |
+
idx = [200]
|
| 53 |
|
| 54 |
def show_image(i):
|
| 55 |
ax.clear()
|
|
|
|
| 66 |
|
| 67 |
|
| 68 |
point_cam, distances_cam, intensities_cam, all_points_cam, valid_cam = pointcloud.points_ouster_to_cam() #, beam_id, azimuth
|
| 69 |
+
|
| 70 |
+
p_high = np.percentile(intensities_cam, 99)
|
| 71 |
+
intensities_cam = np.clip(intensities_cam, 0, p_high) / p_high * 255
|
| 72 |
+
|
| 73 |
+
img_vis, _, _, _ = image.project_points(all_points_cam, intensities_cam, cmap, valid_cam, colour_norm=255) #, beam_id, azimuth
|
| 74 |
|
| 75 |
ax.imshow(img_vis[:, :, ::-1])
|
| 76 |
ax.set_title(f"{image_timestamp}.png")
|
visualisation/points2image-single.ipynb
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|