Mercurial > repos > astroteam > source_extractor_astro_tool
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 ***") |
