CMalone-Jupiter commited on
Commit
4490536
·
verified ·
1 Parent(s): b68dd83

Upload folder using huggingface_hub

Browse files
.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 = [183]
49
 
50
  def show_image(i):
51
- # ax.clear()
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
- point_cam, distances_cam, intensities_cam, all_points_cam, valid_cam = pointcloud.points_ouster_to_cam() #, beam_id, azimuth
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
- labels_norm = semantic_labels.astype(np.float64) / semantic_labels.max()
 
 
75
 
76
- colors = np.stack(
77
- (labels_norm, np.zeros(labels_norm.shape[0]), np.zeros(labels_norm.shape[0])),
78
- axis=1
79
- ) # shape (N, 3)
80
 
81
- pcd = o3d.geometry.PointCloud()
82
- pcd.points = o3d.utility.Vector3dVector(pointcloud.points[:,:3])
 
 
83
 
84
- pcd.colors = o3d.utility.Vector3dVector(colors)
85
- o3d.visualization.draw_geometries([pcd,])
86
 
87
- idx[0] += 1
 
 
 
 
 
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
- while idx[0] < len(timestamps):
97
- show_image(idx[0])
 
 
 
 
 
 
 
 
 
 
 
 
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"../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,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"../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,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": 30,
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": 31,
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": 33,
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": "CARRSQ",
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.15"
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="../Datasets/FRED/", help="Root dataset directory (e.g., ../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,7 +75,7 @@ else:
75
  # sequence = '20241217_113410'
76
  # condition = 'flooded'
77
  # camera_pos = 'front'
78
- # root_directory = f"../Datasets/FRED/{condition}/KITTI-style"
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
- return img_vis, image_points, valid_img_mask
 
 
 
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, points, ground_labels, inlier_labels):
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 = ground_labels.reshape(W, H).T # (64, 1024)
140
- inlier_img = inlier_labels.reshape(W, H).T
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"../Datasets/FRED/{condition}/KITTI-style"
 
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
- img_vis, _, _ = image.project_points(all_points_cam, distances_cam, cmap, valid_cam, colour_norm=None) #, beam_id, azimuth
 
 
 
 
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