adopted 2d rendering for vif dataset

This commit is contained in:
Jan Kowalczyk
2024-06-11 11:07:05 +02:00
parent 0ee3bc82b5
commit 7486e110ee
4 changed files with 168 additions and 29 deletions

View File

@@ -7,7 +7,7 @@ from pathlib import Path
from util import ( from util import (
load_dataset_from_bag, load_dataset,
existing_file, existing_file,
) )
@@ -49,7 +49,7 @@ def main() -> int:
args = parser.parse_args() args = parser.parse_args()
print("Creating camera settings!") print("Creating camera settings!")
print("Move the view in the window to the desired camera position" " and then close the window using the ESC key!") print("Move the view in the window to the desired camera position" " and then close the window using the ESC key!")
dataset = load_dataset_from_bag(args.input_bag_path) dataset = load_dataset(args.input_bag_path)
open3d_pointcloud = dataset[args.bag_pointcloud_index].to_instance("open3d") open3d_pointcloud = dataset[args.bag_pointcloud_index].to_instance("open3d")
create_camera_settings(open3d_pointcloud, args.camera_config_output_json_path) create_camera_settings(open3d_pointcloud, args.camera_config_output_json_path)

View File

@@ -1,15 +1,24 @@
from configargparse import ArgParser, YAMLConfigFileParser, ArgumentDefaultsRawHelpFormatter from configargparse import (
ArgParser,
YAMLConfigFileParser,
ArgumentDefaultsRawHelpFormatter,
)
from sys import exit from sys import exit
from pathlib import Path from pathlib import Path
from pointcloudset import Dataset from pointcloudset import Dataset
from rich.progress import track from rich.progress import track
from pandas import DataFrame from pandas import DataFrame
from PIL import Image from PIL import Image
import matplotlib
import numpy as np
matplotlib.use("Agg")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from util import ( from util import (
load_dataset_from_bag, angle, angle_width, positive_int,
existing_file, load_dataset,
existing_path,
create_video_from_images, create_video_from_images,
calculate_average_frame_rate, calculate_average_frame_rate,
get_colormap_with_special_missing_color, get_colormap_with_special_missing_color,
@@ -23,11 +32,15 @@ def create_2d_projection(
colormap_name: str, colormap_name: str,
missing_data_color: str, missing_data_color: str,
reverse_colormap: bool, reverse_colormap: bool,
horizontal_resolution: int,
vertical_resolution: int,
): ):
fig, ax = plt.subplots(figsize=(20.48, 5.12)) fig, ax = plt.subplots(figsize=(float(horizontal_resolution) / 100, float(vertical_resolution) / 100))
ax.imshow( ax.imshow(
df, df,
cmap=get_colormap_with_special_missing_color(colormap_name, missing_data_color, reverse_colormap), cmap=get_colormap_with_special_missing_color(
colormap_name, missing_data_color, reverse_colormap
),
aspect="auto", aspect="auto",
) )
ax.axis("off") ax.axis("off")
@@ -35,7 +48,7 @@ def create_2d_projection(
plt.savefig(tmp_file_path, dpi=100, bbox_inches="tight", pad_inches=0) plt.savefig(tmp_file_path, dpi=100, bbox_inches="tight", pad_inches=0)
plt.close() plt.close()
img = Image.open(tmp_file_path) img = Image.open(tmp_file_path)
img_resized = img.resize((2048, 512), Image.LANCZOS) img_resized = img.resize((horizontal_resolution, vertical_resolution), Image.LANCZOS)
img_resized.save(output_file_path) img_resized.save(output_file_path)
@@ -47,13 +60,38 @@ def render_2d_images(
colormap_name: str, colormap_name: str,
missing_data_color: str, missing_data_color: str,
reverse_colormap: bool, reverse_colormap: bool,
horizontal_resolution: int,
roi_angle_start: float,
roi_angle_width: float,
vertical_scale: int,
) -> list[Path]: ) -> list[Path]:
rendered_images = [] rendered_images = []
for i, pc in track(enumerate(dataset, 1), description="Rendering images...", total=len(dataset)): for i, pc in track(
pc.data["horizontal_position"] = pc.data["original_id"] % 2048 enumerate(dataset, 1), description="Rendering images...", total=len(dataset)
image_data = pc.data.pivot(index="ring", columns="horizontal_position", values="range") ):
normalized_data = (image_data - image_data.min().min()) / (image_data.max().max() - image_data.min().min()) complete_original_ids = DataFrame({'original_id': np.arange(0, (pc.data['ring'].max() + 1) * horizontal_resolution, dtype=np.uint32)})
pc.data = complete_original_ids.merge(pc.data, on='original_id', how='left')
pc.data['ring'] = (pc.data['original_id'] // horizontal_resolution)
pc.data["horizontal_position"] = pc.data["original_id"] % horizontal_resolution
image_data = pc.data.pivot(
index="ring", columns="horizontal_position", values="range"
)
if roi_angle_width != 360:
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:
image_data = image_data.iloc[:, roi_index_start:roi_index_end]
else:
roi_index_end = roi_index_end - horizontal_resolution
image_data = image_data.iloc[:, roi_index_end:roi_index_start]
normalized_data = (image_data - image_data.min().min()) / (
image_data.max().max() - image_data.min().min()
)
image_path = create_2d_projection( image_path = create_2d_projection(
normalized_data, normalized_data,
output_images_path / f"{image_pattern_prefix}_frame_{i:04d}.png", output_images_path / f"{image_pattern_prefix}_frame_{i:04d}.png",
@@ -61,6 +99,8 @@ def render_2d_images(
colormap_name, colormap_name,
missing_data_color, missing_data_color,
reverse_colormap, reverse_colormap,
horizontal_resolution=roi_index_width if roi_angle_width != 360 else horizontal_resolution,
vertical_resolution=(pc.data['ring'].max() + 1) * vertical_scale
) )
rendered_images.append(image_path) rendered_images.append(image_path)
@@ -75,19 +115,35 @@ def main() -> int:
formatter_class=ArgumentDefaultsRawHelpFormatter, formatter_class=ArgumentDefaultsRawHelpFormatter,
description="Render a 2d projection of a point cloud", description="Render a 2d projection of a point cloud",
) )
parser.add_argument("--render-config-file", is_config_file=True, help="yaml config file path")
parser.add_argument("--input-bag-path", required=True, type=existing_file, help="path to bag file")
parser.add_argument( parser.add_argument(
"--tmp-files-path", default=Path("./tmp"), type=Path, help="path temporary files will be written to" "--render-config-file", is_config_file=True, help="yaml config file path"
) )
parser.add_argument( parser.add_argument(
"--output-images", type=bool, default=True, help="if rendered frames should be outputted as images" "--input-experiment-path", required=True, type=existing_path, help="path to experiment. (directly to bag file, to parent folder for mcap)"
) )
parser.add_argument( parser.add_argument(
"--output-images-path", default=Path("./output"), type=Path, help="path rendered frames should be written to" "--tmp-files-path",
default=Path("./tmp"),
type=Path,
help="path temporary files will be written to",
) )
parser.add_argument( parser.add_argument(
"--output-video", type=bool, default=True, help="if rendered frames should be outputted as a video" "--output-images",
type=bool,
default=True,
help="if rendered frames should be outputted as images",
)
parser.add_argument(
"--output-images-path",
default=Path("./output"),
type=Path,
help="path rendered frames should be written to",
)
parser.add_argument(
"--output-video",
type=bool,
default=True,
help="if rendered frames should be outputted as a video",
) )
parser.add_argument( parser.add_argument(
"--output-video-path", "--output-video-path",
@@ -95,12 +151,60 @@ def main() -> int:
type=Path, type=Path,
help="path rendered video should be written to", help="path rendered video should be written to",
) )
parser.add_argument("--output-images-prefix", default="2d_render", type=str, help="filename prefix for output")
parser.add_argument("--colormap-name", default="viridis", type=str, help="name of matplotlib colormap to be used")
parser.add_argument( parser.add_argument(
"--missing-data-color", default="black", type=str, help="name of color to be used for missing data" "--output-images-prefix",
default="2d_render",
type=str,
help="filename prefix for output",
)
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",
)
parser.add_argument(
"--pointcloud-topic",
default="/ouster/points",
type=str,
help="topic in the ros/mcap bag file containing the point cloud data",
)
parser.add_argument(
"--horizontal-resolution",
default=2048,
type=positive_int,
help="number of horizontal lidar data points",
)
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",
)
parser.add_argument(
"--vertical-scale",
default=1,
type=positive_int,
help="multiplier for vertical scale, for better visualization",
) )
parser.add_argument("--reverse-colormap", default=True, type=bool, help="if colormap should be reversed")
args = parser.parse_args() args = parser.parse_args()
@@ -113,7 +217,7 @@ def main() -> int:
if args.output_video: if args.output_video:
args.output_video_path.parent.mkdir(parents=True, exist_ok=True) args.output_video_path.parent.mkdir(parents=True, exist_ok=True)
dataset = load_dataset_from_bag(args.input_bag_path) dataset = load_dataset(args.input_experiment_path, args.pointcloud_topic)
images = render_2d_images( images = render_2d_images(
dataset, dataset,
@@ -123,11 +227,21 @@ def main() -> int:
args.colormap_name, args.colormap_name,
args.missing_data_color, args.missing_data_color,
args.reverse_colormap, args.reverse_colormap,
args.horizontal_resolution,
args.roi_angle_start,
args.roi_angle_width,
args.vertical_scale,
) )
if args.output_video: if args.output_video:
input_images_pattern = f"{args.tmp_files_path / args.output_images_prefix}_frame_%04d.png" input_images_pattern = (
create_video_from_images(input_images_pattern, args.output_video_path, calculate_average_frame_rate(dataset)) f"{args.tmp_files_path / args.output_images_prefix}_frame_%04d.png"
)
create_video_from_images(
input_images_pattern,
args.output_video_path,
calculate_average_frame_rate(dataset),
)
if not args.output_images: if not args.output_images:
for image in images: for image in images:

