teareduce 0.5.9__py3-none-any.whl → 0.6.1__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.
@@ -9,26 +9,56 @@
9
9
 
10
10
  """Definitions for the cleanest module."""
11
11
 
12
+ VALID_LACOSMIC_CLEANTYPE_VALUES = ["median", "medmask", "meanmask", "idw"]
13
+ VALID_LACOSMIC_FSMODE_VALUES = ["median", "convolve"]
14
+ VALID_LACOSMIC_PSFMODEL_VALUES = ["gauss", "moffat", "gaussx", "gaussy", "gaussxy"]
15
+
12
16
  # Default parameters for L.A.Cosmic algorithm
13
17
  # Note that 'type' is set to the expected data type for each parameter
14
18
  # using the intrinsic Python types, so that they can be easily cast
15
19
  # when reading user input.
16
20
  lacosmic_default_dict = {
17
21
  # L.A.Cosmic parameters for run 1
18
- "run1_gain": {"value": 1.0, "type": float, "positive": True},
19
- "run1_readnoise": {"value": 6.5, "type": float, "positive": True},
20
22
  "run1_sigclip": {"value": 5.0, "type": float, "positive": True},
21
23
  "run1_sigfrac": {"value": 0.3, "type": float, "positive": True},
22
24
  "run1_objlim": {"value": 5.0, "type": float, "positive": True},
23
- "run1_niter": {"value": 4, "type": int, "positive": True},
25
+ "run1_gain": {"value": 1.0, "type": float, "positive": True},
26
+ "run1_readnoise": {"value": 6.5, "type": float, "positive": True},
27
+ "run1_satlevel": {"value": 65535, "type": float, "positive": True},
28
+ "run1_niter": {"value": 4, "type": int, "positive": True, "intmode": "any"},
29
+ "run1_sepmed": {"value": True, "type": bool},
30
+ "run1_cleantype": {"value": "meanmask", "type": str, "valid_values": VALID_LACOSMIC_CLEANTYPE_VALUES},
31
+ "run1_fsmode": {"value": "median", "type": str, "valid_values": VALID_LACOSMIC_FSMODE_VALUES},
32
+ "run1_psfmodel": {
33
+ "value": "gaussxy",
34
+ "type": str,
35
+ "valid_values": VALID_LACOSMIC_PSFMODEL_VALUES,
36
+ },
37
+ "run1_psffwhm_x": {"value": 2.5, "type": float, "positive": True},
38
+ "run1_psffwhm_y": {"value": 2.5, "type": float, "positive": True},
39
+ "run1_psfsize": {"value": 7, "type": int, "positive": True, "intmode": "odd"},
40
+ "run1_psfbeta": {"value": 4.765, "type": float, "positive": True},
24
41
  "run1_verbose": {"value": True, "type": bool},
25
42
  # L.A.Cosmic parameters for run 2
26
- "run2_gain": {"value": 1.0, "type": float, "positive": True},
27
- "run2_readnoise": {"value": 6.5, "type": float, "positive": True},
28
43
  "run2_sigclip": {"value": 3.0, "type": float, "positive": True},
29
44
  "run2_sigfrac": {"value": 0.3, "type": float, "positive": True},
30
45
  "run2_objlim": {"value": 5.0, "type": float, "positive": True},
31
- "run2_niter": {"value": 4, "type": int, "positive": True},
46
+ "run2_gain": {"value": 1.0, "type": float, "positive": True},
47
+ "run2_readnoise": {"value": 6.5, "type": float, "positive": True},
48
+ "run2_satlevel": {"value": 65535, "type": float, "positive": True},
49
+ "run2_niter": {"value": 4, "type": int, "positive": True, "intmode": "any"},
50
+ "run2_sepmed": {"value": True, "type": bool},
51
+ "run2_cleantype": {"value": "meanmask", "type": str, "valid_values": VALID_LACOSMIC_CLEANTYPE_VALUES},
52
+ "run2_fsmode": {"value": "median", "type": str, "valid_values": VALID_LACOSMIC_FSMODE_VALUES},
53
+ "run2_psfmodel": {
54
+ "value": "gaussxy",
55
+ "type": str,
56
+ "valid_values": VALID_LACOSMIC_PSFMODEL_VALUES,
57
+ },
58
+ "run2_psffwhm_x": {"value": 2.5, "type": float, "positive": True},
59
+ "run2_psffwhm_y": {"value": 2.5, "type": float, "positive": True},
60
+ "run2_psfsize": {"value": 7, "type": int, "positive": True, "intmode": "odd"},
61
+ "run2_psfbeta": {"value": 4.765, "type": float, "positive": True},
32
62
  "run2_verbose": {"value": True, "type": bool},
33
63
  # Dilation of the mask
34
64
  "dilation": {"value": 0, "type": int, "positive": True},
@@ -39,11 +69,20 @@ lacosmic_default_dict = {
39
69
  "ymin": {"value": 1, "type": int, "positive": True},
40
70
  "ymax": {"value": None, "type": int, "positive": True},
41
71
  # Number of runs to execute L.A.Cosmic
42
- "nruns": {"value": 1, "type": int, "positive": True},
72
+ "nruns": {"value": 1, "type": int, "positive": True, "intmode": "any"},
43
73
  }
44
74
 
45
- # Default parameters for cleaning methods
46
- VALID_CLEANING_METHODS = ["x interp.", "y interp.", "surface interp.", "median", "mean", "lacosmic", "maskfill", "auxdata"]
75
+ # Valid cleaning methods (as shown to the user and their internal codes)
76
+ VALID_CLEANING_METHODS = {
77
+ "x interp.": "x",
78
+ "y interp.": "y",
79
+ "surface interp.": "a-plane",
80
+ "median": "a-median",
81
+ "mean": "a-mean",
82
+ "lacosmic": "lacosmic",
83
+ "maskfill": "maskfill",
84
+ "auxdata": "auxdata",
85
+ }
47
86
 
48
87
  # Maximum pixel distance to consider when finding closest CR pixel
49
88
  MAX_PIXEL_DISTANCE_TO_CR = 15
@@ -54,6 +93,13 @@ DEFAULT_NPOINTS_INTERP = 2
54
93
  # Default degree for interpolation
55
94
  DEFAULT_DEGREE_INTERP = 1
56
95
 
96
+ # Default maskfill parameters
97
+ DEFAULT_MASKFILL_SIZE = 3
98
+ DEFAULT_MASKFILL_OPERATOR = "median"
99
+ MASKFILL_OPERATOR_VALUES = ["median", "mean"]
100
+ DEFAULT_MASKFILL_SMOOTH = True
101
+ DEFAULT_MASKFILL_VERBOSE = False
102
+
57
103
  # Default Tk window size
58
104
  DEFAULT_TK_WINDOW_SIZE_X = 800
59
105
  DEFAULT_TK_WINDOW_SIZE_Y = 700
@@ -0,0 +1,54 @@
1
+ #
2
+ # Copyright 2025 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ """Calculate elliptical 2D Gaussian kernel."""
11
+
12
+ import math
13
+ import numpy as np
14
+
15
+
16
+ def gausskernel2d_elliptical(fwhm_x, fwhm_y, kernsize):
17
+ """Calculate elliptical 2D Gaussian kernel.
18
+
19
+ This kernel can be used as the psfk parameter of
20
+ ccdproc.cosmicray_lacosmic.
21
+
22
+ Parameters
23
+ ----------
24
+ fwhm_x : float
25
+ Full width at half maximum in the x direction.
26
+ fwhm_y : float
27
+ Full width at half maximum in the y direction.
28
+ kernsize : int
29
+ Size of the kernel (must be odd).
30
+
31
+ Returns
32
+ -------
33
+ kernel : 2D numpy array
34
+ The elliptical Gaussian kernel. It is normalized
35
+ so that the sum of all its elements is 1. It
36
+ is returned as a float32 array (required by
37
+ ccdproc.cosmicray_lacosmic).
38
+ """
39
+
40
+ if kernsize % 2 == 0 or kernsize < 3:
41
+ raise ValueError("kernsize must be an odd integer >= 3.")
42
+ if fwhm_x <= 0 or fwhm_y <= 0:
43
+ raise ValueError("fwhm_x and fwhm_y must be positive numbers.")
44
+
45
+ sigma_x = fwhm_x / (2 * math.sqrt(2 * math.log(2)))
46
+ sigma_y = fwhm_y / (2 * math.sqrt(2 * math.log(2)))
47
+ halfsize = kernsize // 2
48
+ y, x = np.mgrid[-halfsize : halfsize + 1, -halfsize : halfsize + 1]
49
+ kernel = np.exp(-0.5 * ((x / sigma_x) ** 2 + (y / sigma_y) ** 2))
50
+ kernel /= np.sum(kernel)
51
+
52
+ # reverse the psf kernel as that is what it is used in the convolution
53
+ kernel = kernel[::-1, ::-1]
54
+ return kernel.astype(np.float32)
@@ -11,6 +11,7 @@
11
11
 
12
12
  from tkinter import simpledialog
13
13
  import numpy as np
14
+ from rich import print
14
15
 
15
16
  from ..sliceregion import SliceRegion2D
16
17
  from ..zscale import zscale
@@ -9,8 +9,17 @@
9
9
 
10
10
  """Interpolate pixels identified in a mask."""
11
11
 
12
- from scipy import ndimage
12
+ try:
13
+ from maskfill import maskfill
14
+ except ModuleNotFoundError as e:
15
+ raise ModuleNotFoundError(
16
+ "The 'teareduce.cleanest' module requires the 'ccdproc' and 'maskfill' packages. "
17
+ "Please install teareduce with the 'cleanest' extra dependencies: "
18
+ "`pip install teareduce[cleanest]`."
19
+ ) from e
13
20
  import numpy as np
21
+ from rich import print
22
+ from scipy import ndimage
14
23
  from tqdm import tqdm
15
24
 
16
25
  from .dilatemask import dilatemask
@@ -25,6 +34,19 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
25
34
  The original data and mask are not modified. A copy of both
26
35
  arrays are created and returned with the interpolated pixels.
27
36
 
37
+ Cosmic-ray pixels are initially dilated by the specified number
38
+ of pixels. The resulting flagged pixels are grouped into cosmic-ray
39
+ features. Each cosmic-ray feature is then interpolated using the
40
+ specified interpolation method.
41
+
42
+ Note that the interpolation methods `lacosmic` and `auxfile`,
43
+ available in the interactive use of **tea-cleanest** are not
44
+ implemented in this function because both cases are simply
45
+ an inmediate replacement of the cosmic ray pixels in the data
46
+ array by the corresponding pixels in another array using the
47
+ mask array. Therefore, these two methods do not require any
48
+ interpolation algorithm.
49
+
28
50
  Parameters
29
51
  ----------
30
52
  data : 2D numpy.ndarray
@@ -37,11 +59,12 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
37
59
  interpolation.
38
60
  interp_method : str, optional
39
61
  The interpolation method to use. Options are:
40
- 'x' : Polynomial interpolation in the X direction.
41
- 'y' : Polynomial interpolation in the Y direction.
42
- 's' : Surface fit (degree 1) interpolation.
43
- 'd' : Median of border pixels interpolation.
44
- 'm' : Mean of border pixels interpolation.
62
+ 'x' : Polynomial interpolation in the X direction using neighbor pixels
63
+ 'y' : Polynomial interpolation in the Y direction using neighbor pixels
64
+ 's' : Surface fit (degree 1) interpolation using neighbor pixels
65
+ 'd' : Median of neighbor pixels interpolation using neighbor pixels
66
+ 'm' : Mean of neighbor pixels interpolation using neighbor pixels
67
+ 'k' : Maskfill method, as described in van Dokkum & Pasha (2024)
45
68
  npoints : int, optional
46
69
  The number of points to use for interpolation. This
47
70
  parameter is relevant for 'x', 'y', 's', 'd', and 'm' methods.
@@ -67,12 +90,14 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
67
90
  """
68
91
  if interp_method is None:
69
92
  raise ValueError("interp_method must be specified.")
70
- if interp_method not in ["x", "y", "s", "d", "m"]:
93
+ if interp_method not in ["x", "y", "s", "d", "m", "k"]:
71
94
  raise ValueError(f"Unknown interp_method: {interp_method}")
72
- if npoints is None:
73
- raise ValueError("npoints must be specified.")
74
- if degree is None and interp_method in ["x", "y"]:
95
+ if interp_method in ["x", "y", "s", "d", "m"] and npoints is None:
96
+ raise ValueError("npoints must be specified for the chosen interp_method.")
97
+ if interp_method in ["x", "y"] and degree is None:
75
98
  raise ValueError("degree must be specified for the chosen interp_method.")
99
+ if data.shape != mask_crfound.shape:
100
+ raise ValueError("data and mask_crfound must have the same shape.")
76
101
 
77
102
  # Apply dilation to the cosmic ray mask if needed
78
103
  if dilation > 0:
@@ -93,44 +118,50 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
93
118
 
94
119
  # Fix cosmic rays using the specified interpolation method
95
120
  cleaned_data = data.copy()
96
- num_cr_cleaned = 0
97
- for cr_index in tqdm(range(1, num_features + 1), disable=not debug):
98
- if interp_method in ["x", "y"]:
99
- if 2 * npoints <= degree:
100
- raise ValueError("2*npoints must be greater than degree for polynomial interpolation.")
101
- if interp_method == "x":
102
- interp_func = interpolation_x
103
- else:
104
- interp_func = interpolation_y
105
- interpolation_performed, _, _ = interp_func(
106
- data=cleaned_data,
107
- mask_fixed=mask_fixed,
108
- cr_labels=cr_labels,
109
- cr_index=cr_index,
110
- npoints=npoints,
111
- degree=degree,
112
- )
113
- if interpolation_performed:
114
- num_cr_cleaned += 1
115
- elif interp_method in ["s", "d", "m"]:
116
- if interp_method == "s":
117
- method = "surface"
118
- elif interp_method == "d":
119
- method = "median"
120
- elif interp_method == "m":
121
- method = "mean"
122
- interpolation_performed, _, _ = interpolation_a(
123
- data=cleaned_data,
124
- mask_fixed=mask_fixed,
125
- cr_labels=cr_labels,
126
- cr_index=cr_index,
127
- npoints=npoints,
128
- method=method,
129
- )
130
- if interpolation_performed:
131
- num_cr_cleaned += 1
132
- else:
133
- raise ValueError(f"Unknown interpolation method: {interp_method}")
121
+ if interp_method == "k":
122
+ smoothed_output, _ = maskfill(input_image=data, mask=mask_crfound, size=3, operator="median", smooth=True)
123
+ cleaned_data[mask_crfound] = smoothed_output[mask_crfound]
124
+ mask_fixed[mask_crfound] = True
125
+ num_cr_cleaned = num_features
126
+ elif interp_method in ["x", "y", "s", "d", "m"]:
127
+ num_cr_cleaned = 0
128
+ for cr_index in tqdm(range(1, num_features + 1), disable=not debug):
129
+ if interp_method in ["x", "y"]:
130
+ if 2 * npoints <= degree:
131
+ raise ValueError("2*npoints must be greater than degree for polynomial interpolation.")
132
+ if interp_method == "x":
133
+ interp_func = interpolation_x
134
+ else:
135
+ interp_func = interpolation_y
136
+ interpolation_performed, _, _ = interp_func(
137
+ data=cleaned_data,
138
+ mask_fixed=mask_fixed,
139
+ cr_labels=cr_labels,
140
+ cr_index=cr_index,
141
+ npoints=npoints,
142
+ degree=degree,
143
+ )
144
+ if interpolation_performed:
145
+ num_cr_cleaned += 1
146
+ elif interp_method in ["s", "d", "m"]:
147
+ if interp_method == "s":
148
+ method = "surface"
149
+ elif interp_method == "d":
150
+ method = "median"
151
+ elif interp_method == "m":
152
+ method = "mean"
153
+ interpolation_performed, _, _ = interpolation_a(
154
+ data=cleaned_data,
155
+ mask_fixed=mask_fixed,
156
+ cr_labels=cr_labels,
157
+ cr_index=cr_index,
158
+ npoints=npoints,
159
+ method=method,
160
+ )
161
+ if interpolation_performed:
162
+ num_cr_cleaned += 1
163
+ else:
164
+ raise ValueError(f"Unknown interpolation method: {interp_method}")
134
165
 
135
166
  if debug:
136
167
  print(f"Number of cosmic rays cleaned............: {num_cr_cleaned:>{len(sdum)}}")
@@ -10,6 +10,7 @@
10
10
  """Surface interpolation (plane fit) or median interpolation"""
11
11
 
12
12
  import numpy as np
13
+ from rich import print
13
14
 
14
15
  from .dilatemask import dilatemask
15
16
 
@@ -10,6 +10,7 @@
10
10
  """Polynomial nterpolation in the X direction"""
11
11
 
12
12
  import numpy as np
13
+ from rich import print
13
14
 
14
15
 
15
16
  def interpolation_x(data, mask_fixed, cr_labels, cr_index, npoints, degree):
@@ -10,6 +10,7 @@
10
10
  """Polynomial nterpolation in the Y direction"""
11
11
 
12
12
  import numpy as np
13
+ from rich import print
13
14
 
14
15
 
15
16
  def interpolation_y(data, mask_fixed, cr_labels, cr_index, npoints, degree):