full upload so not to lose anything important
This commit is contained in:
188
tools/plot_scripts/data_spherical_projection.py
Normal file
188
tools/plot_scripts/data_spherical_projection.py
Normal file
@@ -0,0 +1,188 @@
|
||||
import argparse
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from matplotlib import colormaps
|
||||
from matplotlib.cm import ScalarMappable
|
||||
from matplotlib.colors import Colormap, ListedColormap
|
||||
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
|
||||
from PIL import Image # new import
|
||||
|
||||
|
||||
def get_colormap_with_special_missing_color(
|
||||
colormap_name: str, missing_data_color: str = "black", reverse: bool = False
|
||||
) -> Colormap:
|
||||
colormap = (
|
||||
colormaps[colormap_name] if not reverse else colormaps[f"{colormap_name}_r"]
|
||||
)
|
||||
colormap.set_bad(missing_data_color)
|
||||
colormap.set_over("white")
|
||||
return colormap
|
||||
|
||||
|
||||
# --- Setup output folders (similar to data_missing_points.py) ---
|
||||
# Change the output path as needed
|
||||
output_path = Path("/home/fedex/mt/plots/data_2d_projections")
|
||||
datetime_folder_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
output_datetime_path = output_path / datetime_folder_name
|
||||
latest_folder_path = output_path / "latest"
|
||||
archive_folder_path = output_path / "archive"
|
||||
|
||||
for folder in (
|
||||
output_path,
|
||||
output_datetime_path,
|
||||
latest_folder_path,
|
||||
archive_folder_path,
|
||||
):
|
||||
folder.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# --- Parse command-line arguments ---
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Plot two 2D projections (from .npy files) in vertical subplots"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input1",
|
||||
type=Path,
|
||||
default=Path(
|
||||
"/home/fedex/mt/data/subter/new_projection/1_loop_closure_illuminated_2023-01-23.npy"
|
||||
),
|
||||
help="Path to first .npy file containing 2D projection data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input2",
|
||||
type=Path,
|
||||
default=Path(
|
||||
"/home/fedex/mt/data/subter/new_projection/3_smoke_human_walking_2023-01-23.npy"
|
||||
),
|
||||
help="Path to second .npy file containing 2D projection data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--frame1",
|
||||
type=int,
|
||||
default=955,
|
||||
help="Frame index to plot from first file (0-indexed)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--frame2",
|
||||
type=int,
|
||||
default=242,
|
||||
help="Frame index to plot from second file (0-indexed)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--colormap",
|
||||
default="viridis",
|
||||
type=str,
|
||||
help="Name of matplotlib colormap to use",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--missing-data-color",
|
||||
default="black",
|
||||
type=str,
|
||||
help="Color to use for missing data in projection",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--reverse-colormap",
|
||||
action="store_true",
|
||||
help="Reverse the colormap if specified",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# --- Load the numpy projection data from the provided files ---
|
||||
# Each file is assumed to be a 3D array: (num_frames, height, width)
|
||||
proj_data1 = np.load(args.input1)
|
||||
proj_data2 = np.load(args.input2)
|
||||
|
||||
# Choose the desired frames
|
||||
try:
|
||||
frame1 = proj_data1[args.frame1]
|
||||
except IndexError:
|
||||
raise ValueError(f"Frame index {args.frame1} out of range for file: {args.input1}")
|
||||
try:
|
||||
frame2 = proj_data2[args.frame2]
|
||||
except IndexError:
|
||||
raise ValueError(f"Frame index {args.frame2} out of range for file: {args.input2}")
|
||||
|
||||
# Determine shared range across both frames (ignoring NaNs)
|
||||
global_vmin = 0 # min(np.nanmin(frame1), np.nanmin(frame2))
|
||||
global_vmax = 0.8 # max(np.nanmax(frame1), np.nanmax(frame2))
|
||||
|
||||
# Create colormap using the utility (to mimic create_2d_projection)
|
||||
cmap = get_colormap_with_special_missing_color(
|
||||
args.colormap, args.missing_data_color, args.reverse_colormap
|
||||
)
|
||||
|
||||
# --- Create a figure with 2 vertical subplots ---
|
||||
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(10, 5))
|
||||
for ax, frame, title in zip(
|
||||
(ax1, ax2),
|
||||
(frame1, frame2),
|
||||
(
|
||||
"Projection of Lidar Frame without Degradation",
|
||||
"Projection of Lidar Frame with Degradation (Artifical Smoke)",
|
||||
),
|
||||
):
|
||||
im = ax.imshow(frame, cmap=cmap, aspect="auto", vmin=global_vmin, vmax=global_vmax)
|
||||
ax.set_title(title)
|
||||
ax.axis("off")
|
||||
|
||||
# Adjust layout to fit margins for a paper
|
||||
plt.tight_layout(rect=[0, 0.05, 1, 1])
|
||||
# Add a colorbar with the colormap below the subplots
|
||||
cbar = fig.colorbar(im, ax=[ax1, ax2], orientation="vertical", fraction=0.05)
|
||||
cbar.set_label("Normalized Range")
|
||||
|
||||
# Add a separate colorbar for NaN values
|
||||
sm = ScalarMappable(cmap=ListedColormap([cmap.get_bad(), cmap.get_over()]))
|
||||
divider = make_axes_locatable(cbar.ax)
|
||||
nan_ax = divider.append_axes(
|
||||
"bottom", size="15%", pad="3%", aspect=3, anchor=cbar.ax.get_anchor()
|
||||
)
|
||||
nan_ax.grid(visible=False, which="both", axis="both")
|
||||
nan_cbar = fig.colorbar(sm, cax=nan_ax, orientation="vertical")
|
||||
nan_cbar.set_ticks([0.3, 0.7])
|
||||
nan_cbar.set_ticklabels(["NaN", f"> {global_vmax}"])
|
||||
nan_cbar.ax.tick_params(length=0)
|
||||
|
||||
# Save the combined plot
|
||||
output_file = output_datetime_path / "data_2d_projections.png"
|
||||
plt.savefig(output_file, dpi=300, bbox_inches="tight", pad_inches=0.1)
|
||||
plt.close()
|
||||
|
||||
print(f"Plot saved to: {output_file}")
|
||||
|
||||
# --- Create grayscale images (high precision) from the numpy frames using Pillow ---
|
||||
# Convert NaN values to 0 and ensure the array is in float32 for 32-bit precision.
|
||||
for degradation_status, frame_number, frame in (
|
||||
("normal", args.frame1, frame1),
|
||||
("smoke", args.frame2, frame2),
|
||||
):
|
||||
frame_gray = np.nan_to_num(frame, nan=0).astype(np.float32)
|
||||
gray_image = Image.fromarray(frame_gray, mode="F")
|
||||
gray_output_file = (
|
||||
output_datetime_path
|
||||
/ f"frame_{frame_number}_grayscale_{degradation_status}.tiff"
|
||||
)
|
||||
gray_image.save(gray_output_file)
|
||||
print(f"Grayscale image saved to: {gray_output_file}")
|
||||
|
||||
# --- Handle folder structure: update latest folder and archive the output folder ---
|
||||
|
||||
# Delete current latest folder and recreate it
|
||||
shutil.rmtree(latest_folder_path, ignore_errors=True)
|
||||
latest_folder_path.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
# Copy contents of the current output datetime folder to latest folder
|
||||
for file in output_datetime_path.iterdir():
|
||||
shutil.copy2(file, latest_folder_path)
|
||||
|
||||
# Copy this python script to both output datetime folder and latest folder for preservation
|
||||
script_path = Path(__file__)
|
||||
shutil.copy2(script_path, output_datetime_path)
|
||||
shutil.copy2(script_path, latest_folder_path)
|
||||
|
||||
# Move the output datetime folder to the archive folder
|
||||
shutil.move(output_datetime_path, archive_folder_path)
|
||||
print(f"Output archived to: {archive_folder_path}")
|
||||
Reference in New Issue
Block a user