2024-10-17 08:36:18 +02:00
|
|
|
from math import pi
|
|
|
|
|
from multiprocessing import Pool
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from sys import exit
|
|
|
|
|
from typing import Optional, Tuple
|
|
|
|
|
|
|
|
|
|
import matplotlib
|
|
|
|
|
import numpy as np
|
2024-06-11 11:07:05 +02:00
|
|
|
from configargparse import (
|
|
|
|
|
ArgParser,
|
|
|
|
|
ArgumentDefaultsRawHelpFormatter,
|
2024-10-17 08:36:18 +02:00
|
|
|
YAMLConfigFileParser,
|
2024-06-11 11:07:05 +02:00
|
|
|
)
|
2024-04-19 13:38:09 +02:00
|
|
|
from pandas import DataFrame
|
2024-10-17 08:36:18 +02:00
|
|
|
from pathvalidate import sanitize_filename
|
2024-04-19 13:38:09 +02:00
|
|
|
from PIL import Image
|
2024-10-17 08:36:18 +02:00
|
|
|
from pointcloudset import Dataset
|
|
|
|
|
from rich.progress import track
|
|
|
|
|
from rosbags.highlevel import AnyReader
|
2024-06-11 11:07:05 +02:00
|
|
|
|
|
|
|
|
matplotlib.use("Agg")
|
2024-04-19 13:38:09 +02:00
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
|
|
|
|
from util import (
|
2024-06-11 15:56:33 +02:00
|
|
|
angle,
|
|
|
|
|
angle_width,
|
2024-04-19 13:38:09 +02:00
|
|
|
calculate_average_frame_rate,
|
2024-10-17 08:36:18 +02:00
|
|
|
create_video_from_images,
|
|
|
|
|
existing_path,
|
2024-04-19 13:38:09 +02:00
|
|
|
get_colormap_with_special_missing_color,
|
2024-10-17 08:36:18 +02:00
|
|
|
load_dataset,
|
|
|
|
|
positive_int,
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2024-10-17 08:36:18 +02:00
|
|
|
def save_image_topics(
|
|
|
|
|
bag_file_path: Path, output_folder_path: Path, topic_names: list[str]
|
|
|
|
|
) -> list[Path]:
|
|
|
|
|
with AnyReader([bag_file_path]) as reader:
|
|
|
|
|
topic_paths = []
|
|
|
|
|
for topic_name in topic_names:
|
|
|
|
|
connections = [x for x in reader.connections if x.topic == topic_name]
|
|
|
|
|
frame_count = 0
|
|
|
|
|
topic_output_folder_path = output_folder_path / sanitize_filename(
|
|
|
|
|
topic_name
|
|
|
|
|
)
|
|
|
|
|
topic_output_folder_path.mkdir(exist_ok=True, parents=True)
|
|
|
|
|
topic_paths.append(topic_output_folder_path)
|
|
|
|
|
for connection, timestamp, rawdata in reader.messages(
|
|
|
|
|
connections=connections
|
|
|
|
|
):
|
|
|
|
|
img_msg = reader.deserialize(rawdata, connection.msgtype)
|
|
|
|
|
|
|
|
|
|
img_data = np.frombuffer(
|
|
|
|
|
img_msg.data,
|
|
|
|
|
dtype=np.uint8 if "8" in img_msg.encoding else np.uint16,
|
|
|
|
|
).reshape((img_msg.height, img_msg.width))
|
|
|
|
|
img = Image.fromarray(img_data, mode="L")
|
|
|
|
|
frame_count += 1
|
|
|
|
|
img.save(topic_output_folder_path / f"frame_{frame_count:04d}.png")
|
|
|
|
|
return topic_paths
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def crop_projection_data_to_roi(
|
2024-06-11 15:56:33 +02:00
|
|
|
data: DataFrame,
|
|
|
|
|
roi_angle_start: float,
|
|
|
|
|
roi_angle_width: float,
|
|
|
|
|
horizontal_resolution: int,
|
|
|
|
|
) -> tuple[DataFrame, int]:
|
|
|
|
|
if roi_angle_width == 360:
|
|
|
|
|
return data, horizontal_resolution
|
|
|
|
|
|
|
|
|
|
roi_index_start = int(horizontal_resolution / 360 * roi_angle_start)
|
|
|
|
|
roi_index_width = int(horizontal_resolution / 360 * roi_angle_width)
|
|
|
|
|
roi_index_end = roi_index_start + roi_index_width
|
|
|
|
|
|
|
|
|
|
if roi_index_end < horizontal_resolution:
|
|
|
|
|
cropped_data = data.iloc[:, roi_index_start:roi_index_end]
|
|
|
|
|
else:
|
|
|
|
|
roi_index_end = roi_index_end - horizontal_resolution
|
|
|
|
|
cropped_data = data.iloc[:, roi_index_end:roi_index_start]
|
|
|
|
|
|
|
|
|
|
return cropped_data, roi_index_width
|
|
|
|
|
|
|
|
|
|
|
2024-04-19 13:38:09 +02:00
|
|
|
def create_2d_projection(
|
|
|
|
|
df: DataFrame,
|
|
|
|
|
output_file_path: Path,
|
|
|
|
|
tmp_file_path: Path,
|
|
|
|
|
colormap_name: str,
|
|
|
|
|
missing_data_color: str,
|
|
|
|
|
reverse_colormap: bool,
|
2024-06-11 11:07:05 +02:00
|
|
|
horizontal_resolution: int,
|
|
|
|
|
vertical_resolution: int,
|
2024-04-19 13:38:09 +02:00
|
|
|
):
|
2024-06-11 15:56:33 +02:00
|
|
|
fig, ax = plt.subplots(
|
|
|
|
|
figsize=(float(horizontal_resolution) / 100, float(vertical_resolution) / 100)
|
|
|
|
|
)
|
2024-04-19 13:38:09 +02:00
|
|
|
ax.imshow(
|
|
|
|
|
df,
|
2024-06-11 11:07:05 +02:00
|
|
|
cmap=get_colormap_with_special_missing_color(
|
|
|
|
|
colormap_name, missing_data_color, reverse_colormap
|
|
|
|
|
),
|
2024-04-19 13:38:09 +02:00
|
|
|
aspect="auto",
|
|
|
|
|
)
|
|
|
|
|
ax.axis("off")
|
|
|
|
|
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
|
|
|
|
plt.savefig(tmp_file_path, dpi=100, bbox_inches="tight", pad_inches=0)
|
|
|
|
|
plt.close()
|
|
|
|
|
img = Image.open(tmp_file_path)
|
2024-06-11 15:56:33 +02:00
|
|
|
img_resized = img.resize(
|
|
|
|
|
(horizontal_resolution, vertical_resolution), Image.LANCZOS
|
|
|
|
|
)
|
2024-04-19 13:38:09 +02:00
|
|
|
img_resized.save(output_file_path)
|
2024-06-11 15:56:33 +02:00
|
|
|
tmp_file_path.unlink()
|
2024-04-19 13:38:09 +02:00
|
|
|
|
|
|
|
|
|
2024-10-17 08:36:18 +02:00
|
|
|
def process_frame(args) -> Tuple[int, np.ndarray, Optional[Path]]:
|
|
|
|
|
(
|
|
|
|
|
i,
|
|
|
|
|
pc,
|
|
|
|
|
output_path,
|
|
|
|
|
colormap_name,
|
|
|
|
|
missing_data_color,
|
|
|
|
|
reverse_colormap,
|
|
|
|
|
vertical_resolution,
|
|
|
|
|
horizontal_resolution,
|
|
|
|
|
vertical_scale,
|
|
|
|
|
horizontal_scale,
|
|
|
|
|
roi_angle_start,
|
|
|
|
|
roi_angle_width,
|
|
|
|
|
render_images,
|
|
|
|
|
) = args
|
|
|
|
|
|
|
|
|
|
lidar_data = pc.data.copy()
|
|
|
|
|
|
|
|
|
|
lidar_data["horizontal_position"] = (
|
|
|
|
|
lidar_data["original_id"] % horizontal_resolution
|
|
|
|
|
)
|
2025-03-14 18:02:23 +01:00
|
|
|
# lidar_data["horizontal_position_yaw_f"] = (
|
|
|
|
|
# 0.5
|
|
|
|
|
# * horizontal_resolution
|
|
|
|
|
# * (np.arctan2(lidar_data["y"], lidar_data["x"]) / pi + 1.0)
|
|
|
|
|
# )
|
|
|
|
|
# lidar_data["horizontal_position_yaw"] = np.floor(
|
|
|
|
|
# lidar_data["horizontal_position_yaw_f"]
|
|
|
|
|
# )
|
2024-10-17 08:36:18 +02:00
|
|
|
lidar_data["vertical_position"] = np.floor(
|
|
|
|
|
lidar_data["original_id"] / horizontal_resolution
|
|
|
|
|
)
|
|
|
|
|
# fov = 32 * pi / 180
|
|
|
|
|
# fov_down = 17 * pi / 180
|
2025-03-14 18:02:23 +01:00
|
|
|
# fov = 31.76 * pi / 180
|
|
|
|
|
# fov_down = 17.3 * pi / 180
|
|
|
|
|
# lidar_data["vertical_angle"] = np.arcsin(
|
|
|
|
|
# lidar_data["z"]
|
|
|
|
|
# / np.sqrt(lidar_data["x"] ** 2 + lidar_data["y"] ** 2 + lidar_data["z"] ** 2)
|
|
|
|
|
# )
|
|
|
|
|
# lidar_data["vertical_angle_degree"] = lidar_data["vertical_angle"] * 180 / pi
|
|
|
|
|
|
|
|
|
|
# lidar_data["vertical_position_pitch_f"] = vertical_resolution * (
|
|
|
|
|
# 1 - ((lidar_data["vertical_angle"] + fov_down) / fov)
|
|
|
|
|
# )
|
|
|
|
|
# lidar_data["vertical_position_pitch"] = np.floor(
|
|
|
|
|
# lidar_data["vertical_position_pitch_f"]
|
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
# duplicates = lidar_data[
|
|
|
|
|
# lidar_data.duplicated(
|
|
|
|
|
# subset=["vertical_position_pitch", "horizontal_position_yaw"],
|
|
|
|
|
# keep=False,
|
|
|
|
|
# )
|
|
|
|
|
# ].sort_values(by=["vertical_position_pitch", "horizontal_position_yaw"])
|
2024-10-17 08:36:18 +02:00
|
|
|
|
|
|
|
|
lidar_data["normalized_range"] = 1 / np.sqrt(
|
|
|
|
|
lidar_data["x"] ** 2 + lidar_data["y"] ** 2 + lidar_data["z"] ** 2
|
|
|
|
|
)
|
2025-07-10 09:10:10 +02:00
|
|
|
# check for zero values in normalized_range and output the x y and z values for them
|
|
|
|
|
zero_range_indices = lidar_data[lidar_data["normalized_range"] == 0].index
|
|
|
|
|
if not zero_range_indices.empty:
|
|
|
|
|
print(
|
|
|
|
|
f"Warning: Found {len(zero_range_indices)} points with zero normalized range at indices: {zero_range_indices.tolist()}"
|
|
|
|
|
)
|
|
|
|
|
print(
|
|
|
|
|
"X, Y, Z values for these points:",
|
|
|
|
|
lidar_data.loc[zero_range_indices, ["x", "y", "z"]].to_numpy(),
|
|
|
|
|
)
|
|
|
|
|
# Set zero values to NaN to avoid issues with training
|
|
|
|
|
lidar_data.loc[zero_range_indices, "normalized_range"] = np.nan
|
|
|
|
|
|
2024-10-17 08:36:18 +02:00
|
|
|
projection_data = lidar_data.pivot(
|
2025-03-14 18:02:23 +01:00
|
|
|
index="vertical_position",
|
|
|
|
|
columns="horizontal_position",
|
2024-10-17 08:36:18 +02:00
|
|
|
values="normalized_range",
|
|
|
|
|
)
|
|
|
|
|
projection_data = projection_data.reindex(
|
2025-07-10 09:10:10 +02:00
|
|
|
columns=range(horizontal_resolution), fill_value=np.nan
|
2024-10-17 08:36:18 +02:00
|
|
|
)
|
|
|
|
|
projection_data = projection_data.reindex(
|
2025-07-10 09:10:10 +02:00
|
|
|
index=range(vertical_resolution), fill_value=np.nan
|
2024-10-17 08:36:18 +02:00
|
|
|
)
|
|
|
|
|
projection_data, output_horizontal_resolution = crop_projection_data_to_roi(
|
|
|
|
|
projection_data, roi_angle_start, roi_angle_width, horizontal_resolution
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if render_images:
|
|
|
|
|
image_path = create_2d_projection(
|
|
|
|
|
projection_data,
|
|
|
|
|
output_path / f"frame_{i:04d}.png",
|
|
|
|
|
output_path / f"tmp_{i:04d}.png",
|
|
|
|
|
colormap_name,
|
|
|
|
|
missing_data_color,
|
|
|
|
|
reverse_colormap,
|
|
|
|
|
horizontal_resolution=output_horizontal_resolution * horizontal_scale,
|
|
|
|
|
vertical_resolution=vertical_resolution * vertical_scale,
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
image_path = None
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
i,
|
|
|
|
|
projection_data.to_numpy(),
|
|
|
|
|
image_path,
|
2025-03-14 18:02:23 +01:00
|
|
|
# lidar_data["vertical_position_pitch_f"].min(),
|
|
|
|
|
# lidar_data["vertical_position_pitch_f"].max(),
|
2024-10-17 08:36:18 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Adjusted to use a generator for args_list
|
2024-06-28 09:46:38 +02:00
|
|
|
def create_projection_data(
|
2024-04-19 13:38:09 +02:00
|
|
|
dataset: Dataset,
|
2024-06-11 15:56:33 +02:00
|
|
|
output_path: Path,
|
2024-04-19 13:38:09 +02:00
|
|
|
colormap_name: str,
|
|
|
|
|
missing_data_color: str,
|
|
|
|
|
reverse_colormap: bool,
|
2024-10-17 08:36:18 +02:00
|
|
|
vertical_resolution: int,
|
2024-06-11 11:07:05 +02:00
|
|
|
horizontal_resolution: int,
|
|
|
|
|
vertical_scale: int,
|
2024-06-11 11:45:57 +02:00
|
|
|
horizontal_scale: int,
|
2024-06-11 15:56:33 +02:00
|
|
|
roi_angle_start: float,
|
|
|
|
|
roi_angle_width: float,
|
2024-06-28 09:46:38 +02:00
|
|
|
render_images: bool,
|
2024-10-17 08:36:18 +02:00
|
|
|
) -> Tuple[np.ndarray, Optional[list[Path]]]:
|
2024-04-19 13:38:09 +02:00
|
|
|
rendered_images = []
|
2024-06-28 09:46:38 +02:00
|
|
|
converted_lidar_frames = []
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-10-17 08:36:18 +02:00
|
|
|
# Generator for args_list
|
|
|
|
|
# def args_generator():
|
|
|
|
|
# for i, pc in enumerate(dataset, 1):
|
|
|
|
|
# yield (
|
|
|
|
|
# i,
|
|
|
|
|
# pc,
|
|
|
|
|
# output_path,
|
|
|
|
|
# colormap_name,
|
|
|
|
|
# missing_data_color,
|
|
|
|
|
# reverse_colormap,
|
|
|
|
|
# vertical_resolution,
|
|
|
|
|
# horizontal_resolution,
|
|
|
|
|
# vertical_scale,
|
|
|
|
|
# horizontal_scale,
|
|
|
|
|
# roi_angle_start,
|
|
|
|
|
# roi_angle_width,
|
|
|
|
|
# render_images,
|
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
# results = []
|
|
|
|
|
|
|
|
|
|
# with Pool() as pool:
|
|
|
|
|
# results_gen = pool.imap(process_frame, args_generator())
|
|
|
|
|
# for result in track(
|
|
|
|
|
# results_gen, description="Processing...", total=len(dataset)
|
|
|
|
|
# ):
|
|
|
|
|
# results.append(result)
|
|
|
|
|
|
|
|
|
|
# results.sort(key=lambda x: x[0])
|
|
|
|
|
|
2024-06-11 11:07:05 +02:00
|
|
|
for i, pc in track(
|
2024-10-17 08:36:18 +02:00
|
|
|
enumerate(dataset, 1), description="Processing...", total=len(dataset)
|
2024-06-11 11:07:05 +02:00
|
|
|
):
|
2024-10-17 08:36:18 +02:00
|
|
|
results = process_frame(
|
|
|
|
|
(
|
|
|
|
|
i,
|
|
|
|
|
pc,
|
|
|
|
|
output_path,
|
2024-06-28 09:46:38 +02:00
|
|
|
colormap_name,
|
|
|
|
|
missing_data_color,
|
|
|
|
|
reverse_colormap,
|
2024-10-17 08:36:18 +02:00
|
|
|
vertical_resolution,
|
|
|
|
|
horizontal_resolution,
|
|
|
|
|
vertical_scale,
|
|
|
|
|
horizontal_scale,
|
|
|
|
|
roi_angle_start,
|
|
|
|
|
roi_angle_width,
|
|
|
|
|
render_images,
|
2024-06-28 09:46:38 +02:00
|
|
|
)
|
2024-10-17 08:36:18 +02:00
|
|
|
)
|
|
|
|
|
converted_lidar_frames.append(results[1])
|
|
|
|
|
if render_images:
|
|
|
|
|
rendered_images.append(results[2])
|
|
|
|
|
|
|
|
|
|
# min_all = 100
|
|
|
|
|
# max_all = 0
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-10-17 08:36:18 +02:00
|
|
|
# for _, data, img_path, min_frame, max_frame in results:
|
|
|
|
|
# converted_lidar_frames.append(data)
|
|
|
|
|
# if img_path:
|
|
|
|
|
# rendered_images.append(img_path)
|
|
|
|
|
# if min_frame < min_all:
|
|
|
|
|
# min_all = min_frame
|
|
|
|
|
# if max_frame > max_all:
|
|
|
|
|
# max_all = max_frame
|
|
|
|
|
|
|
|
|
|
# print(f"{min_all=}, {max_all=}")
|
2024-06-28 09:46:38 +02:00
|
|
|
|
|
|
|
|
projection_data = np.stack(converted_lidar_frames, axis=0)
|
|
|
|
|
|
|
|
|
|
if render_images:
|
2024-10-17 08:36:18 +02:00
|
|
|
return projection_data, rendered_images
|
2024-06-28 09:46:38 +02:00
|
|
|
else:
|
2025-03-14 18:02:23 +01:00
|
|
|
return projection_data, None
|
2024-04-19 13:38:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
parser = ArgParser(
|
|
|
|
|
config_file_parser_class=YAMLConfigFileParser,
|
|
|
|
|
default_config_files=["render2d_config.yaml"],
|
|
|
|
|
formatter_class=ArgumentDefaultsRawHelpFormatter,
|
|
|
|
|
description="Render a 2d projection of a point cloud",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-11 11:07:05 +02:00
|
|
|
"--render-config-file", is_config_file=True, help="yaml config file path"
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-11 15:56:33 +02:00
|
|
|
"--input-experiment-path",
|
|
|
|
|
required=True,
|
|
|
|
|
type=existing_path,
|
|
|
|
|
help="path to experiment. (directly to bag file, to parent folder for mcap)",
|
2024-06-11 11:07:05 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-11 15:56:33 +02:00
|
|
|
"--pointcloud-topic",
|
|
|
|
|
default="/ouster/points",
|
|
|
|
|
type=str,
|
|
|
|
|
help="topic in the ros/mcap bag file containing the point cloud data",
|
|
|
|
|
)
|
2024-10-17 08:36:18 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--image-topics",
|
|
|
|
|
default=[],
|
|
|
|
|
nargs="+",
|
|
|
|
|
help="topics in the ros/mcap bag file containing the image data",
|
|
|
|
|
)
|
2024-06-11 15:56:33 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--output-path",
|
|
|
|
|
default=Path("./output"),
|
2024-06-11 11:07:05 +02:00
|
|
|
type=Path,
|
2024-06-11 15:56:33 +02:00
|
|
|
help="path rendered frames should be written to",
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-28 09:46:38 +02:00
|
|
|
"--output-no-images",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="do not create individual image files for the projection frames",
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-28 09:46:38 +02:00
|
|
|
"--output-no-video",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="do not create a video file from the projection frames",
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-28 09:46:38 +02:00
|
|
|
"--output-no-numpy",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="do not create a numpy file with the projection data",
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
2024-06-28 09:46:38 +02:00
|
|
|
"--force-generation",
|
|
|
|
|
action="store_true",
|
|
|
|
|
help="if used will force the generation even if output already exists",
|
2024-06-11 11:07:05 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--colormap-name",
|
|
|
|
|
default="viridis",
|
|
|
|
|
type=str,
|
|
|
|
|
help="name of matplotlib colormap to be used",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--missing-data-color",
|
|
|
|
|
default="black",
|
|
|
|
|
type=str,
|
|
|
|
|
help="name of color to be used for missing data",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--reverse-colormap",
|
|
|
|
|
default=True,
|
|
|
|
|
type=bool,
|
|
|
|
|
help="if colormap should be reversed",
|
|
|
|
|
)
|
2024-10-17 08:36:18 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--vertical-resolution",
|
|
|
|
|
default=32,
|
|
|
|
|
type=positive_int,
|
|
|
|
|
help="number of vertical lidar data point rows",
|
|
|
|
|
)
|
2024-06-11 11:07:05 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--horizontal-resolution",
|
|
|
|
|
default=2048,
|
|
|
|
|
type=positive_int,
|
2024-10-17 08:36:18 +02:00
|
|
|
help="number of horizontal lidar data point columns",
|
2024-06-11 11:07:05 +02:00
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--vertical-scale",
|
|
|
|
|
default=1,
|
|
|
|
|
type=positive_int,
|
|
|
|
|
help="multiplier for vertical scale, for better visualization",
|
2024-04-19 13:38:09 +02:00
|
|
|
)
|
2024-06-11 11:45:57 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--horizontal-scale",
|
|
|
|
|
default=1,
|
|
|
|
|
type=positive_int,
|
|
|
|
|
help="multiplier for horizontal scale, for better visualization",
|
|
|
|
|
)
|
2024-06-11 15:56:33 +02:00
|
|
|
parser.add_argument(
|
|
|
|
|
"--roi-angle-start",
|
|
|
|
|
default=0,
|
|
|
|
|
type=angle,
|
|
|
|
|
help="angle where roi starts",
|
|
|
|
|
)
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
"--roi-angle-width",
|
|
|
|
|
default=360,
|
|
|
|
|
type=angle_width,
|
|
|
|
|
help="width of roi in degrees",
|
|
|
|
|
)
|
2024-04-19 13:38:09 +02:00
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
2024-06-11 15:56:33 +02:00
|
|
|
output_path = args.output_path / args.input_experiment_path.stem
|
|
|
|
|
output_path.mkdir(parents=True, exist_ok=True)
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-06-28 09:46:38 +02:00
|
|
|
parser.write_config_file(
|
|
|
|
|
parser.parse_known_args()[0],
|
|
|
|
|
output_file_paths=[(output_path / "config.yaml").as_posix()],
|
|
|
|
|
)
|
|
|
|
|
|
2024-06-11 15:56:33 +02:00
|
|
|
# Create temporary folder for images, if outputting images we use the output folder itself as temp folder
|
2024-06-28 09:46:38 +02:00
|
|
|
tmp_path = output_path / "tmp" if args.output_no_images else output_path / "frames"
|
2024-06-11 15:56:33 +02:00
|
|
|
tmp_path.mkdir(parents=True, exist_ok=True)
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-06-11 11:07:05 +02:00
|
|
|
dataset = load_dataset(args.input_experiment_path, args.pointcloud_topic)
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-06-28 09:46:38 +02:00
|
|
|
images = []
|
2024-10-17 08:36:18 +02:00
|
|
|
topic_paths = []
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-06-28 09:46:38 +02:00
|
|
|
if not args.output_no_images or not args.output_no_video:
|
|
|
|
|
if not args.force_generation and all(
|
|
|
|
|
(tmp_path / f"frame_{i:04d}.png").exists()
|
|
|
|
|
for i in range(1, len(dataset) + 1)
|
|
|
|
|
):
|
|
|
|
|
print(
|
|
|
|
|
f"Skipping image generation for {args.input_experiment_path} as all frames already exist"
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
projection_data, images = create_projection_data(
|
|
|
|
|
dataset,
|
|
|
|
|
tmp_path,
|
|
|
|
|
args.colormap_name,
|
|
|
|
|
args.missing_data_color,
|
|
|
|
|
args.reverse_colormap,
|
2024-10-17 08:36:18 +02:00
|
|
|
args.vertical_resolution,
|
2024-06-28 09:46:38 +02:00
|
|
|
args.horizontal_resolution,
|
|
|
|
|
args.vertical_scale,
|
|
|
|
|
args.horizontal_scale,
|
|
|
|
|
args.roi_angle_start,
|
|
|
|
|
args.roi_angle_width,
|
|
|
|
|
render_images=True,
|
|
|
|
|
)
|
2024-10-17 08:36:18 +02:00
|
|
|
if args.image_topics:
|
|
|
|
|
topic_paths = save_image_topics(
|
|
|
|
|
args.input_experiment_path, output_path, args.image_topics
|
|
|
|
|
)
|
2024-06-28 09:46:38 +02:00
|
|
|
|
|
|
|
|
output_numpy_path = (output_path / args.input_experiment_path.stem).with_suffix(
|
|
|
|
|
".npy"
|
|
|
|
|
)
|
|
|
|
|
if not args.output_no_numpy:
|
|
|
|
|
if not args.force_generation and output_numpy_path.exists():
|
|
|
|
|
print(
|
|
|
|
|
f"Skipping numpy file generation for {args.input_experiment_path} as {output_numpy_path} already exists"
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
if args.output_no_images:
|
|
|
|
|
projection_data, _ = create_projection_data(
|
|
|
|
|
dataset,
|
|
|
|
|
tmp_path,
|
|
|
|
|
args.colormap_name,
|
|
|
|
|
args.missing_data_color,
|
|
|
|
|
args.reverse_colormap,
|
2024-10-17 08:36:18 +02:00
|
|
|
args.vertical_resolution,
|
2024-06-28 09:46:38 +02:00
|
|
|
args.horizontal_resolution,
|
|
|
|
|
args.vertical_scale,
|
|
|
|
|
args.horizontal_scale,
|
|
|
|
|
args.roi_angle_start,
|
|
|
|
|
args.roi_angle_width,
|
|
|
|
|
render_images=False,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# processed_range_data.dump(output_numpy_path)
|
|
|
|
|
np.save(output_numpy_path, projection_data, fix_imports=False)
|
|
|
|
|
|
|
|
|
|
if not args.output_no_video:
|
|
|
|
|
if (
|
|
|
|
|
not args.force_generation
|
|
|
|
|
and (output_path / args.input_experiment_path.stem)
|
|
|
|
|
.with_suffix(".mp4")
|
|
|
|
|
.exists()
|
|
|
|
|
):
|
|
|
|
|
print(
|
|
|
|
|
f"Skipping video generation for {args.input_experiment_path} as {output_path / args.input_experiment_path.stem}.mp4 already exists"
|
|
|
|
|
)
|
|
|
|
|
else:
|
2024-10-17 08:36:18 +02:00
|
|
|
frame_rate = calculate_average_frame_rate(dataset)
|
2024-06-28 09:46:38 +02:00
|
|
|
input_images_pattern = f"{tmp_path}/frame_%04d.png"
|
|
|
|
|
create_video_from_images(
|
|
|
|
|
input_images_pattern,
|
|
|
|
|
(output_path / args.input_experiment_path.stem).with_suffix(".mp4"),
|
2024-10-17 08:36:18 +02:00
|
|
|
frame_rate,
|
2024-06-28 09:46:38 +02:00
|
|
|
)
|
2024-10-17 08:36:18 +02:00
|
|
|
for topic_path in topic_paths:
|
|
|
|
|
input_images_pattern = f"{topic_path}/frame_%04d.png"
|
|
|
|
|
create_video_from_images(
|
|
|
|
|
input_images_pattern,
|
|
|
|
|
(output_path / topic_path.stem).with_suffix(".mp4"),
|
|
|
|
|
frame_rate,
|
|
|
|
|
)
|
2024-04-19 13:38:09 +02:00
|
|
|
|
2024-06-28 09:46:38 +02:00
|
|
|
if args.output_no_images:
|
2024-04-19 13:38:09 +02:00
|
|
|
for image in images:
|
|
|
|
|
image.unlink()
|
2024-06-11 15:56:33 +02:00
|
|
|
tmp_path.rmdir()
|
2024-04-19 13:38:09 +02:00
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
exit(main())
|