teareduce 0.5.8__py3-none-any.whl → 0.6.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.
@@ -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)
@@ -9,8 +9,16 @@
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 scipy import ndimage
14
22
  from tqdm import tqdm
15
23
 
16
24
  from .dilatemask import dilatemask
@@ -37,11 +45,12 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
37
45
  interpolation.
38
46
  interp_method : str, optional
39
47
  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.
48
+ 'x' : Polynomial interpolation in the X direction using neighbor pixels
49
+ 'y' : Polynomial interpolation in the Y direction using neighbor pixels
50
+ 's' : Surface fit (degree 1) interpolation using neighbor pixels
51
+ 'd' : Median of neighbor pixels interpolation using neighbor pixels
52
+ 'm' : Mean of neighbor pixels interpolation using neighbor pixels
53
+ 'k' : Maskfill method, as described in van Dokkum & Pasha (2024)
45
54
  npoints : int, optional
46
55
  The number of points to use for interpolation. This
47
56
  parameter is relevant for 'x', 'y', 's', 'd', and 'm' methods.
@@ -67,12 +76,14 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
67
76
  """
68
77
  if interp_method is None:
69
78
  raise ValueError("interp_method must be specified.")
70
- if interp_method not in ["x", "y", "s", "d", "m"]:
79
+ if interp_method not in ["x", "y", "s", "d", "m", "k"]:
71
80
  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"]:
81
+ if interp_method in ["x", "y", "s", "d", "m"] and npoints is None:
82
+ raise ValueError("npoints must be specified for the chosen interp_method.")
83
+ if interp_method in ["x", "y"] and degree is None:
75
84
  raise ValueError("degree must be specified for the chosen interp_method.")
85
+ if data.shape != mask_crfound.shape:
86
+ raise ValueError("data and mask_crfound must have the same shape.")
76
87
 
77
88
  # Apply dilation to the cosmic ray mask if needed
78
89
  if dilation > 0:
@@ -93,44 +104,50 @@ def interpolate(data, mask_crfound, dilation=0, interp_method=None, npoints=None
93
104
 
94
105
  # Fix cosmic rays using the specified interpolation method
95
106
  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}")
107
+ if interp_method == "k":
108
+ smoothed_output, _ = maskfill(input_image=data, mask=mask_crfound, size=3, operator="median", smooth=True)
109
+ cleaned_data[mask_crfound] = smoothed_output[mask_crfound]
110
+ mask_fixed[mask_crfound] = True
111
+ num_cr_cleaned = num_features
112
+ elif interp_method in ["x", "y", "s", "d", "m"]:
113
+ num_cr_cleaned = 0
114
+ for cr_index in tqdm(range(1, num_features + 1), disable=not debug):
115
+ if interp_method in ["x", "y"]:
116
+ if 2 * npoints <= degree:
117
+ raise ValueError("2*npoints must be greater than degree for polynomial interpolation.")
118
+ if interp_method == "x":
119
+ interp_func = interpolation_x
120
+ else:
121
+ interp_func = interpolation_y
122
+ interpolation_performed, _, _ = interp_func(
123
+ data=cleaned_data,
124
+ mask_fixed=mask_fixed,
125
+ cr_labels=cr_labels,
126
+ cr_index=cr_index,
127
+ npoints=npoints,
128
+ degree=degree,
129
+ )
130
+ if interpolation_performed:
131
+ num_cr_cleaned += 1
132
+ elif interp_method in ["s", "d", "m"]:
133
+ if interp_method == "s":
134
+ method = "surface"
135
+ elif interp_method == "d":
136
+ method = "median"
137
+ elif interp_method == "m":
138
+ method = "mean"
139
+ interpolation_performed, _, _ = interpolation_a(
140
+ data=cleaned_data,
141
+ mask_fixed=mask_fixed,
142
+ cr_labels=cr_labels,
143
+ cr_index=cr_index,
144
+ npoints=npoints,
145
+ method=method,
146
+ )
147
+ if interpolation_performed:
148
+ num_cr_cleaned += 1
149
+ else:
150
+ raise ValueError(f"Unknown interpolation method: {interp_method}")
134
151
 
135
152
  if debug:
136
153
  print(f"Number of cosmic rays cleaned............: {num_cr_cleaned:>{len(sdum)}}")