Files
mt/tools/plot_scripts/data_spherical_projection.py

196 lines
6.4 KiB
Python
Raw Permalink Normal View History

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(
2025-08-28 18:36:02 +02:00
"/home/fedex/mt/data/subter/1_loop_closure_illuminated_2023-01-23.npy"
),
help="Path to first .npy file containing 2D projection data",
)
parser.add_argument(
"--input2",
type=Path,
2025-08-28 18:36:02 +02:00
default=Path("/home/fedex/mt/data/subter/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
)
2025-10-21 19:04:19 +02:00
# --- Create a figure with 2 vertical subplots and move titles to the left ---
fig, (ax1, ax2) = plt.subplots(nrows=2, ncols=1, figsize=(10, 5))
2025-10-21 19:04:19 +02:00
# leave extra left margin for the left-side labels
fig.subplots_adjust(left=0.14, hspace=0.05)
for ax, frame, label in zip(
(ax1, ax2),
(frame1, frame2),
2025-10-21 19:04:19 +02:00
("(a)", "(b)"),
):
im = ax.imshow(frame, cmap=cmap, aspect="auto", vmin=global_vmin, vmax=global_vmax)
2025-10-21 19:04:19 +02:00
# place the "title" to the left, vertically centered relative to the axes
ax.text(
-0.02, # negative x places text left of the axes (in axes coordinates)
0.5,
label,
transform=ax.transAxes,
va="center",
ha="right",
fontsize=12,
)
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)
2025-08-28 18:36:02 +02:00
cbar.set_label("Reciprocal 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}")