View File

@@ -12,7 +12,7 @@ import matplotlib.pyplot as plt
import numpy as np import numpy as np
from util import ( from util import (
load_dataset_from_bag, load_dataset,
existing_file, existing_file,
create_video_from_images, create_video_from_images,
calculate_average_frame_rate, calculate_average_frame_rate,
@@ -107,7 +107,7 @@ def main() -> int:
if args.output_video: if args.output_video:
args.output_video_path.parent.mkdir(parents=True, exist_ok=True) args.output_video_path.parent.mkdir(parents=True, exist_ok=True)
dataset = load_dataset_from_bag(args.input_bag_path) dataset = load_dataset(args.input_bag_path)
images = render_3d_images( images = render_3d_images(
dataset, args.camera_config_input_json_path, args.tmp_files_path, args.output_images_prefix dataset, args.camera_config_input_json_path, args.tmp_files_path, args.output_images_prefix

View File

@@ -1,4 +1,5 @@
from pointcloudset import Dataset from pointcloudset import Dataset
from pointcloudset.io.dataset.ros import dataset_from_ros
from pathlib import Path from pathlib import Path
from argparse import ArgumentTypeError from argparse import ArgumentTypeError
from subprocess import run from subprocess import run
@@ -7,10 +8,11 @@ from matplotlib.colors import Colormap
from matplotlib import colormaps from matplotlib import colormaps
def load_dataset_from_bag(bag_file_path: Path, pointcloud_topic: str = "/ouster/points") -> Dataset: def load_dataset(bag_file_path: Path, pointcloud_topic: str = "/ouster/points") -> Dataset:
return Dataset.from_file(bag_file_path, topic=pointcloud_topic) return Dataset.from_file(bag_file_path, topic=pointcloud_topic)
def calculate_average_frame_rate(dataset: Dataset): def calculate_average_frame_rate(dataset: Dataset):
timestamps = dataset.timestamps timestamps = dataset.timestamps
time_deltas = [timestamps[i + 1] - timestamps[i] for i in range(len(timestamps) - 1)] time_deltas = [timestamps[i + 1] - timestamps[i] for i in range(len(timestamps) - 1)]
@@ -32,10 +34,33 @@ def existing_folder(path_string: str) -> Path:
path = Path(path_string) path = Path(path_string)
if not path.exists(): if not path.exists():
raise ArgumentTypeError(f"{path} does not exist!") raise ArgumentTypeError(f"{path} does not exist!")
if not path.is_folder(): if not path.is_dir():
raise ArgumentTypeError(f"{path} is not a valid folder!") raise ArgumentTypeError(f"{path} is not a valid folder!")
return path return path
def existing_path(path_string: str) -> Path:
path = Path(path_string)
if not path.exists():
raise ArgumentTypeError(f"{path} does not exist!")
return path
def positive_int(number_str: str) -> int:
number_val = int(number_str)
if number_val < 0:
raise ArgumentTypeError(f"{number_val} is not a positive integer!")
return number_val
def angle(angle_str: str) -> float:
angle_val = float(angle_str)
if angle_val < 0 or angle_val >= 360:
raise ArgumentTypeError(f"{angle_val} is not a valid angle! Needs to be in [0, 360)")
return angle_val
def angle_width(angle_str: str) -> float:
angle_val = float(angle_str)
if angle_val < 0 or angle_val > 360:
raise ArgumentTypeError(f"{angle_val} is not a valid angle width! Needs to be in [0, 360]")
return angle_val
def get_colormap_with_special_missing_color( def get_colormap_with_special_missing_color(
colormap_name: str, missing_data_color: str = "black", reverse: bool = False colormap_name: str, missing_data_color: str = "black", reverse: bool = False