spanks 0.1.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.

Potentially problematic release.


This version of spanks might be problematic. Click here for more details.

spanks/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from .noise_suppressor1D import suppress_noise1d
2
+ from .noise_suppressor2D import suppress_noise2d
@@ -0,0 +1,123 @@
1
+ import numpy as np
2
+ from scipy.stats import norm
3
+ from scipy.signal import convolve
4
+ from astropy.convolution import Gaussian1DKernel
5
+ from .utils import sigma_clipping
6
+
7
+ def suppress_noise1d(profile, method ="simple", kernel = None, logit_alpha = 1.0, abs_profile = False):
8
+ """
9
+ Suppress background-dominated samples in a 1D profile using a noise-based
10
+ probability model.
11
+
12
+ The function assumes that the profile noise follows an approximately
13
+ normal distribution. A noise probability profile is first computed from
14
+ the profile intensity distribution and then refined using one of several
15
+ methods. The resulting probability profile is applied as a weighting
16
+ function, enhancing statistically significant features while attenuating
17
+ samples consistent with the background noise.
18
+
19
+ Parameters
20
+ ----------
21
+ profile : ndarray
22
+ Input one-dimensional profile.
23
+
24
+ method : {'simple', 'logit', 'layers'}, optional
25
+ Method used to generate the probability profile.
26
+
27
+ * ``'simple'``: local smoothing of the noise probability profile.
28
+ * ``'logit'``: smoothing in logit space followed by a logistic
29
+ transformation.
30
+ * ``'layers'``: iterative probability refinement using an
31
+ additional intensity-weighted layer.
32
+
33
+ Default is ``'simple'``.
34
+
35
+ kernel : ndarray, optional
36
+ Kernel used for local probability aggregation. If ``None``, a
37
+ Gaussian kernel with a width equivalent to
38
+ ``Gaussian1DKernel(2.0)`` is used.
39
+
40
+ logit_alpha : float, optional
41
+ Scaling factor applied to the logistic transform when
42
+ ``method='logit'``. Larger values increase the contrast between
43
+ low- and high-probability regions.
44
+
45
+ abs_profile : bool, optional
46
+ If True will use absolute values to make to probability profile,
47
+ else it will use squared values. Default is False.
48
+
49
+ Returns
50
+ -------
51
+ ndarray
52
+ Background-suppressed profile rescaled to approximately match the
53
+ original profile intensity range.
54
+
55
+ See Also
56
+ --------
57
+ _get_p_simple_1d
58
+ _get_p_logit_1d
59
+ _get_p_layers_1d
60
+ _apply_p_profile
61
+ """
62
+
63
+ if kernel is None:
64
+ kernel = Gaussian1DKernel(2.0) # Larger kernels yield higher noise level, but more symmetric noise distribution
65
+
66
+ profile_noise_lvl, profile_noise_std = sigma_clipping(profile, robust=False)
67
+
68
+ profile_centered = profile - profile_noise_lvl
69
+
70
+ # Probability of obtaining x or lower:
71
+ if abs_profile:
72
+ noise_p_profile = norm.cdf(np.abs(profile_centered), loc=0, scale=profile_noise_std)
73
+ else:
74
+ noise_p_profile = norm.cdf(profile_centered**2, loc=0, scale=profile_noise_std)
75
+
76
+ if method == "logit":
77
+ p_prof = _get_p_logit_1d(noise_p_profile, kernel, alpha = logit_alpha)
78
+ elif method == "layers":
79
+ p_prof = _get_p_layers_1d(profile, noise_p_profile, kernel)
80
+ else: #simple
81
+ p_prof = _get_p_simple_1d(noise_p_profile, kernel)
82
+
83
+ bkg_reduced_image_leveled = _apply_p_profile(profile_centered, p_prof) + profile_noise_lvl
84
+
85
+ return bkg_reduced_image_leveled
86
+
87
+ def _get_p_simple_1d(p_prof, kernel):
88
+ p_out = convolve(p_prof, kernel, mode='same')
89
+ return p_out
90
+
91
+ def _get_p_logit_1d(p_prof, kernel, alpha = 1.0):
92
+ p_prof[p_prof >= 1] = 1-1e-9
93
+ logit = np.log(p_prof / (1 - p_prof))
94
+ L = convolve(logit, kernel, mode='same') # weighted sum of logit
95
+ p_out = 1.0 / (1.0 + np.exp(-alpha * L))
96
+ return p_out
97
+
98
+ def _get_p_layers_1d(data, p_prof, kernel, abs_profile = False):
99
+ p_prof_cnv = convolve(p_prof, kernel, mode='same')
100
+ data_p_prof_cnv = data * p_prof_cnv
101
+ data_p_prof_cnv_noise_lvl, data_p_prof_cnv_noise_lvl_std = sigma_clipping(data_p_prof_cnv, robust=False)
102
+ data_p_prof_cnv_center = data_p_prof_cnv - data_p_prof_cnv_noise_lvl
103
+ if abs_profile:
104
+ p_prof_new_layer = norm.cdf(np.abs(data_p_prof_cnv_center), loc=0, scale=data_p_prof_cnv_noise_lvl_std)
105
+ else:
106
+ p_prof_new_layer = norm.cdf(data_p_prof_cnv_center**2, loc=0, scale=data_p_prof_cnv_noise_lvl_std)
107
+ p_prof_new_layer_cnv = convolve(p_prof_new_layer, kernel, mode='same')
108
+ p_out = p_prof_cnv * p_prof_new_layer_cnv
109
+ return p_out
110
+
111
+ def _apply_p_profile(data, p_prof):
112
+ """
113
+ Apply a probability map (p_prof) to an image and rescale according to their maxima.
114
+ """
115
+
116
+ data_p_prof = data * p_prof
117
+ data_p_prof_leveled = data_p_prof * data.max() / data_p_prof.max()
118
+ return data_p_prof_leveled
119
+
120
+ if __name__ == "__main__":
121
+ quit()
122
+
123
+
@@ -0,0 +1,147 @@
1
+ import numpy as np
2
+ from scipy.stats import norm
3
+ from scipy.signal import convolve2d
4
+ from astropy.convolution import Gaussian2DKernel
5
+ from .utils import sigma_clipping
6
+
7
+ def suppress_noise2d(image, method ="simple", kernel = None, logit_alpha = 1.0, symmetric = False, abs_profile = False):
8
+ """
9
+ Suppress background-dominated pixels in an image using a noise-based
10
+ probability model.
11
+
12
+ The function assumes that image noise follows an approximately normal
13
+ distribution. A noise probability map is first computed from the image
14
+ intensity distribution and then refined using one of several methods.
15
+ The resulting probability map is applied as a weighting function,
16
+ enhancing statistically significant structures while attenuating pixels
17
+ consistent with the background noise.
18
+
19
+ Parameters
20
+ ----------
21
+ image : ndarray
22
+ Input image.
23
+
24
+ method : {'simple', 'logit', 'layers'}, optional
25
+ Method used to generate the probability map.
26
+
27
+ * ``'simple'``: spatial smoothing of the noise probability map.
28
+ * ``'logit'``: smoothing in logit space followed by a logistic
29
+ transformation.
30
+ * ``'layers'``: iterative probability refinement using an
31
+ additional intensity-weighted layer.
32
+
33
+ Default is ``'simple'``.
34
+
35
+ kernel : ndarray, optional
36
+ Convolution kernel used for local probability aggregation. If
37
+ ``None``, a ``Gaussian2DKernel(2.0)`` is used.
38
+
39
+ logit_alpha : float, optional
40
+ Scaling factor applied to the logistic transform when
41
+ ``method='logit'``. Larger values increase the contrast between
42
+ low- and high-probability regions.
43
+
44
+ Returns
45
+ -------
46
+ ndarray
47
+ Background-suppressed image rescaled to approximately match the
48
+ original image intensity range.
49
+
50
+ See Also
51
+ --------
52
+ _get_p_simple2d
53
+ _get_p_logit2d
54
+ _get_p_layers2d
55
+ _apply_p_map
56
+ """
57
+
58
+ if kernel is None:
59
+ kernel = Gaussian2DKernel(2.0) # Larger kernels yield higher noise level, but more symmetric noise distribution
60
+
61
+ image_noise_lvl, image_noise_std = sigma_clipping(image, robust=False)
62
+
63
+ # Probability of obtaining x or lower:
64
+ if symmetric:
65
+ if abs_profile:
66
+ noise_p_map = norm.cdf(np.abs(image), loc=image_noise_lvl, scale=image_noise_std)
67
+ else:
68
+ noise_p_map = norm.cdf(image**2, loc=image_noise_lvl, scale=image_noise_std)
69
+ else:
70
+ noise_p_map = norm.cdf(image, loc=image_noise_lvl, scale=image_noise_std)
71
+
72
+ if method == "logit":
73
+ p_map = _get_p_logit2d(noise_p_map, kernel, alpha = logit_alpha)
74
+ elif method == "layers":
75
+ p_map = _get_p_layers2d(image, noise_p_map, kernel, symmetric, abs_profile)
76
+ else: #simple
77
+ p_map = _get_p_simple2d(noise_p_map, kernel)
78
+
79
+ bkg_reduced_image_leveled = _apply_p_map(image, p_map)
80
+
81
+ return bkg_reduced_image_leveled
82
+
83
+ def _get_p_simple2d(p_map, kernel):
84
+ """
85
+ Convolve probability map (p_map) with the input kernel.
86
+ """
87
+
88
+ p_out = convolve2d(p_map, kernel, mode='same', boundary='symm')
89
+ return p_out
90
+
91
+ def _get_p_logit2d(p_map, kernel, alpha = 1.0):
92
+ """
93
+ Transform probability map (p_map) into logits, convolve with the supplied
94
+ kernel, and transform back into a probability map.
95
+
96
+ alpha is the scaling factor applied to the aggregated logits before converting
97
+ back to probabilities. Values greater than one amplify local contrast,
98
+ while values less than one reduce it.
99
+
100
+ Notes
101
+ -----
102
+ Pixels with probability equal to one are clipped slightly below one
103
+ before the logit transformation to avoid numerical singularities.
104
+ """
105
+
106
+ p_map[p_map >= 1] = 1-1e-9
107
+ logit = np.log(p_map / (1 - p_map))
108
+ L = convolve2d(logit, kernel, mode='same', boundary='symm') # weighted sum of logit
109
+ p_out = 1.0 / (1.0 + np.exp(-alpha * L))
110
+ return p_out
111
+
112
+ def _get_p_layers2d(image, p_map, kernel, symmetric = False, abs_profile = False):
113
+ """
114
+ Generate a probability map using a two-layer probability refinement
115
+ scheme.
116
+
117
+ An initial probability map (p_map) is first convolved with the provided kernel.
118
+ The image is then weighted by this smoothed map, and a second probability map is
119
+ estimated from the resulting intensity distribution. The final output
120
+ is obtained by combining the two probability layers.
121
+ """
122
+
123
+ p_map_cnv = convolve2d(p_map, kernel, mode='same', boundary='symm')
124
+ image_p_map_cnv = image * p_map_cnv
125
+ image_p_map_cnv_noise_lvl, image_p_map_cnv_noise_std = sigma_clipping(image_p_map_cnv, robust=False)
126
+ if symmetric:
127
+ if abs_profile:
128
+ p_map_new_layer = norm.cdf(np.abs(image_p_map_cnv), loc=image_p_map_cnv_noise_lvl, scale=image_p_map_cnv_noise_std)
129
+ else:
130
+ p_map_new_layer = norm.cdf(image_p_map_cnv**2, loc=image_p_map_cnv_noise_lvl, scale=image_p_map_cnv_noise_std)
131
+ else:
132
+ p_map_new_layer = norm.cdf(image_p_map_cnv, loc=image_p_map_cnv_noise_lvl, scale=image_p_map_cnv_noise_std)
133
+ p_map_new_layer_cnv = convolve2d(p_map_new_layer, kernel, mode='same', boundary='symm')
134
+ p_out = p_map_cnv * p_map_new_layer_cnv
135
+ return p_out
136
+
137
+ def _apply_p_map(image, p_map):
138
+ """
139
+ Apply a probability map (p_map) to an image and rescale according to their maxima.
140
+ """
141
+
142
+ image_p_map = image * p_map
143
+ image_p_map_leveled = image_p_map * image.max() / image_p_map.max()
144
+ return image_p_map_leveled
145
+
146
+ if __name__ == "__main__":
147
+ quit()
@@ -0,0 +1,310 @@
1
+ import numpy as np
2
+ from astropy.modeling.models import Sersic2D
3
+
4
+ def _get_psf_sources(number_of_psf_sources, image_size, psf_fwhm_range, psf_dynamic_range, symmetric):
5
+ """
6
+ Generate an image containing randomly positioned PSF-like sources.
7
+
8
+ Each source is modeled as a circular two-dimensional Gaussian profile
9
+ with a randomly selected position, width, and peak intensity. The
10
+ resulting source images are summed to produce a single image.
11
+
12
+ Parameters
13
+ ----------
14
+ number_of_psf_sources : int
15
+ Number of PSF sources to generate.
16
+
17
+ image_size : tuple of int
18
+ Output image size as ``(nx, ny)``.
19
+
20
+ psf_fwhm_range : tuple of float
21
+ Minimum and maximum full width at half maximum (FWHM) values for
22
+ the generated Gaussian sources.
23
+
24
+ psf_dynamic_range : tuple of float
25
+ Minimum and maximum absolute source amplitudes.
26
+
27
+ symmetric : bool
28
+ If ``True``, sources may have either positive or negative peak
29
+ amplitudes. If ``False``, all amplitudes are positive.
30
+
31
+ Returns
32
+ -------
33
+ ndarray
34
+ Image containing the sum of all generated PSF sources.
35
+
36
+ Notes
37
+ -----
38
+ The Gaussian standard deviation is computed from the FWHM using
39
+
40
+ ``sigma = FWHM / 2.3548``.
41
+ """
42
+
43
+ all_psf_image = np.zeros(image_size, dtype = float)
44
+ y, x = np.mgrid[:image_size[1], :image_size[0]]
45
+ x_positions = np.random.random(number_of_psf_sources) * image_size[0]
46
+ y_positions = np.random.random(number_of_psf_sources) * image_size[1]
47
+ sources_fwhms = np.random.random(number_of_psf_sources) * (psf_fwhm_range[1] - psf_fwhm_range[0]) + psf_fwhm_range[0]
48
+ sources_stds = sources_fwhms/2.3548 # std = fwhm/2.3548
49
+
50
+ if symmetric:
51
+ peaks = (2 * np.random.random(number_of_psf_sources) - 1) * (psf_dynamic_range[1] - psf_dynamic_range[0]) #+ psf_dynamic_range[0]
52
+ peaks = peaks + psf_dynamic_range[0] * peaks / np.abs(peaks)
53
+ else:
54
+ peaks = (np.random.random(number_of_psf_sources)) * (psf_dynamic_range[1] - psf_dynamic_range[0]) + psf_dynamic_range[0]
55
+ sources = zip(x_positions, y_positions, sources_stds, peaks)
56
+ for this_source in sources:
57
+ sigma = this_source[2]
58
+ source_g = np.exp(-((x-this_source[0])**2 + (y-this_source[1])**2) / (2*sigma**2))
59
+ source_g = this_source[3] * source_g/source_g.max()
60
+ all_psf_image += source_g
61
+ return all_psf_image
62
+
63
+ def _get_sersic_sources(number_of_sersic_sources, image_size, r_eff_range, n_range, ellip_range, theta_range,
64
+ dynamic_range, symmetric = False):
65
+ """
66
+ Generate an image containing randomly positioned Sérsic profile sources.
67
+
68
+ Each source is modeled using a two-dimensional Sérsic profile with
69
+ randomly selected structural parameters and position. The generated
70
+ source images are summed to produce a composite image.
71
+
72
+ Parameters
73
+ ----------
74
+ number_of_sersic_sources : int
75
+ Number of Sérsic sources to generate.
76
+
77
+ image_size : tuple of int
78
+ Output image size as ``(nx, ny)``.
79
+
80
+ r_eff_range : tuple of float
81
+ Minimum and maximum effective radius values.
82
+
83
+ n_range : tuple of float
84
+ Minimum and maximum Sérsic indices.
85
+
86
+ ellip_range : tuple of float
87
+ Minimum and maximum ellipticities.
88
+
89
+ theta_range : tuple of float
90
+ Minimum and maximum position angles in radians.
91
+
92
+ dynamic_range : tuple of float
93
+ Minimum and maximum source amplitudes.
94
+
95
+ symmetric : bool, optional
96
+ If ``True``, each source is randomly assigned either a positive
97
+ or negative sign. Default is ``False``.
98
+
99
+ Returns
100
+ -------
101
+ ndarray
102
+ Image containing the sum of all generated Sérsic sources.
103
+
104
+ See Also
105
+ --------
106
+ astropy.modeling.functional_models.Sersic2D
107
+ """
108
+
109
+
110
+ all_sersic_image = np.zeros(image_size, dtype = float)
111
+ y, x = np.mgrid[:image_size[1], :image_size[0]]
112
+ x_positions = np.random.random(number_of_sersic_sources) * image_size[0]
113
+ y_positions = np.random.random(number_of_sersic_sources) * image_size[1]
114
+ r_eff = np.random.random(number_of_sersic_sources) * (r_eff_range[1] - r_eff_range[0]) + r_eff_range[0]
115
+ n = np.random.random(number_of_sersic_sources) * (n_range[1] - n_range[0]) + n_range[0]
116
+ ellip = np.random.random(number_of_sersic_sources) * (ellip_range[1] - ellip_range[0]) + ellip_range[0]
117
+ theta = np.random.random(number_of_sersic_sources) * (theta_range[1] - theta_range[0]) + theta_range[0]
118
+ peaks = (np.random.random(number_of_sersic_sources)) * (dynamic_range[1] - dynamic_range[0]) + dynamic_range[0]
119
+ sources = zip(x_positions, y_positions, r_eff, n, ellip, theta, peaks)
120
+ for this_source in sources:
121
+ sersic_model = Sersic2D(amplitude=this_source[6], r_eff=this_source[2], n=this_source[3], x_0=this_source[0], y_0=this_source[1], ellip=this_source[4], theta=this_source[5])
122
+ galaxy = sersic_model(x, y)
123
+ galaxy = this_source[6] * galaxy/galaxy.max()
124
+ if symmetric:
125
+ rd = np.random.rand() - 0.5
126
+ galaxy = galaxy * (rd/np.abs(rd))
127
+ all_sersic_image += galaxy
128
+ return all_sersic_image
129
+
130
+ def get_synthetic_sky_image(image_size = None, noise_std = 0.125, number_of_psf_sources = 25, psf_fwhm_range = None,
131
+ psf_dynamic_range = None, number_of_sersic_sources = 8, r_eff_range = None, n_range = None,
132
+ ellip_range = None, theta_range = None, sersic_dynamic_range = None,
133
+ gradient_level = 0.5, gradient_slope = 1.5, symmetric = False):
134
+ """
135
+ Generate a synthetic astronomical image containing noise,
136
+ background structure, point sources, and extended galaxies.
137
+
138
+ The generated image consists of four components:
139
+
140
+ * Gaussian-distributed background noise.
141
+ * A large-scale background gradient.
142
+ * PSF-like point sources represented by Gaussian profiles.
143
+ * Extended sources represented by Sérsic profiles.
144
+
145
+ This function is intended for testing and benchmarking image
146
+ processing, source detection, and background estimation algorithms.
147
+
148
+ Parameters
149
+ ----------
150
+ image_size : tuple of int, optional
151
+ Output image size as ``(nx, ny)``.
152
+ Default is ``(1000, 1000)``.
153
+
154
+ noise_std : float, optional
155
+ Standard deviation of the Gaussian background noise.
156
+ Default is ``0.125``.
157
+
158
+ number_of_psf_sources : int, optional
159
+ Number of PSF sources to generate.
160
+ Default is ``25``.
161
+
162
+ psf_fwhm_range : tuple of float, optional
163
+ Range of PSF FWHM values. Default is ``(1000, 1000)``.
164
+
165
+ psf_dynamic_range : tuple of float, optional
166
+ Range of PSF amplitudes. Default is ``(1, 4)``.
167
+
168
+ number_of_sersic_sources : int, optional
169
+ Number of Sérsic sources to generate.
170
+ Default is ``8``.
171
+
172
+ r_eff_range : tuple of float, optional
173
+ Range of effective radii for Sérsic profiles.
174
+ Default is ``(100, 170)``.
175
+
176
+ n_range : tuple of float, optional
177
+ Range of Sérsic indices.
178
+ Default is ``(1, 4)``.
179
+
180
+ ellip_range : tuple of float, optional
181
+ Range of Sérsic ellipticities.
182
+ Default is ``(0, 0.8)``.
183
+
184
+ theta_range : tuple of float, optional
185
+ Range of Sérsic position angles in radians.
186
+ Default is ``(0, pi)``.
187
+
188
+ sersic_dynamic_range : tuple of float, optional
189
+ Range of Sérsic source amplitudes.
190
+ Default is ``(1, 4)``.
191
+
192
+ gradient_level : float, optional
193
+ Constant background level of the smooth gradient component.
194
+ Default is ``0.5``.
195
+
196
+ gradient_slope : float, optional
197
+ Controls the strength of the background gradient.
198
+ Default is ``1.5``.
199
+
200
+ symmetric : bool, optional
201
+ If ``True``, generated sources may have positive or negative
202
+ amplitudes. Default is ``False``.
203
+
204
+ Returns
205
+ -------
206
+ ndarray
207
+ Synthetic image containing background noise, gradient,
208
+ PSF sources, and Sérsic sources.
209
+
210
+ See Also
211
+ --------
212
+ _get_psf_sources
213
+ _get_sersic_sources
214
+ _get_simple_gradient
215
+ """
216
+
217
+
218
+ if image_size is None:
219
+ image_size = (1000, 1000)
220
+ if psf_fwhm_range is None:
221
+ psf_fwhm_range = (1000, 1000)
222
+ if psf_dynamic_range is None:
223
+ psf_dynamic_range = (1, 4)
224
+ if r_eff_range is None:
225
+ r_eff_range = (100, 170)
226
+ if n_range is None:
227
+ n_range = (1, 4)
228
+ if ellip_range is None:
229
+ ellip_range = (0, 0.8)
230
+ if theta_range is None:
231
+ theta_range = (0, np.pi)
232
+ if sersic_dynamic_range is None:
233
+ sersic_dynamic_range = (1, 4)
234
+
235
+ psfs_image = _get_psf_sources(number_of_psf_sources, image_size, psf_fwhm_range, psf_dynamic_range, symmetric)
236
+ sersic_image = _get_sersic_sources(number_of_sersic_sources, image_size, r_eff_range, n_range, ellip_range, theta_range, sersic_dynamic_range, symmetric)
237
+ full_map = np.zeros(image_size, dtype = float)
238
+ bkg_gradient = _get_simple_gradient(image_size, gradient_level, gradient_slope)
239
+ noise = np.random.normal(loc=0.0, scale=noise_std, size=(image_size[1], image_size[0]))
240
+ full_map += noise + bkg_gradient + psfs_image + sersic_image
241
+
242
+ return full_map
243
+
244
+ def _get_simple_gradient(image_size, level = 0.5, slope = 1.5):
245
+ """
246
+ Generate a simple two-dimensional background gradient.
247
+
248
+ The gradient increases linearly along both image axes and is
249
+ normalized such that its minimum value is approximately equal to
250
+ ``level``.
251
+
252
+ Parameters
253
+ ----------
254
+ image_size : tuple of int
255
+ Output image size as ``(nx, ny)``.
256
+
257
+ level : float, optional
258
+ Baseline offset added to the gradient.
259
+ Default is ``0.5``.
260
+
261
+ slope : float, optional
262
+ Gradient strength. A value of ``0`` produces a flat image.
263
+ Default is ``1.5``.
264
+
265
+ Returns
266
+ -------
267
+ ndarray
268
+ Two-dimensional background gradient image.
269
+
270
+ Notes
271
+ -----
272
+ When ``slope == 0``, a zero-valued image with shape
273
+ ``image_size`` is returned.
274
+ """
275
+
276
+ y, x = np.mgrid[:image_size[1], :image_size[0]]
277
+ if slope != 0:
278
+ gradient = slope * (x / image_size[1] + y / image_size[0]) / 2
279
+ gradient = level + gradient/gradient.max()
280
+ else:
281
+ return np.zeros(image_size, dtype = float)
282
+ # gradient = 0.2 + 0.5 * x / image_size[1]
283
+ return gradient
284
+
285
+ if __name__ == "__main__":
286
+ import matplotlib.pyplot as plt
287
+
288
+ image_size = (1000, 1000)
289
+ symmetric = False
290
+ noise_std = 0.125
291
+
292
+ number_of_psf_sources = 25
293
+ psf_fwhm_range = (2, 14)
294
+ psf_dynamic_range = (1, 4)
295
+
296
+ number_of_sersic_sources = 8
297
+ r_eff_range = (100, 170)
298
+ n_range = (1, 4)
299
+ ellip_range = (0, 0.8)
300
+ theta_range = (0, np.pi)
301
+ sersic_dynamic_range = (1, 4)
302
+
303
+ gradient_level = 0.2
304
+ gradient_slope = 2
305
+
306
+ full_map = get_synthetic_sky_image(image_size, noise_std, number_of_psf_sources, psf_fwhm_range, psf_dynamic_range,
307
+ number_of_sersic_sources, r_eff_range, n_range, ellip_range, theta_range,
308
+ sersic_dynamic_range, gradient_level, gradient_slope, symmetric)
309
+ plt.imshow(full_map)
310
+ plt.show()