comparison source_extraction.py @ 0:f4648bd69d48 draft default tip

planemo upload for repository https://github.com/esg-epfl-apc/tools-astro/tree/main/tools commit 89e20bd2ec941d1d362da85bd81230419a1a4b2a
author astroteam
date Fri, 18 Jul 2025 14:23:35 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f4648bd69d48
1 #!/usr/bin/env python
2 # coding: utf-8
3
4 #!/usr/bin/env python
5
6 # This script is generated with nb2galaxy
7
8 # flake8: noqa
9
10 import json
11 import os
12 import shutil
13
14 import matplotlib.pyplot as plt
15 import numpy as np
16 import sep
17 import tifffile
18 from astropy.io import fits
19 from astropy.table import Table
20 from matplotlib import rcParams
21 from matplotlib.patches import Ellipse
22 from oda_api.json import CustomJSONEncoder
23
24 get_ipython().run_line_magic("matplotlib", "inline") # noqa: F821
25
26 rcParams["figure.figsize"] = [10.0, 8.0]
27
28 input_file = "./input.fits" # oda:POSIXPath; oda:label "Input file"
29
30 ### These params are for both functions
31 mask_file = None # oda:POSIXPath, oda:optional; oda:label "Mask file"
32
33 ### These params are for sep.extract()
34 thresh = 1.5 # oda:Float
35 err_option = "float_globalrms" # oda:String; oda:allowed_value 'float_globalrms','array_rms', 'none'
36 # gain = None
37 maskthresh = 0.0 # oda:Float
38 minarea = 5 # oda:Integer
39 filter_case = "default" # oda:String; oda:label "Filter Case"; oda:allowed_value 'none', 'default', 'file'
40 filter_file = None # oda:POSIXPath, oda:optional; oda:label "Filter file"
41 filter_type = "matched" # oda:String; oda:allowed_value 'matched','conv'
42 deblend_nthresh = 32 # oda:Integer
43 deblend_cont = 0.005 # oda:Float
44 clean = True # oda:Boolean
45 clean_param = 1.0 # oda:Float
46
47 ### These params are for sep.Background()
48 # maskthresh = 0.0
49 bw = 64 # oda:Integer
50 bh = 64 # oda:Integer
51 fw = 3 # oda:Integer
52 fh = 3 # oda:Integer
53 fthresh = 0.0 # oda:Float
54
55 _galaxy_wd = os.getcwd()
56
57 with open("inputs.json", "r") as fd:
58 inp_dic = json.load(fd)
59 if "C_data_product_" in inp_dic.keys():
60 inp_pdic = inp_dic["C_data_product_"]
61 else:
62 inp_pdic = inp_dic
63 input_file = str(inp_pdic["input_file"])
64
65 mask_file = (
66 str(inp_pdic["mask_file"])
67 if inp_pdic.get("mask_file", None) is not None
68 else None
69 )
70
71 thresh = float(inp_pdic["thresh"])
72 err_option = str(inp_pdic["err_option"])
73 maskthresh = float(inp_pdic["maskthresh"])
74 minarea = int(inp_pdic["minarea"])
75 filter_case = str(inp_pdic["filter_case"])
76
77 filter_file = (
78 str(inp_pdic["filter_file"])
79 if inp_pdic.get("filter_file", None) is not None
80 else None
81 )
82
83 filter_type = str(inp_pdic["filter_type"])
84 deblend_nthresh = int(inp_pdic["deblend_nthresh"])
85 deblend_cont = float(inp_pdic["deblend_cont"])
86 clean = bool(inp_pdic["clean"])
87 clean_param = float(inp_pdic["clean_param"])
88 bw = int(inp_pdic["bw"])
89 bh = int(inp_pdic["bh"])
90 fw = int(inp_pdic["fw"])
91 fh = int(inp_pdic["fh"])
92 fthresh = float(inp_pdic["fthresh"])
93
94 try:
95 hdul = fits.open(input_file)
96 data = hdul[0].data
97 data = data.astype(data.dtype.newbyteorder("=")).astype(float)
98 except:
99 try:
100 data = tifffile.imread(input_file).astype(float)
101 except:
102 raise RuntimeError(
103 "The input file should have the FITS or TIFF format."
104 )
105
106 print("INFO: Data shape:", data.shape)
107
108 if mask_file is not None:
109 try:
110 hdul = fits.open(mask_file)
111 mask = hdul[0].data
112 mask = mask.astype(mask.dtype.newbyteorder("="))
113 except:
114 try:
115 mask = tifffile.imread(mask_file)
116 except:
117 raise RuntimeError(
118 "The mask file should have the FITS or TIFF format."
119 )
120 else:
121 mask = None
122
123 print("INFO: Mask type:", type(mask))
124
125 filter_kernel = None
126 if filter_case == "none":
127 filter_kernel = None
128 elif filter_case == "default":
129 filter_kernel = np.array([[1, 2, 1], [2, 4, 2], [1, 2, 1]])
130 elif filter_case == "file":
131 try:
132 filter_kernel = np.loadtxt(filter_file)
133 except:
134 raise RuntimeError(
135 "The filter file should be a text file that is loaded with numpy.loadtxt"
136 )
137
138 print("INFO: Filter kernel:", filter_kernel)
139
140 # measure a spatially varying background on the image
141 bkg = sep.Background(
142 data,
143 mask=mask,
144 maskthresh=maskthresh,
145 bw=bw,
146 bh=bh,
147 fw=fw,
148 fh=fh,
149 fthresh=fthresh,
150 )
151
152 # evaluate background as 2-d array, same size as original image
153 bkg_array = bkg.back()
154 hdu_bkg = fits.PrimaryHDU(bkg_array)
155 hdu_bkg.writeto("bkg_array.fits")
156
157 # evaluate the background noise as 2-d array, same size as original image
158 bkg_rms = bkg.rms()
159 hdu_rms = fits.PrimaryHDU(bkg_rms)
160 hdu_rms.writeto("bkg_rms.fits")
161
162 # subtract the background
163 data_sub = data - bkg
164
165 if err_option == "float_globalrms":
166 err = bkg.globalrms
167 elif err_option == "array_rms":
168 err = bkg_rms
169 else:
170 err = None
171
172 # extract sources:
173 objects, segmap = sep.extract(
174 data_sub,
175 thresh,
176 err=err,
177 gain=None,
178 mask=mask,
179 maskthresh=maskthresh,
180 minarea=minarea,
181 filter_kernel=filter_kernel,
182 filter_type=filter_type,
183 deblend_nthresh=deblend_nthresh,
184 deblend_cont=deblend_cont,
185 clean=clean,
186 clean_param=clean_param,
187 segmentation_map=True,
188 )
189
190 # show the background
191 fig, ax = plt.subplots()
192 im = ax.imshow(bkg_array, interpolation="nearest", cmap="gray", origin="lower")
193 fig.colorbar(im)
194 fig.savefig("./bkg_image.png", format="png", bbox_inches="tight")
195
196 # show the background noise
197 fig, ax = plt.subplots()
198 im = ax.imshow(bkg_rms, interpolation="nearest", cmap="gray", origin="lower")
199 ax.set_title(f"This is array_rms. While float_globalrms={bkg.globalrms}")
200 fig.colorbar(im)
201 fig.savefig("./bkg_rms.png", format="png", bbox_inches="tight")
202
203 # plot image
204 fig, ax = plt.subplots()
205 m, s = np.mean(data), np.std(data)
206 im = ax.imshow(
207 data,
208 interpolation="nearest",
209 cmap="gray",
210 vmin=m - s,
211 vmax=m + s,
212 origin="lower",
213 )
214 fig.colorbar(im)
215 fig.savefig("./fits2image.png", format="png", bbox_inches="tight")
216
217 # show the segmentation map
218 fig, ax = plt.subplots()
219 im = ax.imshow(
220 segmap,
221 interpolation="nearest",
222 cmap="gray",
223 origin="lower",
224 vmin=0,
225 vmax=1,
226 )
227 fig.colorbar(im)
228 fig.savefig("./segmap.png", format="png", bbox_inches="tight")
229
230 # plot background-subtracted image
231 fig, ax = plt.subplots()
232 m, s = np.mean(data_sub), np.std(data_sub)
233 im = ax.imshow(
234 data_sub,
235 interpolation="nearest",
236 cmap="gray",
237 vmin=m - s,
238 vmax=m + s,
239 origin="lower",
240 )
241
242 # plot an ellipse for each object
243 for i in range(len(objects)):
244 e = Ellipse(
245 xy=(objects["x"][i], objects["y"][i]),
246 width=6 * objects["a"][i],
247 height=6 * objects["b"][i],
248 angle=objects["theta"][i] * 180.0 / np.pi,
249 )
250 e.set_facecolor("none")
251 e.set_edgecolor("red")
252 ax.add_artist(e)
253
254 fig.savefig("./sources.png", format="png", bbox_inches="tight")
255
256 plt.show()
257
258 from oda_api.data_products import ODAAstropyTable
259
260 cat = ODAAstropyTable(Table(data=objects))
261 hdu_rms = fits.PrimaryHDU(segmap.astype("uint32"))
262 hdu_rms.writeto("segmentation_map.fits")
263
264 bkg_picture = "./bkg_image.png" # oda:POSIXPath
265 rms_picture = "./bkg_rms.png" # oda:POSIXPath
266 data_picture = "./fits2image.png" # oda:POSIXPath
267 sources_picture = "./sources.png" # oda:POSIXPath
268 segmentation_map_picture = "./segmap.png" # oda:POSIXPath
269 segmentation_map = "./segmentation_map.fits" # oda:POSIXPath
270 bkg_array = "./bkg_array.fits" # oda:POSIXPath
271 rms_array = "./bkg_rms.fits" # oda:POSIXPath
272 catalog_table = cat # oda:ODAAstropyTable
273
274 # output gathering
275 _galaxy_meta_data = {}
276 _oda_outs = []
277 _oda_outs.append(
278 (
279 "out_source_extraction_catalog_table",
280 "catalog_table_galaxy.output",
281 catalog_table,
282 )
283 )
284
285 for _outn, _outfn, _outv in _oda_outs:
286 _galaxy_outfile_name = os.path.join(_galaxy_wd, _outfn)
287 if isinstance(_outv, str) and os.path.isfile(_outv):
288 shutil.move(_outv, _galaxy_outfile_name)
289 _galaxy_meta_data[_outn] = {"ext": "_sniff_"}
290 elif getattr(_outv, "write_fits_file", None):
291 _outv.write_fits_file(_galaxy_outfile_name)
292 _galaxy_meta_data[_outn] = {"ext": "fits"}
293 elif getattr(_outv, "write_file", None):
294 _outv.write_file(_galaxy_outfile_name)
295 _galaxy_meta_data[_outn] = {"ext": "_sniff_"}
296 else:
297 with open(_galaxy_outfile_name, "w") as fd:
298 json.dump(_outv, fd, cls=CustomJSONEncoder)
299 _galaxy_meta_data[_outn] = {"ext": "json"}
300 _simple_outs = []
301 _simple_outs.append(
302 (
303 "out_source_extraction_bkg_picture",
304 "bkg_picture_galaxy.output",
305 bkg_picture,
306 )
307 )
308 _simple_outs.append(
309 (
310 "out_source_extraction_rms_picture",
311 "rms_picture_galaxy.output",
312 rms_picture,
313 )
314 )
315 _simple_outs.append(
316 (
317 "out_source_extraction_data_picture",
318 "data_picture_galaxy.output",
319 data_picture,
320 )
321 )
322 _simple_outs.append(
323 (
324 "out_source_extraction_sources_picture",
325 "sources_picture_galaxy.output",
326 sources_picture,
327 )
328 )
329 _simple_outs.append(
330 (
331 "out_source_extraction_segmentation_map_picture",
332 "segmentation_map_picture_galaxy.output",
333 segmentation_map_picture,
334 )
335 )
336 _simple_outs.append(
337 (
338 "out_source_extraction_segmentation_map",
339 "segmentation_map_galaxy.output",
340 segmentation_map,
341 )
342 )
343 _simple_outs.append(
344 ("out_source_extraction_bkg_array", "bkg_array_galaxy.output", bkg_array)
345 )
346 _simple_outs.append(
347 ("out_source_extraction_rms_array", "rms_array_galaxy.output", rms_array)
348 )
349 _numpy_available = True
350
351 for _outn, _outfn, _outv in _simple_outs:
352 _galaxy_outfile_name = os.path.join(_galaxy_wd, _outfn)
353 if isinstance(_outv, str) and os.path.isfile(_outv):
354 shutil.move(_outv, _galaxy_outfile_name)
355 _galaxy_meta_data[_outn] = {"ext": "_sniff_"}
356 elif _numpy_available and isinstance(_outv, np.ndarray):
357 with open(_galaxy_outfile_name, "wb") as fd:
358 np.savez(fd, _outv)
359 _galaxy_meta_data[_outn] = {"ext": "npz"}
360 else:
361 with open(_galaxy_outfile_name, "w") as fd:
362 json.dump(_outv, fd)
363 _galaxy_meta_data[_outn] = {"ext": "expression.json"}
364
365 with open(os.path.join(_galaxy_wd, "galaxy.json"), "w") as fd:
366 json.dump(_galaxy_meta_data, fd)
367 print("*** Job finished successfully ***")