Coverage for supercat/viz.py: 100.00%
49 statements
« prev ^ index » next coverage.py v7.10.6, created at 2026-09-11 13:26 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2026-09-11 13:26 +0000
1from pathlib import Path
2import numpy as np
3import plotly.graph_objects as go
4from PIL import Image
5from plotly.subplots import make_subplots
6import numpy as np
7import typer
9app = typer.Typer()
12def format_fig(fig):
13 fig.update_layout(
14 plot_bgcolor="white",
15 title_font_color="black",
16 font=dict(
17 family="Linux Libertine Display O",
18 size=18,
19 color="black",
20 ),
21 )
24@app.command()
25def comparison(
26 hr: list[Path] = typer.Option(
27 ..., help="Paths to the original high-resolution images"
28 ),
29 lr: list[Path] = typer.Option(
30 ..., help="Paths to the downscaled low-resolution images"
31 ),
32 sr: list[Path] = typer.Option(
33 ..., help="Paths to the upscaled super-resolution images"
34 ),
35 titles: list[str] = typer.Option(
36 ..., help="Titles for each row, in the same order as the images"
37 ),
38 output: Path = typer.Option(
39 None, help="Path to save the resulting figure (e.g., comparison.html)"
40 ),
41):
42 assert (
43 len(hr) == len(lr) == len(sr) == len(titles)
44 ), "The number of original, downscaled, upscaled images and titles must be the same"
46 fig = make_subplots(
47 rows=len(hr),
48 cols=4,
49 subplot_titles=(
50 "Original",
51 "Downscaled",
52 "Upscaled",
53 "Difference",
54 ),
55 vertical_spacing=0.02,
56 horizontal_spacing=0.02,
57 )
59 def read(x):
60 if not isinstance(x, (Path, str)):
61 return np.asarray(x)
63 x = str(x)
64 if x.endswith(".mat"):
65 from .data import read_mat
67 volume = read_mat(x)
68 return np.asarray(volume[volume.shape[0] // 2]).astype(int)
70 return np.asarray(Image.open(x).convert("L")).astype(int)
72 for row, (original, downscaled, upscaled, title) in enumerate(
73 zip(hr, lr, sr, titles)
74 ):
75 original_im = read(original)
76 downscaled_im = read(
77 downscaled
78 ) # .resize( (original_im.size[0], original_im.size[1]), resample=PIL.Image.Resampling.NEAREST)
79 upscaled = read(upscaled)
81 difference = upscaled - original_im
82 # squared_error = np.power(difference.astype(float)/255, 2.0)
84 fig.add_trace(
85 go.Heatmap(
86 z=np.asarray(original_im).astype(int),
87 colorscale="gray",
88 showscale=False,
89 zmin=0,
90 zmax=255,
91 ),
92 row=row + 1,
93 col=1,
94 )
95 fig.add_trace(
96 go.Heatmap(
97 z=np.asarray(downscaled_im).astype(int),
98 colorscale="gray",
99 showscale=False,
100 zmin=0,
101 zmax=255,
102 ),
103 row=row + 1,
104 col=2,
105 )
106 fig.add_trace(
107 go.Heatmap(
108 z=np.asarray(upscaled).astype(int),
109 colorscale="gray",
110 showscale=False,
111 zmin=0,
112 zmax=255,
113 ),
114 row=row + 1,
115 col=3,
116 )
117 fig.add_trace(
118 go.Heatmap(z=difference, coloraxis="coloraxis2"), row=row + 1, col=4
119 )
121 update_dict = {
122 f"yaxis{1+row*4}_title": title,
123 }
124 fig.update_layout(**update_dict)
125 fig.update_layout(plot_bgcolor="rgba(0,0,0,0)")
126 fig.update_xaxes(showticklabels=False)
127 fig.update_yaxes(showticklabels=False)
128 fig.update_layout(
129 height=150 + 240 * len(hr),
130 width=1200,
131 )
132 format_fig(fig)
134 # fig.update_layout(coloraxis1=dict(colorscale='gray'), showlegend=False)
135 fig.update_layout(coloraxis2=dict(colorscale="Rainbow"), showlegend=False)
136 fig.update_annotations(font_size=24)
138 if output is not None:
139 print(f"Saving figure to {output}")
140 if output.suffix.lower() in [".html", ".htm"]:
141 fig.write_html(output)
142 else:
143 fig.write_image(output, scale=2)
145 return fig
148if __name__ == "__main__":
149 app()