noisechisel-torch 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.
- noisechisel_torch/__init__.py +25 -0
- noisechisel_torch/cli.py +123 -0
- noisechisel_torch/core.py +1318 -0
- noisechisel_torch/io.py +113 -0
- noisechisel_torch-0.1.0.dist-info/METADATA +230 -0
- noisechisel_torch-0.1.0.dist-info/RECORD +10 -0
- noisechisel_torch-0.1.0.dist-info/WHEEL +5 -0
- noisechisel_torch-0.1.0.dist-info/entry_points.txt +2 -0
- noisechisel_torch-0.1.0.dist-info/licenses/LICENSE +674 -0
- noisechisel_torch-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""PyTorch re-implementation of Gnuastro's NoiseChisel (2D images)."""
|
|
3
|
+
from .core import (
|
|
4
|
+
DEFAULT_KERNEL,
|
|
5
|
+
NoiseChisel,
|
|
6
|
+
NoiseChiselParams,
|
|
7
|
+
NoiseChiselResult,
|
|
8
|
+
gaussian_kernel,
|
|
9
|
+
noisechisel,
|
|
10
|
+
)
|
|
11
|
+
from .io import run_fits, write_fits
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"DEFAULT_KERNEL",
|
|
17
|
+
"NoiseChisel",
|
|
18
|
+
"NoiseChiselParams",
|
|
19
|
+
"NoiseChiselResult",
|
|
20
|
+
"gaussian_kernel",
|
|
21
|
+
"noisechisel",
|
|
22
|
+
"run_fits",
|
|
23
|
+
"write_fits",
|
|
24
|
+
"__version__",
|
|
25
|
+
]
|
noisechisel_torch/cli.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""Command line interface, modelled on ``astnoisechisel``::
|
|
3
|
+
|
|
4
|
+
noisechisel-torch image.fits -h 1 -o out.fits --tilesize 30,30 --device cuda
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import fields
|
|
13
|
+
from typing import get_args, get_origin, get_type_hints
|
|
14
|
+
|
|
15
|
+
from .core import NoiseChiselParams
|
|
16
|
+
|
|
17
|
+
# Sizes given in FITS (x, y) order on the command line, as in Gnuastro; the
|
|
18
|
+
# Python API uses numpy (rows, cols) order.
|
|
19
|
+
FITS_ORDER = ("tilesize", "largetilesize")
|
|
20
|
+
|
|
21
|
+
# Gnuastro's short option names.
|
|
22
|
+
SHORT = {"meanmedqdiff": "-Q", "qthresh": "-t", "erode": "-e", "opening": "-p",
|
|
23
|
+
"minskyfrac": "-B", "sigmaclip": "-s", "dthresh": "-R", "snminarea": "-m",
|
|
24
|
+
"snquant": "-c", "detgrowquant": "-d"}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _parse_value(text, typ):
|
|
28
|
+
origin = get_origin(typ)
|
|
29
|
+
args = [a for a in get_args(typ) if a is not type(None)]
|
|
30
|
+
if origin is not None and type(None) in get_args(typ): # Optional[...]
|
|
31
|
+
if text.lower() == "none":
|
|
32
|
+
return None
|
|
33
|
+
return _parse_value(text, args[0])
|
|
34
|
+
if origin is tuple:
|
|
35
|
+
parts = text.split(",")
|
|
36
|
+
return tuple(a(p) for a, p in zip(args, parts))
|
|
37
|
+
if typ is bool:
|
|
38
|
+
return text.lower() in ("1", "true", "yes", "y")
|
|
39
|
+
return typ(text)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _hdu(text):
|
|
43
|
+
return int(text) if text.lstrip("-").isdigit() else text
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _read_kernel(path, hdu):
|
|
47
|
+
from astropy.io import fits
|
|
48
|
+
return fits.getdata(path, ext=_hdu(hdu))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_parser():
|
|
52
|
+
ap = argparse.ArgumentParser(
|
|
53
|
+
prog="noisechisel-torch", add_help=False,
|
|
54
|
+
description="PyTorch NoiseChisel: detect signal and measure Sky/STD of a FITS image.")
|
|
55
|
+
ap.add_argument("--help", action="help")
|
|
56
|
+
ap.add_argument("input", help="input FITS image")
|
|
57
|
+
ap.add_argument("-h", "--hdu", default="0", help="input HDU (number or name)")
|
|
58
|
+
ap.add_argument("-o", "--output", help="output FITS (default: <input>_detected.fits)")
|
|
59
|
+
ap.add_argument("--device", default=None, help="cpu | cuda | cuda:N (default: auto)")
|
|
60
|
+
ap.add_argument("-N", "--numthreads", type=int, default=None, metavar="N",
|
|
61
|
+
help="number of CPU threads (default: all cores)")
|
|
62
|
+
ap.add_argument("--ccl", default="auto", choices=["auto", "cupy", "scipy"])
|
|
63
|
+
ap.add_argument("-k", "--kernel", default=None,
|
|
64
|
+
help="kernel FITS file, or 'none' (default: Gnuastro's built-in kernel)")
|
|
65
|
+
ap.add_argument("--khdu", default="1", help="HDU of --kernel")
|
|
66
|
+
ap.add_argument("--widekernel", default=None, help="wide kernel FITS file")
|
|
67
|
+
ap.add_argument("--whdu", default="1", help="HDU of --widekernel")
|
|
68
|
+
ap.add_argument("--label", action="store_true", help="write labels instead of a 0/1 mask")
|
|
69
|
+
ap.add_argument("--rawoutput", action="store_true", help="skip the INPUT-NO-SKY HDU")
|
|
70
|
+
ap.add_argument("--oneelempertile", action="store_true",
|
|
71
|
+
help="write SKY/SKY_STD with one value per tile (smaller output)")
|
|
72
|
+
ap.add_argument("-q", "--quiet", action="store_true")
|
|
73
|
+
hints = get_type_hints(NoiseChiselParams)
|
|
74
|
+
for f in fields(NoiseChiselParams):
|
|
75
|
+
names = ([SHORT[f.name]] if f.name in SHORT else []) + [f"--{f.name}"]
|
|
76
|
+
if hints[f.name] is bool: # flags, as in Gnuastro
|
|
77
|
+
ap.add_argument(*names, dest=f.name, action="store_const", const=True,
|
|
78
|
+
default=None, help="(default: off)")
|
|
79
|
+
else:
|
|
80
|
+
ap.add_argument(*names, dest=f.name, default=None, metavar="VAL",
|
|
81
|
+
help=f"(default: {f.default})")
|
|
82
|
+
ap.set_defaults(_hints=hints)
|
|
83
|
+
return ap
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main(argv=None):
|
|
87
|
+
from .io import run_fits
|
|
88
|
+
|
|
89
|
+
ap = build_parser()
|
|
90
|
+
a = ap.parse_args(argv)
|
|
91
|
+
hints = a._hints
|
|
92
|
+
params = {}
|
|
93
|
+
for f in fields(NoiseChiselParams):
|
|
94
|
+
v = getattr(a, f.name)
|
|
95
|
+
if v is True:
|
|
96
|
+
params[f.name] = True
|
|
97
|
+
elif v is not None:
|
|
98
|
+
params[f.name] = _parse_value(v, hints[f.name])
|
|
99
|
+
if f.name in FITS_ORDER and isinstance(params[f.name], tuple):
|
|
100
|
+
params[f.name] = params[f.name][::-1]
|
|
101
|
+
hdu = _hdu(a.hdu)
|
|
102
|
+
if a.kernel is not None:
|
|
103
|
+
params["kernel"] = None if a.kernel.lower() == "none" else _read_kernel(a.kernel, a.khdu)
|
|
104
|
+
if a.widekernel is not None:
|
|
105
|
+
params["widekernel"] = _read_kernel(a.widekernel, a.whdu)
|
|
106
|
+
out = a.output or os.path.splitext(os.path.basename(a.input))[0] + "_detected.fits"
|
|
107
|
+
t0 = time.perf_counter()
|
|
108
|
+
try:
|
|
109
|
+
res = run_fits(a.input, out, hdu=hdu, device=a.device, ccl=a.ccl,
|
|
110
|
+
numthreads=a.numthreads, label=a.label,
|
|
111
|
+
rawoutput=a.rawoutput, oneelempertile=a.oneelempertile,
|
|
112
|
+
verbose=not a.quiet, **params)
|
|
113
|
+
except OSError as e:
|
|
114
|
+
print(f"noisechisel-torch: error: {e}", file=sys.stderr)
|
|
115
|
+
return 1
|
|
116
|
+
if not a.quiet:
|
|
117
|
+
print(f"{res.num_detections} detections, S/N thresh {res.det_sn_threshold:.3f}, "
|
|
118
|
+
f"median std {res.med_std:.6g} -> {out} [{time.perf_counter() - t0:.2f}s]")
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
sys.exit(main())
|