teareduce 0.6.8__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- teareduce/cleanest/__main__.py +74 -22
- teareduce/cleanest/choose_index_with_three_buttons.py +126 -0
- teareduce/cleanest/cosmicraycleanerapp.py +192 -68
- teareduce/cleanest/definitions.py +1 -1
- teareduce/cleanest/interpolationeditor.py +73 -10
- teareduce/cleanest/reviewcosmicray.py +182 -25
- teareduce/simulateccdexposure.py +3 -1
- teareduce/version.py +2 -2
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/METADATA +1 -1
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/RECORD +14 -13
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/WHEEL +1 -1
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/entry_points.txt +0 -0
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/licenses/LICENSE.txt +0 -0
- {teareduce-0.6.8.dist-info → teareduce-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright 2025 Universidad Complutense de Madrid
|
|
2
|
+
# Copyright 2025-2026 Universidad Complutense de Madrid
|
|
3
3
|
#
|
|
4
4
|
# This file is part of teareduce
|
|
5
5
|
#
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
"""Interpolation editor dialog for interpolation parameters."""
|
|
11
11
|
|
|
12
|
+
import os
|
|
12
13
|
import tkinter as tk
|
|
13
14
|
from tkinter import messagebox
|
|
14
15
|
from tkinter import ttk
|
|
@@ -33,7 +34,8 @@ class InterpolationEditor:
|
|
|
33
34
|
last_maskfill_operator,
|
|
34
35
|
last_maskfill_smooth,
|
|
35
36
|
last_maskfill_verbose,
|
|
36
|
-
|
|
37
|
+
auxfile_list,
|
|
38
|
+
extension_auxfile_list,
|
|
37
39
|
cleandata_lacosmic,
|
|
38
40
|
cleandata_pycosmic,
|
|
39
41
|
cleandata_deepcr,
|
|
@@ -63,8 +65,10 @@ class InterpolationEditor:
|
|
|
63
65
|
The last used maskfill smooth parameter.
|
|
64
66
|
last_maskfill_verbose : bool
|
|
65
67
|
The last used maskfill verbose parameter.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
auxfile_list : list of str
|
|
69
|
+
List of paths to auxiliary FITS files.
|
|
70
|
+
extension_auxfile_list : list of str
|
|
71
|
+
List of FITS extensions for auxiliary files.
|
|
68
72
|
cleandata_lacosmic : array-like or None
|
|
69
73
|
Cleaned data from L.A.Cosmic, if available.
|
|
70
74
|
cleandata_pycosmic : array-like or None
|
|
@@ -99,8 +103,17 @@ class InterpolationEditor:
|
|
|
99
103
|
The root Tkinter window.
|
|
100
104
|
last_dilation : int
|
|
101
105
|
The last used dilation parameter.
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
auxfile_list : list of str
|
|
107
|
+
List of paths to auxiliary FITS files.
|
|
108
|
+
extension_auxfile_list : list of str
|
|
109
|
+
List of FITS extensions for auxiliary files.
|
|
110
|
+
naux : int
|
|
111
|
+
Number of auxiliary files.
|
|
112
|
+
extension_auxfile_list : list of str
|
|
113
|
+
List of file names and extensions for auxiliary files.
|
|
114
|
+
auxiliary_data_index : int or None
|
|
115
|
+
Index of the selected auxiliary data option, ranging
|
|
116
|
+
from 1 to naux + 3 (mean, median, minimum), or None if not selected.
|
|
104
117
|
cleandata_lacosmic : array-like or None
|
|
105
118
|
Cleaned data from L.A.Cosmic, if available.
|
|
106
119
|
cleandata_pycosmic : array-like or None
|
|
@@ -135,7 +148,22 @@ class InterpolationEditor:
|
|
|
135
148
|
self.root = root
|
|
136
149
|
self.root.title("Cleaning Parameters")
|
|
137
150
|
self.last_dilation = last_dilation
|
|
138
|
-
self.
|
|
151
|
+
self.auxfile_list = auxfile_list
|
|
152
|
+
self.naux = len(auxfile_list)
|
|
153
|
+
if len(extension_auxfile_list) != self.naux:
|
|
154
|
+
raise ValueError("Length of extension_auxfile_list must match length of auxfile_list.")
|
|
155
|
+
self.extension_auxfile_list = extension_auxfile_list
|
|
156
|
+
if self.naux == 0:
|
|
157
|
+
self.auxdata_options = []
|
|
158
|
+
else:
|
|
159
|
+
self.auxdata_options = [
|
|
160
|
+
f"{os.path.basename(self.auxfile_list[i])}[{self.extension_auxfile_list[i]}]" for i in range(self.naux)
|
|
161
|
+
]
|
|
162
|
+
if self.naux > 1:
|
|
163
|
+
self.auxdata_options.extend(
|
|
164
|
+
["MEAN of auxiliary data", "MEDIAN of auxiliary data", "MINIMUM of auxiliary data"]
|
|
165
|
+
)
|
|
166
|
+
self.auxiliary_data_index = None # to be set when OK is pressed
|
|
139
167
|
self.cleandata_lacosmic = cleandata_lacosmic
|
|
140
168
|
self.cleandata_pycosmic = cleandata_pycosmic
|
|
141
169
|
self.cleandata_deepcr = cleandata_deepcr
|
|
@@ -158,6 +186,11 @@ class InterpolationEditor:
|
|
|
158
186
|
self.create_widgets()
|
|
159
187
|
center_on_parent(child=self.root, parent=self.root.master)
|
|
160
188
|
|
|
189
|
+
def on_combo_auxdata_change(self, event):
|
|
190
|
+
"""Handle changes in the auxiliary data combobox selection."""
|
|
191
|
+
self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
|
|
192
|
+
self.action_on_method_change()
|
|
193
|
+
|
|
161
194
|
def create_widgets(self):
|
|
162
195
|
"""Create the widgets for the dialog."""
|
|
163
196
|
# Main frame
|
|
@@ -196,7 +229,7 @@ class InterpolationEditor:
|
|
|
196
229
|
if self.cleandata_deepcr is None:
|
|
197
230
|
state = "disabled"
|
|
198
231
|
# Skip auxdata method if auxdata is not available
|
|
199
|
-
elif interp_method == "
|
|
232
|
+
elif interp_method == "Auxiliary data" and self.naux == 0:
|
|
200
233
|
state = "disabled"
|
|
201
234
|
tk.Radiobutton(
|
|
202
235
|
main_frame,
|
|
@@ -210,6 +243,21 @@ class InterpolationEditor:
|
|
|
210
243
|
if column > 6:
|
|
211
244
|
column = 0
|
|
212
245
|
row += 1
|
|
246
|
+
# Add combobox to choose auxdata if auxdata method is selected and naux > 0:
|
|
247
|
+
if self.naux > 0:
|
|
248
|
+
self.auxdata_var = tk.StringVar(value=self.auxdata_options[0])
|
|
249
|
+
self.auxdata_combobox = ttk.Combobox(
|
|
250
|
+
main_frame,
|
|
251
|
+
textvariable=self.auxdata_var,
|
|
252
|
+
values=self.auxdata_options,
|
|
253
|
+
state="readonly",
|
|
254
|
+
width=40,
|
|
255
|
+
)
|
|
256
|
+
self.auxdata_combobox.grid(row=row, column=column, columnspan=4, sticky="w", padx=5, pady=5)
|
|
257
|
+
self.auxdata_combobox.config(state="disabled")
|
|
258
|
+
# --- Bind the selection event to the handler
|
|
259
|
+
self.auxdata_combobox.bind("<<ComboboxSelected>>", self.on_combo_auxdata_change)
|
|
260
|
+
|
|
213
261
|
row += 1
|
|
214
262
|
|
|
215
263
|
# Separator
|
|
@@ -335,6 +383,7 @@ class InterpolationEditor:
|
|
|
335
383
|
def on_ok(self):
|
|
336
384
|
"""Handle the OK button click event."""
|
|
337
385
|
self.cleaning_method = VALID_CLEANING_METHODS[self.cleaning_method_var.get()]
|
|
386
|
+
|
|
338
387
|
try:
|
|
339
388
|
self.npoints = int(self.entry_npoints.get())
|
|
340
389
|
except ValueError:
|
|
@@ -434,6 +483,11 @@ class InterpolationEditor:
|
|
|
434
483
|
return
|
|
435
484
|
self.__dict__[key] = value
|
|
436
485
|
|
|
486
|
+
if self.cleaning_method == "auxdata":
|
|
487
|
+
# set auxiliary_data_index to the index of the selected auxdata option
|
|
488
|
+
# ranging from 1 to naux + 3 (mean, median, minimum)
|
|
489
|
+
self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
|
|
490
|
+
|
|
437
491
|
self.root.destroy()
|
|
438
492
|
|
|
439
493
|
def on_cancel(self):
|
|
@@ -446,7 +500,16 @@ class InterpolationEditor:
|
|
|
446
500
|
def action_on_method_change(self):
|
|
447
501
|
"""Handle changes in the selected cleaning method."""
|
|
448
502
|
selected_method = self.cleaning_method_var.get()
|
|
449
|
-
|
|
503
|
+
if selected_method == "Auxiliary data":
|
|
504
|
+
self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
|
|
505
|
+
print(
|
|
506
|
+
f"Selected cleaning method: [red bold]{selected_method}[/red bold]: {self.auxdata_options[self.auxiliary_data_index - 1]}"
|
|
507
|
+
)
|
|
508
|
+
self.auxdata_combobox.config(state="readonly")
|
|
509
|
+
else:
|
|
510
|
+
print(f"Selected cleaning method: [red bold]{selected_method}[/red bold]")
|
|
511
|
+
self.auxdata_combobox.config(state="disabled")
|
|
512
|
+
|
|
450
513
|
if selected_method in ["x interp.", "y interp."]:
|
|
451
514
|
self.entry_npoints.config(state="normal")
|
|
452
515
|
self.entry_degree.config(state="normal")
|
|
@@ -475,7 +538,7 @@ class InterpolationEditor:
|
|
|
475
538
|
self.entry_maskfill_operator.config(state="disabled")
|
|
476
539
|
self.entry_maskfill_smooth.config(state="disabled")
|
|
477
540
|
self.entry_maskfill_verbose.config(state="disabled")
|
|
478
|
-
elif selected_method in ["L.A.Cosmic", "PyCosmic", "deepCR", "
|
|
541
|
+
elif selected_method in ["L.A.Cosmic", "PyCosmic", "deepCR", "Auxiliary data"]:
|
|
479
542
|
self.entry_npoints.config(state="disabled")
|
|
480
543
|
self.entry_degree.config(state="disabled")
|
|
481
544
|
self.entry_maskfill_size.config(state="disabled")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright 2025 Universidad Complutense de Madrid
|
|
2
|
+
# Copyright 2025-2026 Universidad Complutense de Madrid
|
|
3
3
|
#
|
|
4
4
|
# This file is part of teareduce
|
|
5
5
|
#
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
"""Define the ReviewCosmicRay class."""
|
|
11
11
|
|
|
12
|
+
import os
|
|
12
13
|
import tkinter as tk
|
|
14
|
+
from tkinter import ttk
|
|
13
15
|
from tkinter import messagebox
|
|
14
16
|
from tkinter import simpledialog
|
|
15
17
|
|
|
@@ -55,8 +57,14 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
55
57
|
root,
|
|
56
58
|
root_width,
|
|
57
59
|
root_height,
|
|
60
|
+
input_fits,
|
|
58
61
|
data,
|
|
59
62
|
auxdata,
|
|
63
|
+
auxfile_list,
|
|
64
|
+
extension_auxfile_list,
|
|
65
|
+
auxdata_mean,
|
|
66
|
+
auxdata_median,
|
|
67
|
+
auxdata_min,
|
|
60
68
|
cleandata_lacosmic,
|
|
61
69
|
cleandata_pycosmic,
|
|
62
70
|
cleandata_deepcr,
|
|
@@ -82,10 +90,22 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
82
90
|
The width of the root window. The review window is scaled accordingly.
|
|
83
91
|
root_height : int
|
|
84
92
|
The height of the root window. The review window is scaled accordingly.
|
|
93
|
+
input_fits : str
|
|
94
|
+
Path to the FITS file to be cleaned.
|
|
85
95
|
data : 2D numpy array
|
|
86
96
|
The original image data.
|
|
87
97
|
auxdata : 2D numpy array or None
|
|
88
98
|
The auxiliary image data.
|
|
99
|
+
auxfile_list : list of str
|
|
100
|
+
List of auxiliary FITS file names.
|
|
101
|
+
extension_auxfile_list : list of str
|
|
102
|
+
List of FITS extensions for auxiliary files.
|
|
103
|
+
auxdata_mean : 2D numpy array or None
|
|
104
|
+
The mean auxiliary image data.
|
|
105
|
+
auxdata_median : 2D numpy array or None
|
|
106
|
+
The median auxiliary image data.
|
|
107
|
+
auxdata_min : 2D numpy array or None
|
|
108
|
+
The minimum auxiliary image data.
|
|
89
109
|
cleandata_lacosmic: 2D numpy array or None
|
|
90
110
|
The cleaned image data from L.A.Cosmic.
|
|
91
111
|
cleandata_pycosmic: 2D numpy array or None
|
|
@@ -160,10 +180,22 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
160
180
|
The scaling factor for the width of the review window.
|
|
161
181
|
factor_height : float
|
|
162
182
|
The scaling factor for the height of the review window.
|
|
183
|
+
input_fits : str
|
|
184
|
+
Path to the FITS file to be cleaned.
|
|
163
185
|
data : 2D numpy array
|
|
164
186
|
The original image data.
|
|
165
187
|
auxdata : 2D numpy array or None
|
|
166
188
|
The auxiliary image data.
|
|
189
|
+
auxfile_list : list of str
|
|
190
|
+
List of auxiliary FITS file names.
|
|
191
|
+
extension_auxfile_list : list of str
|
|
192
|
+
List of FITS extensions for auxiliary files.
|
|
193
|
+
auxdata_mean : 2D numpy array or None
|
|
194
|
+
The mean auxiliary image data.
|
|
195
|
+
auxdata_median : 2D numpy array or None
|
|
196
|
+
The median auxiliary image data.
|
|
197
|
+
auxdata_min : 2D numpy array or None
|
|
198
|
+
The minimum auxiliary image data.
|
|
167
199
|
cleandata_lacosmic: 2D numpy array or None
|
|
168
200
|
The cleaned image data from L.A.Cosmic.
|
|
169
201
|
cleandata_pycosmic: 2D numpy array or None
|
|
@@ -200,17 +232,46 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
200
232
|
self.root.title(f"Review Cosmic Rays (TEA version {VERSION})")
|
|
201
233
|
self.factor_width = root_width / DEFAULT_TK_WINDOW_SIZE_X
|
|
202
234
|
self.factor_height = root_height / DEFAULT_TK_WINDOW_SIZE_Y
|
|
235
|
+
self.input_fits = input_fits
|
|
203
236
|
self.auxdata = auxdata
|
|
204
|
-
|
|
205
|
-
|
|
237
|
+
self.naux = len(self.auxdata)
|
|
238
|
+
if self.naux > 0:
|
|
239
|
+
# self.root.geometry("1000x800+100+100") # This does not work in Fedora
|
|
206
240
|
window_width = int(1000 * self.factor_width + 0.5)
|
|
207
|
-
window_height = int(
|
|
241
|
+
window_height = int(800 * self.factor_height + 0.5)
|
|
208
242
|
self.root.minsize(window_width, window_height)
|
|
209
243
|
else:
|
|
210
|
-
# self.root.geometry("
|
|
211
|
-
window_width = int(
|
|
212
|
-
window_height = int(
|
|
244
|
+
# self.root.geometry("950x800+100+100") # This does not work in Fedora
|
|
245
|
+
window_width = int(950 * self.factor_width + 0.5)
|
|
246
|
+
window_height = int(800 * self.factor_height + 0.5)
|
|
213
247
|
self.root.minsize(window_width, window_height)
|
|
248
|
+
if len(auxfile_list) != self.naux:
|
|
249
|
+
raise ValueError("Length of auxfile_list must match length of auxdata.")
|
|
250
|
+
if len(extension_auxfile_list) != self.naux:
|
|
251
|
+
raise ValueError("Length of extension_auxfile_list must match length of auxdata.")
|
|
252
|
+
self.auxfile_list = auxfile_list
|
|
253
|
+
self.extension_auxfile_list = extension_auxfile_list
|
|
254
|
+
self.extension_auxfile_list = extension_auxfile_list
|
|
255
|
+
if self.naux == 0:
|
|
256
|
+
self.auxdata_options = []
|
|
257
|
+
self.auxiliary_data_index = None
|
|
258
|
+
else:
|
|
259
|
+
self.auxdata_options = [
|
|
260
|
+
f"#[{i+1}]: {os.path.basename(self.auxfile_list[i])}[{self.extension_auxfile_list[i]}]"
|
|
261
|
+
for i in range(self.naux)
|
|
262
|
+
]
|
|
263
|
+
if self.naux > 1:
|
|
264
|
+
self.auxdata_options.extend(
|
|
265
|
+
[
|
|
266
|
+
f"#[{self.naux+1}]: MEAN of auxiliary data",
|
|
267
|
+
f"#[{self.naux+2}]: MEDIAN of auxiliary data",
|
|
268
|
+
f"#[{self.naux+3}]: MINIMUM of auxiliary data",
|
|
269
|
+
]
|
|
270
|
+
)
|
|
271
|
+
self.auxiliary_data_index = 0 # first auxiliary data by default
|
|
272
|
+
self.auxdata_mean = auxdata_mean
|
|
273
|
+
self.auxdata_median = auxdata_median
|
|
274
|
+
self.auxdata_min = auxdata_min
|
|
214
275
|
self.root.update_idletasks()
|
|
215
276
|
self.root.geometry("+100+100")
|
|
216
277
|
self.data = data
|
|
@@ -247,6 +308,22 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
247
308
|
self.create_widgets()
|
|
248
309
|
center_on_parent(child=self.root, parent=self.root.master, offset_x=50, offset_y=50)
|
|
249
310
|
|
|
311
|
+
def on_combo_auxdata_select(self, event):
|
|
312
|
+
"""Handle selection of auxiliary data from the combobox."""
|
|
313
|
+
selected_option = self.auxdata_var.get()
|
|
314
|
+
self.auxiliary_data_index = self.auxdata_options.index(selected_option) + 1
|
|
315
|
+
self.update_display()
|
|
316
|
+
|
|
317
|
+
# Return focus to the Matplotlib figure to keep keyboard shortcuts working
|
|
318
|
+
# (requires a tiny delay to avoid conflicts with the combobox event handling)
|
|
319
|
+
def give_focus_to_canvas():
|
|
320
|
+
self.canvas.get_tk_widget().focus_set()
|
|
321
|
+
|
|
322
|
+
if event is None:
|
|
323
|
+
self.root.after(10, give_focus_to_canvas)
|
|
324
|
+
else:
|
|
325
|
+
event.widget.after(10, give_focus_to_canvas)
|
|
326
|
+
|
|
250
327
|
def create_widgets(self):
|
|
251
328
|
"""Create the GUI widgets for the review window."""
|
|
252
329
|
# Define instance of TrackedTkButton, that facilitates to show help information
|
|
@@ -290,6 +367,15 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
290
367
|
)
|
|
291
368
|
self.restore_cr_button.pack(side=tk.LEFT, padx=5)
|
|
292
369
|
self.restore_cr_button.config(state=tk.DISABLED)
|
|
370
|
+
# --- Fix CR button
|
|
371
|
+
self.fix_cr_button = tkbutton.new(
|
|
372
|
+
self.button_frame1,
|
|
373
|
+
text="[f]ix CR",
|
|
374
|
+
command=self.fix_cr_interpolation,
|
|
375
|
+
help_text="Fix current interpolation and allow new one.",
|
|
376
|
+
)
|
|
377
|
+
self.fix_cr_button.pack(side=tk.LEFT, padx=5)
|
|
378
|
+
self.fix_cr_button.config(state=tk.DISABLED)
|
|
293
379
|
# --- Next button
|
|
294
380
|
self.next_button = tkbutton.new(
|
|
295
381
|
self.button_frame1,
|
|
@@ -403,24 +489,45 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
403
489
|
help_text="Perform maskfill interpolation for the current cosmic ray.",
|
|
404
490
|
)
|
|
405
491
|
self.interp_maskfill_button.pack(side=tk.LEFT, padx=5)
|
|
492
|
+
|
|
493
|
+
# Row 4 of buttons
|
|
494
|
+
self.button_frame4 = tk.Frame(self.root)
|
|
495
|
+
self.button_frame4.pack(pady=5)
|
|
406
496
|
# --- Interpolation using auxiliary data button
|
|
407
497
|
self.interp_aux_button = tkbutton.new(
|
|
408
|
-
self.
|
|
498
|
+
self.button_frame4,
|
|
409
499
|
text="[a]ux. data",
|
|
410
500
|
command=self.use_auxdata,
|
|
411
501
|
help_text="Use auxiliary data for interpolation of the current cosmic ray.",
|
|
412
502
|
)
|
|
413
503
|
self.interp_aux_button.pack(side=tk.LEFT, padx=5)
|
|
414
|
-
if self.
|
|
504
|
+
if self.naux == 0:
|
|
415
505
|
self.interp_aux_button.config(state=tk.DISABLED)
|
|
506
|
+
# --- Add combobox to choose auxdata if auxdata method is selected and naux > 0:
|
|
507
|
+
if self.naux == 0:
|
|
508
|
+
self.auxdata_var = tk.StringVar(value="No auxiliary data available")
|
|
509
|
+
else:
|
|
510
|
+
self.auxdata_var = tk.StringVar(value=self.auxdata_options[self.auxiliary_data_index])
|
|
511
|
+
self.auxdata_combobox = ttk.Combobox(
|
|
512
|
+
self.button_frame4,
|
|
513
|
+
textvariable=self.auxdata_var,
|
|
514
|
+
values=self.auxdata_options,
|
|
515
|
+
state="readonly",
|
|
516
|
+
width=40,
|
|
517
|
+
)
|
|
518
|
+
self.auxdata_combobox.pack(side=tk.LEFT, padx=5)
|
|
519
|
+
if self.naux == 0:
|
|
520
|
+
self.auxdata_combobox.config(state="disabled")
|
|
521
|
+
# --- Bind the selection event to the handler
|
|
522
|
+
self.auxdata_combobox.bind("<<ComboboxSelected>>", self.on_combo_auxdata_select)
|
|
416
523
|
|
|
417
|
-
# Row
|
|
418
|
-
self.
|
|
419
|
-
self.
|
|
524
|
+
# Row 5 of buttons
|
|
525
|
+
self.button_frame5 = tk.Frame(self.root)
|
|
526
|
+
self.button_frame5.pack(pady=5)
|
|
420
527
|
# --- vmin button
|
|
421
528
|
vmin, vmax = zscale(self.data)
|
|
422
529
|
self.vmin_button = tkbutton.new(
|
|
423
|
-
self.
|
|
530
|
+
self.button_frame5,
|
|
424
531
|
text=f"vmin: {vmin:.2f}",
|
|
425
532
|
command=self.set_vmin,
|
|
426
533
|
help_text="Set the minimum value for the display scale.",
|
|
@@ -429,7 +536,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
429
536
|
self.vmin_button.pack(side=tk.LEFT, padx=5)
|
|
430
537
|
# --- vmax button
|
|
431
538
|
self.vmax_button = tkbutton.new(
|
|
432
|
-
self.
|
|
539
|
+
self.button_frame5,
|
|
433
540
|
text=f"vmax: {vmax:.2f}",
|
|
434
541
|
command=self.set_vmax,
|
|
435
542
|
help_text="Set the maximum value for the display scale.",
|
|
@@ -438,7 +545,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
438
545
|
self.vmax_button.pack(side=tk.LEFT, padx=5)
|
|
439
546
|
# --- minmax button
|
|
440
547
|
self.set_minmax_button = tkbutton.new(
|
|
441
|
-
self.
|
|
548
|
+
self.button_frame5,
|
|
442
549
|
text="minmax [,]",
|
|
443
550
|
command=self.set_minmax,
|
|
444
551
|
help_text="Set the display scale to the minimum and maximum data values.",
|
|
@@ -446,7 +553,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
446
553
|
self.set_minmax_button.pack(side=tk.LEFT, padx=5)
|
|
447
554
|
# --- zscale button
|
|
448
555
|
self.set_zscale_button = tkbutton.new(
|
|
449
|
-
self.
|
|
556
|
+
self.button_frame5,
|
|
450
557
|
text="zscale [/]",
|
|
451
558
|
command=self.set_zscale,
|
|
452
559
|
help_text="Set the display scale using zscale.",
|
|
@@ -454,7 +561,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
454
561
|
self.set_zscale_button.pack(side=tk.LEFT, padx=5)
|
|
455
562
|
# --- Help button
|
|
456
563
|
self.help_button = tkbutton.new(
|
|
457
|
-
self.
|
|
564
|
+
self.button_frame5,
|
|
458
565
|
text="Help",
|
|
459
566
|
command=tkbutton.show_help,
|
|
460
567
|
help_text="Show help information for all buttons.",
|
|
@@ -462,7 +569,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
462
569
|
self.help_button.pack(side=tk.LEFT, padx=5)
|
|
463
570
|
|
|
464
571
|
# Figure
|
|
465
|
-
if self.
|
|
572
|
+
if self.naux > 0:
|
|
466
573
|
self.fig, (self.ax, self.ax_aux) = plt.subplots(
|
|
467
574
|
ncols=2, figsize=(11 * self.factor_width, 5.5 * self.factor_height), constrained_layout=True
|
|
468
575
|
)
|
|
@@ -546,12 +653,23 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
546
653
|
vmax=vmax,
|
|
547
654
|
)
|
|
548
655
|
self.image.set_extent([jmin + 0.5, jmax + 1.5, imin + 0.5, imax + 1.5])
|
|
549
|
-
if self.
|
|
656
|
+
if self.naux > 0:
|
|
657
|
+
self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
|
|
658
|
+
if 1 <= self.auxiliary_data_index <= self.naux:
|
|
659
|
+
auximg = self.auxdata[self.auxiliary_data_index - 1]
|
|
660
|
+
elif self.auxiliary_data_index == self.naux + 1:
|
|
661
|
+
auximg = self.auxdata_mean
|
|
662
|
+
elif self.auxiliary_data_index == self.naux + 2:
|
|
663
|
+
auximg = self.auxdata_median
|
|
664
|
+
elif self.auxiliary_data_index == self.naux + 3:
|
|
665
|
+
auximg = self.auxdata_min
|
|
666
|
+
else:
|
|
667
|
+
raise ValueError(f"Invalid auxiliary data index: {self.auxiliary_data_index}")
|
|
550
668
|
self.ax_aux.clear()
|
|
551
669
|
self.image_aux, _, _ = imshow(
|
|
552
670
|
self.fig,
|
|
553
671
|
self.ax_aux,
|
|
554
|
-
|
|
672
|
+
auximg[self.region],
|
|
555
673
|
colorbar=False,
|
|
556
674
|
xlabel=xlabel,
|
|
557
675
|
ylabel=ylabel,
|
|
@@ -559,7 +677,10 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
559
677
|
vmax=vmax,
|
|
560
678
|
)
|
|
561
679
|
self.image_aux.set_extent([jmin + 0.5, jmax + 1.5, imin + 0.5, imax + 1.5])
|
|
562
|
-
self.
|
|
680
|
+
dumfname = self.auxdata_options[self.auxiliary_data_index - 1]
|
|
681
|
+
dumi = dumfname.find(":")
|
|
682
|
+
dumc = dumfname[:dumi].replace("[", "").replace("]", "")
|
|
683
|
+
self.ax_aux.set_title(f"Auxiliary data {dumc}:\n{dumfname[dumi+1:].strip()}")
|
|
563
684
|
# Overplot cosmic ray pixels
|
|
564
685
|
xlim = self.ax.get_xlim()
|
|
565
686
|
ylim = self.ax.get_ylim()
|
|
@@ -576,7 +697,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
576
697
|
if self.first_cr_index == 0:
|
|
577
698
|
self.ax.set_title("Selecting CR pixels with mouse cursor")
|
|
578
699
|
else:
|
|
579
|
-
self.ax.set_title(f"Cosmic ray #{self.cr_index}/{self.num_features}")
|
|
700
|
+
self.ax.set_title(f"Cosmic ray #{self.cr_index}/{self.num_features}\n{os.path.basename(self.input_fits)}")
|
|
580
701
|
if self.first_plot:
|
|
581
702
|
self.first_plot = False
|
|
582
703
|
self.canvas.draw_idle()
|
|
@@ -654,6 +775,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
654
775
|
"""Set the state of buttons after cleaning a cosmic ray."""
|
|
655
776
|
self.disable_interpolation_buttons()
|
|
656
777
|
self.restore_cr_button.config(state=tk.NORMAL)
|
|
778
|
+
self.fix_cr_button.config(state=tk.NORMAL)
|
|
657
779
|
self.remove_crosses_button.config(state=tk.DISABLED)
|
|
658
780
|
|
|
659
781
|
def interp_x(self):
|
|
@@ -800,13 +922,25 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
800
922
|
|
|
801
923
|
def use_auxdata(self):
|
|
802
924
|
"""Use auxiliary data to clean a cosmic ray."""
|
|
803
|
-
if self.
|
|
925
|
+
if self.naux == 0:
|
|
804
926
|
print("Auxiliary data not available.")
|
|
805
927
|
return
|
|
806
928
|
print(f"Auxiliary data interpolation of cosmic ray {self.cr_index}")
|
|
929
|
+
self.auxiliary_data_index = self.auxdata_options.index(self.auxdata_var.get()) + 1
|
|
930
|
+
print(f"Using auxiliary data: {self.auxdata_options[self.auxiliary_data_index-1]}")
|
|
807
931
|
ycr_list, xcr_list = np.where(self.cr_labels == self.cr_index)
|
|
932
|
+
if 1 <= self.auxiliary_data_index <= self.naux:
|
|
933
|
+
replacement_data = self.auxdata[self.auxiliary_data_index - 1]
|
|
934
|
+
elif self.auxiliary_data_index == self.naux + 1:
|
|
935
|
+
replacement_data = self.auxdata_mean
|
|
936
|
+
elif self.auxiliary_data_index == self.naux + 2:
|
|
937
|
+
replacement_data = self.auxdata_median
|
|
938
|
+
elif self.auxiliary_data_index == self.naux + 3:
|
|
939
|
+
replacement_data = self.auxdata_min
|
|
940
|
+
else:
|
|
941
|
+
raise ValueError(f"Invalid auxiliary data index: {self.auxiliary_data_index}.")
|
|
808
942
|
for iy, ix in zip(ycr_list, xcr_list):
|
|
809
|
-
self.data[iy, ix] =
|
|
943
|
+
self.data[iy, ix] = replacement_data[iy, ix]
|
|
810
944
|
self.mask_fixed[iy, ix] = True
|
|
811
945
|
self.num_cr_cleaned += 1
|
|
812
946
|
self.set_buttons_after_cleaning_cr()
|
|
@@ -835,6 +969,17 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
835
969
|
self.enable_interpolation_buttons()
|
|
836
970
|
self.remove_crosses_button.config(state=tk.NORMAL)
|
|
837
971
|
self.restore_cr_button.config(state=tk.DISABLED)
|
|
972
|
+
self.fix_cr_button.config(state=tk.DISABLED)
|
|
973
|
+
self.update_display()
|
|
974
|
+
|
|
975
|
+
def fix_cr_interpolation(self):
|
|
976
|
+
"""Fix the current interpolation and allow new one."""
|
|
977
|
+
print(f"Fixed interpolation for cosmic ray {self.cr_index}")
|
|
978
|
+
ycr_list, xcr_list = np.where(self.cr_labels == self.cr_index)
|
|
979
|
+
for iy, ix in zip(ycr_list, xcr_list):
|
|
980
|
+
self.cr_labels[iy, ix] = 0
|
|
981
|
+
self.restore_cr_button.config(state=tk.DISABLED)
|
|
982
|
+
self.fix_cr_button.config(state=tk.DISABLED)
|
|
838
983
|
self.update_display()
|
|
839
984
|
|
|
840
985
|
def continue_cr(self):
|
|
@@ -848,6 +993,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
848
993
|
return # important: do not remove (to avoid errors)
|
|
849
994
|
self.first_plot = True
|
|
850
995
|
self.restore_cr_button.config(state=tk.DISABLED)
|
|
996
|
+
self.fix_cr_button.config(state=tk.DISABLED)
|
|
851
997
|
self.enable_interpolation_buttons()
|
|
852
998
|
self.remove_crosses_button.config(state=tk.NORMAL)
|
|
853
999
|
self.update_display()
|
|
@@ -863,6 +1009,9 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
863
1009
|
elif event.key == "r":
|
|
864
1010
|
if self.restore_cr_button.cget("state") != "disabled":
|
|
865
1011
|
self.restore_cr()
|
|
1012
|
+
elif event.key == "f":
|
|
1013
|
+
if self.fix_cr_button.cget("state") != "disabled":
|
|
1014
|
+
self.fix_cr_interpolation()
|
|
866
1015
|
elif event.key == "x":
|
|
867
1016
|
if self.interp_x_button.cget("state") != "disabled":
|
|
868
1017
|
self.interp_x()
|
|
@@ -896,6 +1045,12 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
896
1045
|
elif event.key == "e":
|
|
897
1046
|
self.exit_review()
|
|
898
1047
|
return # important: do not remove (to avoid errors)
|
|
1048
|
+
elif event.key in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
|
|
1049
|
+
if self.naux > 1 and int(event.key) <= self.naux + 3:
|
|
1050
|
+
index = int(event.key) - 1
|
|
1051
|
+
if 0 <= index < len(self.auxdata_options):
|
|
1052
|
+
self.auxdata_var.set(self.auxdata_options[index])
|
|
1053
|
+
self.on_combo_auxdata_select(None)
|
|
899
1054
|
else:
|
|
900
1055
|
print(f"Key pressed: {event.key}")
|
|
901
1056
|
|
|
@@ -933,6 +1088,7 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
933
1088
|
self.interp_deepcr_button.config(state=tk.DISABLED)
|
|
934
1089
|
self.interp_maskfill_button.config(state=tk.DISABLED)
|
|
935
1090
|
self.interp_aux_button.config(state=tk.DISABLED)
|
|
1091
|
+
self.auxdata_combobox.config(state=tk.DISABLED)
|
|
936
1092
|
|
|
937
1093
|
def enable_interpolation_buttons(self):
|
|
938
1094
|
"""Enable all interpolation buttons."""
|
|
@@ -949,5 +1105,6 @@ class ReviewCosmicRay(ImageDisplay):
|
|
|
949
1105
|
if self.cleandata_deepcr is not None:
|
|
950
1106
|
self.interp_deepcr_button.config(state=tk.NORMAL)
|
|
951
1107
|
self.interp_maskfill_button.config(state=tk.NORMAL)
|
|
952
|
-
if self.
|
|
1108
|
+
if self.naux > 0:
|
|
953
1109
|
self.interp_aux_button.config(state=tk.NORMAL)
|
|
1110
|
+
self.auxdata_combobox.config(state="readonly")
|
teareduce/simulateccdexposure.py
CHANGED
|
@@ -194,7 +194,7 @@ class SimulateCCDExposure:
|
|
|
194
194
|
|
|
195
195
|
CCD exposures are simulated making use of basic CCD parameters,
|
|
196
196
|
such as gain, readout noise, bias, dark and flat field.
|
|
197
|
-
A data model can also be employed to simulate more
|
|
197
|
+
A data model can also be employed to simulate more realistic
|
|
198
198
|
CCD exposures.
|
|
199
199
|
|
|
200
200
|
The saturated pixels in 'data_model' are returned as 2**bitpix - 1
|
|
@@ -230,6 +230,8 @@ class SimulateCCDExposure:
|
|
|
230
230
|
Numpy array with the pixel to pixel sensitivity (without units).
|
|
231
231
|
data_model : Quantity
|
|
232
232
|
Numpy array with the model of the source to be simulated (ADU).
|
|
233
|
+
Note that this is the model before applying the flatfield.
|
|
234
|
+
The values in this array must be the total ADU for each pixel.
|
|
233
235
|
seed : int or None
|
|
234
236
|
Random number generator seed.
|
|
235
237
|
_rng : np.random.RandomState
|
teareduce/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright 2023-
|
|
3
|
+
# Copyright 2023-2026 Universidad Complutense de Madrid
|
|
4
4
|
#
|
|
5
5
|
# This file is part of teareduce
|
|
6
6
|
#
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#
|
|
10
10
|
"""Module to define the version of the teareduce package."""
|
|
11
11
|
|
|
12
|
-
VERSION = '0.
|
|
12
|
+
VERSION = '0.7.0'
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def main():
|