1+ #pragma once
2+
3+ #include < cpp-toolbox/io/dataloader/kitti_odometry_dataset.hpp>
4+ #include < cpp-toolbox/logger/thread_logger.hpp>
5+
6+ #include < filesystem>
7+ #include < algorithm>
8+
9+ namespace toolbox ::io {
10+
11+ template <typename DataType>
12+ kitti_odometry_dataset_t <DataType>::kitti_odometry_dataset_t (
13+ const std::string& sequence_path)
14+ : sequence_path_(sequence_path) {
15+
16+ namespace fs = std::filesystem;
17+
18+ // Validate directory structure
19+ if (!validate_kitti_sequence_directory (sequence_path)) {
20+ throw kitti_invalid_sequence (sequence_path);
21+ }
22+
23+ // Extract sequence name
24+ fs::path path (sequence_path);
25+ sequence_name_ = path.filename ().string ();
26+
27+ // Set paths
28+ velodyne_path_ = (path / " velodyne" ).string ();
29+
30+ // Scan for point cloud files
31+ scan_cloud_files ();
32+
33+ if (cloud_files_.empty ()) {
34+ throw kitti_invalid_sequence (" No point cloud files found in: " + velodyne_path_);
35+ }
36+
37+ // Load poses
38+ // Poses are typically in the parent directory structure
39+ fs::path poses_dir = path.parent_path ().parent_path () / " poses" ;
40+ std::string poses_file = (poses_dir / (sequence_name_ + " .txt" )).string ();
41+
42+ if (fs::exists (poses_file)) {
43+ load_poses (poses_file);
44+ } else {
45+ LOG_WARN_S << " No poses file found at: " << poses_file;
46+ }
47+
48+ // Load calibration
49+ load_calibration ();
50+
51+ LOG_INFO_S << " Loaded KITTI sequence " << sequence_name_
52+ << " with " << cloud_files_.size () << " frames" ;
53+ }
54+
55+ template <typename DataType>
56+ void kitti_odometry_dataset_t <DataType>::scan_cloud_files() {
57+ cloud_files_ = list_kitti_cloud_files (velodyne_path_);
58+
59+ // Verify files are properly numbered
60+ for (std::size_t i = 0 ; i < cloud_files_.size (); ++i) {
61+ int frame_idx = parse_kitti_frame_index (cloud_files_[i]);
62+ if (frame_idx != static_cast <int >(i)) {
63+ LOG_WARN_S << " Frame index mismatch: expected " << i
64+ << " but got " << frame_idx
65+ << " for file " << cloud_files_[i];
66+ }
67+ }
68+ }
69+
70+ template <typename DataType>
71+ void kitti_odometry_dataset_t <DataType>::load_poses(const std::string& poses_file) {
72+ if (!pose_reader_.load (poses_file)) {
73+ LOG_ERROR_S << " Failed to load poses from: " << poses_file;
74+ return ;
75+ }
76+
77+ // Verify pose count matches frame count
78+ if (pose_reader_.size () != cloud_files_.size ()) {
79+ LOG_WARN_S << " Pose count (" << pose_reader_.size ()
80+ << " ) does not match frame count (" << cloud_files_.size () << " )" ;
81+ }
82+ }
83+
84+ template <typename DataType>
85+ void kitti_odometry_dataset_t <DataType>::load_calibration() {
86+ namespace fs = std::filesystem;
87+
88+ fs::path calib_file = fs::path (sequence_path_) / " calib.txt" ;
89+
90+ if (fs::exists (calib_file)) {
91+ try {
92+ calibration_ = read_kitti_calibration<DataType>(calib_file.string ());
93+ LOG_INFO_S << " Loaded calibration from: " << calib_file;
94+ } catch (const std::exception& e) {
95+ LOG_WARN_S << " Failed to load calibration: " << e.what ();
96+ }
97+ }
98+ }
99+
100+ template <typename DataType>
101+ std::optional<kitti_odometry_frame_t <DataType>>
102+ kitti_odometry_dataset_t <DataType>::at_impl(std::size_t index) const {
103+
104+ if (index >= cloud_files_.size ()) {
105+ if (error_policy_ == error_recovery_policy_t ::strict) {
106+ throw kitti_index_out_of_range (index, cloud_files_.size ());
107+ }
108+ return std::nullopt ;
109+ }
110+
111+ // Check cache first
112+ auto cached = get_from_cache (index);
113+ if (cached) {
114+ return cached;
115+ }
116+
117+ // Load point cloud
118+ auto cloud = load_cloud (cloud_files_[index]);
119+ if (!cloud) {
120+ if (error_policy_ == error_recovery_policy_t ::strict) {
121+ throw kitti_corrupted_data (" Failed to load cloud at index " +
122+ std::to_string (index));
123+ }
124+ return std::nullopt ;
125+ }
126+
127+ // Create frame
128+ frame_type frame;
129+ frame.cloud = std::move (cloud);
130+ frame.frame_index = index;
131+
132+ // Get pose if available
133+ if (index < pose_reader_.size ()) {
134+ frame.pose = pose_reader_.get_pose (index);
135+ } else {
136+ // Use identity if no pose available
137+ frame.pose = Eigen::Matrix<DataType, 4 , 4 >::Identity ();
138+ if (error_policy_ != error_recovery_policy_t ::best_effort) {
139+ LOG_WARN_S << " No pose available for frame " << index;
140+ }
141+ }
142+
143+ // Set timestamp (frame index as string for now)
144+ frame.timestamp = format_kitti_frame_index (index);
145+
146+ // Update cache before returning
147+ update_cache (index, std::move (frame));
148+
149+ // Return from cache to avoid move issues
150+ return get_from_cache (index);
151+ }
152+
153+ template <typename DataType>
154+ std::unique_ptr<point_cloud_t <DataType>>
155+ kitti_odometry_dataset_t <DataType>::load_cloud(
156+ const std::string& file_path) const {
157+
158+ try {
159+ auto cloud = read_kitti_bin<DataType>(file_path);
160+
161+ if (!cloud) {
162+ LOG_ERROR_S << " Failed to read point cloud from: " << file_path;
163+ return nullptr ;
164+ }
165+
166+ // Optionally clear intensity if not needed
167+ if (!load_intensity_) {
168+ cloud->intensity = 0 ;
169+ }
170+
171+ return cloud;
172+
173+ } catch (const std::exception& e) {
174+ LOG_ERROR_S << " Exception loading point cloud from " << file_path
175+ << " : " << e.what ();
176+ return nullptr ;
177+ }
178+ }
179+
180+ template <typename DataType>
181+ void kitti_odometry_dataset_t <DataType>::update_cache(
182+ std::size_t index, frame_type&& frame) const {
183+
184+ // Remove from LRU list if already present
185+ auto it = std::find (lru_list_.begin (), lru_list_.end (), index);
186+ if (it != lru_list_.end ()) {
187+ lru_list_.erase (it);
188+ }
189+
190+ // Add to front of LRU list
191+ lru_list_.push_front (index);
192+
193+ // Insert/update in cache
194+ cache_[index] = std::move (frame);
195+
196+ // Evict oldest if cache is full
197+ while (cache_.size () > max_cache_size_ && !lru_list_.empty ()) {
198+ auto oldest = lru_list_.back ();
199+ cache_.erase (oldest);
200+ lru_list_.pop_back ();
201+ }
202+ }
203+
204+ template <typename DataType>
205+ std::optional<kitti_odometry_frame_t <DataType>>
206+ kitti_odometry_dataset_t <DataType>::get_from_cache(std::size_t index) const {
207+
208+ auto it = cache_.find (index);
209+ if (it != cache_.end ()) {
210+ // Move to front of LRU list
211+ auto lru_it = std::find (lru_list_.begin (), lru_list_.end (), index);
212+ if (lru_it != lru_list_.end ()) {
213+ lru_list_.erase (lru_it);
214+ }
215+ lru_list_.push_front (index);
216+
217+ // Return copy to maintain cache
218+ return it->second ;
219+ }
220+
221+ return std::nullopt ;
222+ }
223+
224+ } // namespace toolbox::io
0 commit comments