forked from cositools/cosipy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinteractive_deconvolution.py
More file actions
196 lines (161 loc) · 5.99 KB
/
Copy pathinteractive_deconvolution.py
File metadata and controls
196 lines (161 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
"""
Interactive viewer for image-deconvolution results.
A matplotlib slider lets you scrub through iterations in real time.
A Play / Pause button animates the sequence automatically.
Usage
-----
python interactive_deconvolution.py
"""
import os
import numpy as np
import healpy as hp
import matplotlib
matplotlib.use("TkAgg") # change to "Qt5Agg" or "MacOSX" if TkAgg isn't available
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Slider, Button
from cosipy.image_deconvolution.unbinned_image_data_interface import UnbinnedImageDataInterface
from cosipy.image_deconvolution.image_deconvolution import ImageDeconvolution
from cosipy.image_deconvolution.data_interfaces.data_interface_collection import DataInterfaceCollection
# ============================================================
# Configuration
# ============================================================
COMPOSITE = False # set True when PKL came from a multi-file run
PKL_NAME = "ns8e505517n5e5.pkl"
PARAMS_YAML = "deconvolution_params.yaml"
_subdir = "composites" if COMPOSITE else ""
_pkl_dir = os.path.join("Jar of Pickles", _subdir) if _subdir else "Jar of Pickles"
PKL_PATH = os.path.join(_pkl_dir, PKL_NAME)
FRAME_MS = 600 # milliseconds per frame when playing
COORD = "G"
CMAP = "viridis"
GRATICULE = True
LON_SPACING = 30
LAT_SPACING = 30
UNIT = r"cm$^{-2}$ s$^{-1}$ sr$^{-1}$"
LOG_SCALE = False # set False for linear colorbar
MARKER_LON_DEG = None # set to a float (e.g. 0.0) to draw a reference marker
MARKER_LAT_DEG = 0.0
# ============================================================
# Helpers
# ============================================================
def _render_to_array(model_map, iteration, dpi=120):
"""Render one healpy frame and return it as an (H, W, 3) uint8 array."""
if LOG_SCALE:
vmin = max(float(model_map[model_map > 0].min()), float(model_map.max()) * 1e-2)
norm = 'log'
else:
vmin = 0.0
norm = None
hp.projview(
model_map,
title=f"Iteration {iteration}",
unit=UNIT,
cmap=CMAP,
coord=COORD,
graticule=GRATICULE,
graticule_labels=GRATICULE,
longitude_grid_spacing=LON_SPACING,
latitude_grid_spacing=LAT_SPACING,
min=vmin,
norm=norm,
)
if MARKER_LON_DEG is not None:
hp.newprojplot(
MARKER_LON_DEG, MARKER_LAT_DEG,
marker='o', lonlat=True, color='red', markersize=4,
)
fig = plt.gcf()
fig.set_dpi(dpi)
fig.canvas.draw()
buf = fig.canvas.buffer_rgba()
arr = np.asarray(buf)[:, :, :3].copy()
plt.close(fig)
return arr
# ============================================================
# Main
# ============================================================
if __name__ == '__main__':
# ---- Run deconvolution ----
print("Loading interface …")
interface = UnbinnedImageDataInterface.load(PKL_PATH)
dataset = DataInterfaceCollection([interface])
image_decon = ImageDeconvolution()
image_decon.set_dataset(dataset)
image_decon.read_parameterfile(PARAMS_YAML)
image_decon.initialize()
print("Running deconvolution …")
image_decon.run_deconvolution()
results = image_decon.results
n_iter = len(results)
print(f"Deconvolution finished — {n_iter} iteration(s).")
# ---- Collect maps ----
maps = []
iternums = []
for r in results:
contents = r['model'].contents
if hasattr(contents, 'value'):
contents = contents.value
maps.append(np.asarray(contents[:, 0], dtype=float))
iternums.append(r['iteration'])
# ---- Pre-render all frames ----
print("Pre-rendering frames …")
frames = []
for i, (m, it) in enumerate(zip(maps, iternums)):
print(f" frame {i+1}/{n_iter} (iteration {it})")
frames.append(_render_to_array(m, it))
print("Done.")
# ---- Interactive figure ----
fig_i, ax_i = plt.subplots(figsize=(11, 6))
fig_i.subplots_adjust(bottom=0.18, top=0.98, left=0.01, right=0.99)
ax_i.axis('off')
img_display = ax_i.imshow(frames[0], aspect='auto')
ax_i.set_title(f"Iteration {iternums[0]}", fontsize=12)
# -- Slider --
ax_slider = fig_i.add_axes([0.12, 0.08, 0.60, 0.035])
slider = Slider(
ax_slider, 'Iteration',
valmin=1, valmax=n_iter,
valinit=1, valstep=1,
color='steelblue',
)
# Show actual iteration number in the slider label
slider.valtext.set_text(str(iternums[0]))
def update(val):
idx = int(slider.val) - 1
img_display.set_data(frames[idx])
ax_i.set_title(f"Iteration {iternums[idx]} - Galactic Coordinates", fontsize=12)
slider.valtext.set_text(str(iternums[idx]))
fig_i.canvas.draw_idle()
slider.on_changed(update)
# -- Play / Pause button --
ax_play = fig_i.add_axes([0.76, 0.07, 0.10, 0.05])
btn_play = Button(ax_play, '▶ Play', color='0.85', hovercolor='0.75')
_anim = [None]
_playing = [False]
def _advance(_):
next_val = int(slider.val) % n_iter + 1 # wrap at end
slider.set_val(next_val)
def toggle_play(event):
if _playing[0]:
_playing[0] = False
btn_play.label.set_text('▶ Play')
if _anim[0] is not None:
_anim[0].event_source.stop()
else:
_playing[0] = True
btn_play.label.set_text('⏸ Pause')
_anim[0] = animation.FuncAnimation(
fig_i, _advance, interval=FRAME_MS, cache_frame_data=False
)
fig_i.canvas.draw_idle()
btn_play.on_clicked(toggle_play)
# -- Reset button --
ax_reset = fig_i.add_axes([0.88, 0.07, 0.08, 0.05])
btn_reset = Button(ax_reset, '⏮ Reset', color='0.85', hovercolor='0.75')
def reset(event):
if _playing[0]:
toggle_play(None)
slider.set_val(1)
btn_reset.on_clicked(reset)
plt.show()