graxpert 3.2.0a0.dev4__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.
- graxpert/AstroImageRepository.py +114 -0
- graxpert/__init__.py +1 -0
- graxpert/ai_model_handling.py +215 -0
- graxpert/app_state.py +10 -0
- graxpert/application/__init__.py +0 -0
- graxpert/application/app.py +741 -0
- graxpert/application/app_events.py +96 -0
- graxpert/application/eventbus.py +24 -0
- graxpert/astroimage.py +308 -0
- graxpert/background_extraction.py +290 -0
- graxpert/background_flood_selection.py +240 -0
- graxpert/background_grid_selection.py +64 -0
- graxpert/cmdline_tools.py +547 -0
- graxpert/commands.py +200 -0
- graxpert/deconvolution.py +176 -0
- graxpert/denoising.py +178 -0
- graxpert/grid_utils.py +38 -0
- graxpert/img/GraXpert_LOGO_Hauptvariante.png +0 -0
- graxpert/img/Icon.png +0 -0
- graxpert/img/LDN1235_original.jpg +0 -0
- graxpert/img/LDN1235_processed.jpg +0 -0
- graxpert/img/__init__.py +0 -0
- graxpert/img/gfx_numbers.png +0 -0
- graxpert/img/gfx_numbers.svg +737 -0
- graxpert/img/graXpert_Startbadge_Umbriel.png +0 -0
- graxpert/img/hourglass.png +0 -0
- graxpert/locales/__init__.py +0 -0
- graxpert/locales/de_DE/LC_MESSAGES/base.mo +0 -0
- graxpert/localization.py +39 -0
- graxpert/main.py +441 -0
- graxpert/mp_logging.py +97 -0
- graxpert/parallel_processing.py +3 -0
- graxpert/preferences.py +139 -0
- graxpert/radialbasisinterpolation.py +215 -0
- graxpert/resource_utils.py +35 -0
- graxpert/s3_secrets.py +16 -0
- graxpert/skyall.py +152 -0
- graxpert/stretch.py +151 -0
- graxpert/theme/__init__.py +0 -0
- graxpert/theme/graxpert-dark-blue.json +367 -0
- graxpert/ui/__init__.py +1 -0
- graxpert/ui/application_frame.py +115 -0
- graxpert/ui/canvas.py +558 -0
- graxpert/ui/left_menu.py +437 -0
- graxpert/ui/loadingframe.py +142 -0
- graxpert/ui/right_menu.py +315 -0
- graxpert/ui/statusbar.py +102 -0
- graxpert/ui/styling.py +23 -0
- graxpert/ui/tooltip.py +194 -0
- graxpert/ui/ui_events.py +19 -0
- graxpert/ui/widgets.py +297 -0
- graxpert/ui_scaling.py +13 -0
- graxpert/version.py +2 -0
- graxpert-3.2.0a0.dev4.dist-info/METADATA +219 -0
- graxpert-3.2.0a0.dev4.dist-info/RECORD +58 -0
- graxpert-3.2.0a0.dev4.dist-info/WHEEL +5 -0
- graxpert-3.2.0a0.dev4.dist-info/entry_points.txt +2 -0
- graxpert-3.2.0a0.dev4.dist-info/top_level.txt +1 -0
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
graxpert/localization.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import gettext
|
|
2
|
+
import locale
|
|
3
|
+
import os
|
|
4
|
+
from importlib import resources
|
|
5
|
+
|
|
6
|
+
from appdirs import user_config_dir
|
|
7
|
+
|
|
8
|
+
from graxpert.preferences import load_preferences
|
|
9
|
+
from graxpert.resource_utils import resource_path
|
|
10
|
+
|
|
11
|
+
prefs_file = os.path.join(user_config_dir(appname="GraXpert"), "preferences.json")
|
|
12
|
+
prefs = load_preferences(prefs_file)
|
|
13
|
+
|
|
14
|
+
lang = None
|
|
15
|
+
if prefs.lang is None:
|
|
16
|
+
lang, enc = locale.getlocale()
|
|
17
|
+
if lang is None:
|
|
18
|
+
lang = "en_EN"
|
|
19
|
+
elif lang.startswith("de") or lang.startswith("gsw"):
|
|
20
|
+
lang = "de_DE"
|
|
21
|
+
else:
|
|
22
|
+
lang = "en_EN"
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
lang = prefs.lang
|
|
26
|
+
if lang == "Deutsch":
|
|
27
|
+
lang = "de_DE"
|
|
28
|
+
else:
|
|
29
|
+
lang = "en_EN"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
with resources.as_file(resources.files('graxpert').joinpath('locales')) as localedir:
|
|
33
|
+
global lang_gettext
|
|
34
|
+
lang_gettext = gettext.translation("base", localedir=localedir, languages=[lang], fallback=True)
|
|
35
|
+
lang_gettext.install()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _(text):
|
|
39
|
+
return lang_gettext.gettext(text)
|
graxpert/main.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
# ensure sys.stdout and sys.stderr are not None in PyInstaller environments
|
|
6
|
+
if sys.stdout is None:
|
|
7
|
+
sys.stdout = open(os.devnull, "w")
|
|
8
|
+
if sys.stderr is None:
|
|
9
|
+
sys.stderr = open(os.devnull, "w")
|
|
10
|
+
import argparse
|
|
11
|
+
import logging
|
|
12
|
+
import multiprocessing
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
import faulthandler
|
|
16
|
+
import types
|
|
17
|
+
|
|
18
|
+
from packaging import version
|
|
19
|
+
|
|
20
|
+
# Must be before all other graxpert imports
|
|
21
|
+
try:
|
|
22
|
+
# A load-time check for key external package dependencies. Crash and warn user.
|
|
23
|
+
import tkinter
|
|
24
|
+
except ImportError:
|
|
25
|
+
# If it fails, print a helpful message and exit.
|
|
26
|
+
print("ERROR: The required 'tkinter' GUI library is not installed.", file=sys.stderr)
|
|
27
|
+
print("\nPlease install it using your system's package manager.", file=sys.stderr)
|
|
28
|
+
print(" For Debian/Ubuntu: sudo apt-get install python3-tk", file=sys.stderr)
|
|
29
|
+
print(" For Fedora: sudo dnf install python3-tkinter", file=sys.stderr)
|
|
30
|
+
print(" For Arch Linux: sudo pacman -S tk", file=sys.stderr)
|
|
31
|
+
print(" For Windows/OS-X: It should be included with your Python installation by default.", file=sys.stderr)
|
|
32
|
+
sys.exit(1) # Exit with error
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
from graxpert.ai_model_handling import bge_ai_models_dir, denoise_ai_models_dir, deconvolution_object_ai_models_dir, deconvolution_stars_ai_models_dir, list_local_versions, list_remote_versions
|
|
36
|
+
from graxpert.mp_logging import configure_logging
|
|
37
|
+
from graxpert.s3_secrets import bge_bucket_name, denoise_bucket_name, deconvolution_object_bucket_name, deconvolution_stars_bucket_name
|
|
38
|
+
from graxpert.version import release as graxpert_release
|
|
39
|
+
from graxpert.version import version as graxpert_version
|
|
40
|
+
from graxpert.resource_utils import temp_cleanup
|
|
41
|
+
|
|
42
|
+
def collect_available_versions(ai_models_dir, bucket_name):
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
available_local_versions = sorted(
|
|
46
|
+
[v["version"] for v in list_local_versions(ai_models_dir)],
|
|
47
|
+
key=lambda k: version.parse(k),
|
|
48
|
+
reverse=True,
|
|
49
|
+
)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
available_local_versions = ""
|
|
52
|
+
logging.exception(e)
|
|
53
|
+
try:
|
|
54
|
+
available_remote_versions = sorted(
|
|
55
|
+
[v["version"] for v in list_remote_versions(bucket_name)],
|
|
56
|
+
key=lambda k: version.parse(k),
|
|
57
|
+
reverse=True,
|
|
58
|
+
)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
available_remote_versions = ""
|
|
61
|
+
logging.exception(e)
|
|
62
|
+
|
|
63
|
+
return (available_local_versions, available_remote_versions)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def bge_version_type(arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$")):
|
|
67
|
+
return version_type(bge_ai_models_dir, bge_bucket_name, arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$"))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def denoise_version_type(arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$")):
|
|
71
|
+
return version_type(denoise_ai_models_dir, denoise_bucket_name, arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$"))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def deconv_obj_version_type(arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$")):
|
|
75
|
+
return version_type(deconvolution_object_ai_models_dir, deconvolution_object_bucket_name, arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$"))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def deconv_stellar_version_type(arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$")):
|
|
79
|
+
return version_type(deconvolution_stars_ai_models_dir, deconvolution_stars_bucket_name, arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$"))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def version_type(ai_models_dir, bucket_name, arg_value, pat=re.compile(r"^\d+\.\d+\.\d+$")):
|
|
83
|
+
|
|
84
|
+
available_versions = collect_available_versions(ai_models_dir, bucket_name)
|
|
85
|
+
available_local_versions = available_versions[0]
|
|
86
|
+
available_remote_versions = available_versions[1]
|
|
87
|
+
|
|
88
|
+
if not pat.match(arg_value):
|
|
89
|
+
raise argparse.ArgumentTypeError("invalid version, expected format: n.n.n")
|
|
90
|
+
if arg_value not in available_local_versions and arg_value not in available_remote_versions:
|
|
91
|
+
raise argparse.ArgumentTypeError(
|
|
92
|
+
"provided version neither found locally or remotely; available locally: [{}], available remotely: [{}]".format(
|
|
93
|
+
", ".join(available_local_versions),
|
|
94
|
+
", ".join(available_remote_versions),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
if not available_local_versions and not available_remote_versions:
|
|
98
|
+
raise argparse.ArgumentTypeError("no AI versions available locally or remotely")
|
|
99
|
+
return arg_value
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def ui_main(open_with_file=None):
|
|
103
|
+
import logging
|
|
104
|
+
import tkinter as tk
|
|
105
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
106
|
+
from datetime import datetime
|
|
107
|
+
from inspect import signature
|
|
108
|
+
from tkinter import messagebox
|
|
109
|
+
|
|
110
|
+
import requests
|
|
111
|
+
from appdirs import user_config_dir
|
|
112
|
+
from customtkinter import CTk
|
|
113
|
+
|
|
114
|
+
from graxpert.application.app import graxpert
|
|
115
|
+
from graxpert.application.app_events import AppEvents
|
|
116
|
+
from graxpert.application.eventbus import eventbus
|
|
117
|
+
from graxpert.localization import _
|
|
118
|
+
from graxpert.mp_logging import initialize_logging, shutdown_logging
|
|
119
|
+
from graxpert.parallel_processing import executor
|
|
120
|
+
from graxpert.preferences import app_state_2_prefs, save_preferences
|
|
121
|
+
from graxpert.resource_utils import resource_photoimage
|
|
122
|
+
from graxpert.ui.application_frame import ApplicationFrame
|
|
123
|
+
from graxpert.ui.styling import style
|
|
124
|
+
from graxpert.ui.ui_events import UiEvents
|
|
125
|
+
from graxpert.version import release, version
|
|
126
|
+
|
|
127
|
+
def on_closing(root: CTk, logging_thread):
|
|
128
|
+
app_state_2_prefs(graxpert.prefs, graxpert.cmd.app_state)
|
|
129
|
+
|
|
130
|
+
prefs_filename = os.path.join(user_config_dir(appname="GraXpert"), "preferences.json")
|
|
131
|
+
save_preferences(prefs_filename, graxpert.prefs)
|
|
132
|
+
try:
|
|
133
|
+
if "cancel_futures" in signature(ProcessPoolExecutor.shutdown).parameters:
|
|
134
|
+
executor.shutdown(cancel_futures=True) # Python > 3.8
|
|
135
|
+
else:
|
|
136
|
+
executor.shutdown() # Python <= 3.8
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logging.exception("error shutting down ProcessPoolExecutor")
|
|
140
|
+
shutdown_logging(logging_thread)
|
|
141
|
+
root.destroy()
|
|
142
|
+
logging.shutdown()
|
|
143
|
+
sys.exit(0)
|
|
144
|
+
|
|
145
|
+
def check_for_new_version():
|
|
146
|
+
try:
|
|
147
|
+
response = requests.get("https://api.github.com/repos/Steffenhir/GraXpert/releases/latest", timeout=2.5)
|
|
148
|
+
latest_release_date = datetime.strptime(response.json()["created_at"], "%Y-%m-%dT%H:%M:%SZ")
|
|
149
|
+
|
|
150
|
+
response_current = requests.get("https://api.github.com/repos/Steffenhir/GraXpert/releases/tags/" + version, timeout=2.5)
|
|
151
|
+
current_release_date = datetime.strptime(response_current.json()["created_at"], "%Y-%m-%dT%H:%M:%SZ")
|
|
152
|
+
current_is_beta = response_current.json()["prerelease"]
|
|
153
|
+
|
|
154
|
+
if current_is_beta:
|
|
155
|
+
if current_release_date >= latest_release_date:
|
|
156
|
+
messagebox.showinfo(
|
|
157
|
+
title=_("This is a Beta release!"), message=_("Please note that this is a Beta release of GraXpert. You will be notified when a newer official version is available.")
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
messagebox.showinfo(
|
|
161
|
+
title=_("New official release available!"),
|
|
162
|
+
message=_("This Beta version is deprecated. A newer official release of GraXpert is available at") + " https://github.com/Steffenhir/GraXpert/releases/latest",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
elif latest_release_date > current_release_date:
|
|
166
|
+
messagebox.showinfo(title=_("New version available!"), message=_("A newer version of GraXpert is available at") + " https://github.com/Steffenhir/GraXpert/releases/latest")
|
|
167
|
+
except:
|
|
168
|
+
logging.warning("Could not check for newest version")
|
|
169
|
+
|
|
170
|
+
logging_thread = initialize_logging()
|
|
171
|
+
|
|
172
|
+
style()
|
|
173
|
+
root = CTk()
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
if "Linux" == platform.system():
|
|
177
|
+
root.attributes("-zoomed", True)
|
|
178
|
+
else:
|
|
179
|
+
root.state("zoomed")
|
|
180
|
+
except Exception as e:
|
|
181
|
+
root.state("normal")
|
|
182
|
+
logging.warning(e, stack_info=True)
|
|
183
|
+
|
|
184
|
+
root.title("GraXpert | Release: '{}' ({})".format(release, version))
|
|
185
|
+
root.iconbitmap()
|
|
186
|
+
root.iconphoto(True, resource_photoimage("Icon.png"))
|
|
187
|
+
# root.option_add("*TkFDialog*foreground", "black")
|
|
188
|
+
root.protocol("WM_DELETE_WINDOW", lambda: on_closing(root, logging_thread))
|
|
189
|
+
root.createcommand("::tk::mac::Quit", lambda: on_closing(root, logging_thread))
|
|
190
|
+
root.minsize(width=800, height=600)
|
|
191
|
+
root.columnconfigure(0, weight=1)
|
|
192
|
+
root.rowconfigure(0, weight=1)
|
|
193
|
+
app = ApplicationFrame(root)
|
|
194
|
+
app.grid(column=0, row=0, sticky=tk.NSEW)
|
|
195
|
+
root.update()
|
|
196
|
+
check_for_new_version()
|
|
197
|
+
|
|
198
|
+
if open_with_file and len(open_with_file) > 0:
|
|
199
|
+
eventbus.emit(AppEvents.LOAD_IMAGE_REQUEST, {"filename": open_with_file})
|
|
200
|
+
else:
|
|
201
|
+
eventbus.emit(UiEvents.DISPLAY_START_BADGE_REQUEST)
|
|
202
|
+
|
|
203
|
+
root.mainloop()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def parse_args():
|
|
207
|
+
available_bge_versions = collect_available_versions(bge_ai_models_dir, bge_bucket_name)
|
|
208
|
+
available_denoise_versions = collect_available_versions(denoise_ai_models_dir, denoise_bucket_name)
|
|
209
|
+
available_deconv_obj_versions = collect_available_versions(deconvolution_object_ai_models_dir, deconvolution_object_bucket_name)
|
|
210
|
+
available_deconv_stellar_versions = collect_available_versions(deconvolution_stars_ai_models_dir, deconvolution_stars_bucket_name)
|
|
211
|
+
|
|
212
|
+
parser = argparse.ArgumentParser(add_help=False)
|
|
213
|
+
parser.add_argument(
|
|
214
|
+
"-cmd",
|
|
215
|
+
"--command",
|
|
216
|
+
required=False,
|
|
217
|
+
default="background-extraction",
|
|
218
|
+
choices=["background-extraction", "denoising", "deconv-obj", "deconv-stellar"],
|
|
219
|
+
type=str,
|
|
220
|
+
help="Choose the image operation to execute: Background Extraction or Denoising or Deconvolution",
|
|
221
|
+
)
|
|
222
|
+
parser.add_argument("filename", type=str, help="Path of the unprocessed image")
|
|
223
|
+
parser.add_argument("-output", "--output", nargs="?", required=False, type=str, help="Filename of the processed image")
|
|
224
|
+
parser.add_argument(
|
|
225
|
+
"-preferences_file",
|
|
226
|
+
"--preferences_file",
|
|
227
|
+
nargs="?",
|
|
228
|
+
required=False,
|
|
229
|
+
default=None,
|
|
230
|
+
type=str,
|
|
231
|
+
help="Allows GraXpert commandline to run all extraction methods based on a preferences file that contains background grid points",
|
|
232
|
+
)
|
|
233
|
+
parser.add_argument("-gpu", "--gpu_acceleration", type=str, choices=["true", "false"], default=None, help="Set to 'false' in order to disable gpu acceleration during AI inference.")
|
|
234
|
+
parser.add_argument("-v", "--version", action="version", version=f"GraXpert version: {graxpert_version} release: {graxpert_release}")
|
|
235
|
+
parser.add_argument("-cli", "--cli", required=False, action="store_true", help="Deprecated (no longer required)")
|
|
236
|
+
|
|
237
|
+
bge_parser = argparse.ArgumentParser("GraXpert Background Extraction", parents=[parser], description="GraXpert, the astronomical background extraction tool")
|
|
238
|
+
bge_parser.add_argument(
|
|
239
|
+
"-ai_version",
|
|
240
|
+
"--ai_version",
|
|
241
|
+
nargs="?",
|
|
242
|
+
required=False,
|
|
243
|
+
default=None,
|
|
244
|
+
type=bge_version_type,
|
|
245
|
+
help='Version of the Background Extraction AI model, default: "latest"; available locally: [{}], available remotely: [{}]'.format(
|
|
246
|
+
", ".join(available_bge_versions[0]), ", ".join(available_bge_versions[1])
|
|
247
|
+
),
|
|
248
|
+
)
|
|
249
|
+
bge_parser.add_argument("-correction", "--correction", nargs="?", required=False, default=None, choices=["Subtraction", "Division"], type=str, help="Subtraction or Division")
|
|
250
|
+
bge_parser.add_argument("-smoothing", "--smoothing", nargs="?", required=False, default=None, type=float, help="Strength of smoothing between 0 and 1")
|
|
251
|
+
bge_parser.add_argument("-bg", "--bg", required=False, action="store_true", help="Also save the background model")
|
|
252
|
+
|
|
253
|
+
denoise_parser = argparse.ArgumentParser("GraXpert Denoising", parents=[parser], description="GraXpert, the astronomical denoising tool")
|
|
254
|
+
denoise_parser.add_argument(
|
|
255
|
+
"-ai_version",
|
|
256
|
+
"--ai_version",
|
|
257
|
+
nargs="?",
|
|
258
|
+
required=False,
|
|
259
|
+
default=None,
|
|
260
|
+
type=denoise_version_type,
|
|
261
|
+
help='Version of the Denoising AI model, default: "latest"; available locally: [{}], available remotely: [{}]'.format(
|
|
262
|
+
", ".join(available_denoise_versions[0]), ", ".join(available_denoise_versions[1])
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
denoise_parser.add_argument(
|
|
266
|
+
"-strength",
|
|
267
|
+
"--denoise_strength",
|
|
268
|
+
nargs="?",
|
|
269
|
+
required=False,
|
|
270
|
+
default=None,
|
|
271
|
+
type=float,
|
|
272
|
+
help='Strength of the desired denoising effect, default: "0.5"',
|
|
273
|
+
)
|
|
274
|
+
denoise_parser.add_argument(
|
|
275
|
+
"-batch_size",
|
|
276
|
+
"--ai_batch_size",
|
|
277
|
+
nargs="?",
|
|
278
|
+
required=False,
|
|
279
|
+
default=None,
|
|
280
|
+
type=int,
|
|
281
|
+
help='Number of image tiles which Graxpert will denoise in parallel. Be careful: increasing this value might result in out-of-memory errors. Valid Range: 1..32, default: "4"',
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
deconv_obj_parser = argparse.ArgumentParser("GraXpert Deconvolution Object", parents=[parser], description="GraXpert, the astronomical deconvolution tool")
|
|
285
|
+
deconv_obj_parser.add_argument(
|
|
286
|
+
"-ai_version",
|
|
287
|
+
"--ai_version",
|
|
288
|
+
nargs="?",
|
|
289
|
+
required=False,
|
|
290
|
+
default=None,
|
|
291
|
+
type=deconv_obj_version_type,
|
|
292
|
+
help='Version of the Deconvolution Obj AI model, default: "latest"; available locally: [{}], available remotely: [{}]'.format(
|
|
293
|
+
", ".join(available_deconv_obj_versions[0]), ", ".join(available_deconv_obj_versions[1])
|
|
294
|
+
),
|
|
295
|
+
)
|
|
296
|
+
deconv_obj_parser.add_argument(
|
|
297
|
+
"-strength",
|
|
298
|
+
"--deconvolution_strength",
|
|
299
|
+
nargs="?",
|
|
300
|
+
required=False,
|
|
301
|
+
default=None,
|
|
302
|
+
type=float,
|
|
303
|
+
help='Strength of the desired deconvolution effect, default: "0.5"',
|
|
304
|
+
)
|
|
305
|
+
deconv_obj_parser.add_argument(
|
|
306
|
+
"-psfsize",
|
|
307
|
+
"--deconvolution_psfsize",
|
|
308
|
+
nargs="?",
|
|
309
|
+
required=False,
|
|
310
|
+
default=None,
|
|
311
|
+
type=float,
|
|
312
|
+
help='Size of the seeing you want to deconvolve, default: "0.3"',
|
|
313
|
+
)
|
|
314
|
+
deconv_obj_parser.add_argument(
|
|
315
|
+
"-batch_size",
|
|
316
|
+
"--ai_batch_size",
|
|
317
|
+
nargs="?",
|
|
318
|
+
required=False,
|
|
319
|
+
default=None,
|
|
320
|
+
type=int,
|
|
321
|
+
help='Number of image tiles which Graxpert will denoise in parallel. Be careful: increasing this value might result in out-of-memory errors. Valid Range: 1..32, default: "4"',
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
deconv_stellar_parser = argparse.ArgumentParser("GraXpert Deconvolution Stellar", parents=[parser], description="GraXpert, the astronomical denoising tool")
|
|
325
|
+
deconv_stellar_parser.add_argument(
|
|
326
|
+
"-ai_version",
|
|
327
|
+
"--ai_version",
|
|
328
|
+
nargs="?",
|
|
329
|
+
required=False,
|
|
330
|
+
default=None,
|
|
331
|
+
type=deconv_stellar_version_type,
|
|
332
|
+
help='Version of the Deconvolution Stellar AI model, default: "latest"; available locally: [{}], available remotely: [{}]'.format(
|
|
333
|
+
", ".join(available_deconv_stellar_versions[0]), ", ".join(available_deconv_stellar_versions[1])
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
deconv_stellar_parser.add_argument(
|
|
337
|
+
"-strength",
|
|
338
|
+
"--deconvolution_strength",
|
|
339
|
+
nargs="?",
|
|
340
|
+
required=False,
|
|
341
|
+
default=None,
|
|
342
|
+
type=float,
|
|
343
|
+
help='Strength of the desired deconvolution effect, default: "0.5"',
|
|
344
|
+
)
|
|
345
|
+
deconv_stellar_parser.add_argument(
|
|
346
|
+
"-psfsize",
|
|
347
|
+
"--deconvolution_psfsize",
|
|
348
|
+
nargs="?",
|
|
349
|
+
required=False,
|
|
350
|
+
default=None,
|
|
351
|
+
type=float,
|
|
352
|
+
help='Size of the seeing you want to deconvolve, default: "0.3"',
|
|
353
|
+
)
|
|
354
|
+
deconv_stellar_parser.add_argument(
|
|
355
|
+
"-batch_size",
|
|
356
|
+
"--ai_batch_size",
|
|
357
|
+
nargs="?",
|
|
358
|
+
required=False,
|
|
359
|
+
default=None,
|
|
360
|
+
type=int,
|
|
361
|
+
help='Number of image tiles which Graxpert will denoise in parallel. Be careful: increasing this value might result in out-of-memory errors. Valid Range: 1..32, default: "4"',
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if "-h" in sys.argv or "--help" in sys.argv:
|
|
365
|
+
if "background-extraction" in sys.argv:
|
|
366
|
+
bge_parser.print_help()
|
|
367
|
+
elif "denoising" in sys.argv:
|
|
368
|
+
denoise_parser.print_help()
|
|
369
|
+
elif "deconv-obj" in sys.argv:
|
|
370
|
+
deconv_obj_parser.print_help()
|
|
371
|
+
elif "deconv-stellar" in sys.argv:
|
|
372
|
+
deconv_stellar_parser.print_help()
|
|
373
|
+
else:
|
|
374
|
+
parser.print_help()
|
|
375
|
+
sys.exit(0)
|
|
376
|
+
|
|
377
|
+
args, extras = parser.parse_known_args()
|
|
378
|
+
|
|
379
|
+
if args.command == "background-extraction":
|
|
380
|
+
args = bge_parser.parse_args()
|
|
381
|
+
elif args.command == "deconv-obj":
|
|
382
|
+
args = deconv_obj_parser.parse_args()
|
|
383
|
+
elif args.command == "deconv-stellar":
|
|
384
|
+
args = deconv_stellar_parser.parse_args()
|
|
385
|
+
elif args.command == "denoising":
|
|
386
|
+
args = denoise_parser.parse_args()
|
|
387
|
+
|
|
388
|
+
return args
|
|
389
|
+
|
|
390
|
+
def main():
|
|
391
|
+
"""Note: this is entered directly via the entry_point definition in setup.py or called from below"""
|
|
392
|
+
|
|
393
|
+
multiprocessing.freeze_support()
|
|
394
|
+
faulthandler.enable(sys.__stderr__)
|
|
395
|
+
|
|
396
|
+
try:
|
|
397
|
+
# listing available versions might be slow, so only do it if we have command line args
|
|
398
|
+
if len(sys.argv) > 1:
|
|
399
|
+
args = parse_args()
|
|
400
|
+
else:
|
|
401
|
+
# Dummy noarg defs
|
|
402
|
+
args = types.SimpleNamespace(command=None, filename=None)
|
|
403
|
+
|
|
404
|
+
# Note: we wait to setup logging until after parsing args, so that --help response doesn't get log framing
|
|
405
|
+
configure_logging()
|
|
406
|
+
|
|
407
|
+
if args.command == "background-extraction":
|
|
408
|
+
from graxpert.cmdline_tools import BGECmdlineTool
|
|
409
|
+
|
|
410
|
+
logging.info(f"Starting GraXpert CLI, Background-Extraction, version: {graxpert_version} release: {graxpert_release}")
|
|
411
|
+
clt = BGECmdlineTool(args)
|
|
412
|
+
clt.execute()
|
|
413
|
+
elif args.command == "denoising":
|
|
414
|
+
from graxpert.cmdline_tools import DenoiseCmdlineTool
|
|
415
|
+
|
|
416
|
+
logging.info(f"Starting GraXpert CLI, Denoising, version: {graxpert_version} release: {graxpert_release}")
|
|
417
|
+
clt = DenoiseCmdlineTool(args)
|
|
418
|
+
clt.execute()
|
|
419
|
+
elif args.command == "deconv-obj":
|
|
420
|
+
from graxpert.cmdline_tools import DeconvObjCmdlineTool
|
|
421
|
+
|
|
422
|
+
logging.info(f"Starting GraXpert CLI, Deconvolution Obj, version: {graxpert_version} release: {graxpert_release}")
|
|
423
|
+
clt = DeconvObjCmdlineTool(args)
|
|
424
|
+
clt.execute()
|
|
425
|
+
elif args.command == "deconv-stellar":
|
|
426
|
+
from graxpert.cmdline_tools import DeconvStellarCmdlineTool
|
|
427
|
+
|
|
428
|
+
logging.info(f"Starting GraXpert CLI, Deconvolution Stellar, version: {graxpert_version} release: {graxpert_release}")
|
|
429
|
+
clt = DeconvStellarCmdlineTool(args)
|
|
430
|
+
clt.execute()
|
|
431
|
+
else:
|
|
432
|
+
logging.info(f"Starting GraXpert UI, version: {graxpert_version} release: {graxpert_release}")
|
|
433
|
+
ui_main(args.filename)
|
|
434
|
+
finally:
|
|
435
|
+
temp_cleanup()
|
|
436
|
+
logging.shutdown()
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
if __name__ == "__main__":
|
|
440
|
+
main()
|
|
441
|
+
|
graxpert/mp_logging.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import logging.handlers
|
|
3
|
+
import multiprocessing
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
from appdirs import user_log_dir
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StreamToLogger(object):
|
|
12
|
+
"""
|
|
13
|
+
Fake file-like stream object that redirects writes to a logger instance.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, logger, log_level=logging.INFO):
|
|
17
|
+
self.logger = logger
|
|
18
|
+
self.log_level = log_level
|
|
19
|
+
self.linebuf = ""
|
|
20
|
+
self.line_complete = False
|
|
21
|
+
|
|
22
|
+
def write(self, buf):
|
|
23
|
+
self.linebuf += buf
|
|
24
|
+
if self.linebuf.count("\n") > 0:
|
|
25
|
+
self.line_complete = True
|
|
26
|
+
self.flush()
|
|
27
|
+
|
|
28
|
+
def flush(self):
|
|
29
|
+
if self.line_complete:
|
|
30
|
+
for s in self.linebuf.splitlines():
|
|
31
|
+
self.logger.log(self.log_level, s.rstrip())
|
|
32
|
+
self.linebuf = ""
|
|
33
|
+
self.line_complete = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# cf. https://docs.python.org/3/howto/logging-cookbook.html#using-concurrent-futures-processpoolexecutor
|
|
37
|
+
|
|
38
|
+
logging_queue = None
|
|
39
|
+
logfile_name = os.path.join(user_log_dir(appname="GraXpert"), "graxpert.log")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_logging_queue():
|
|
43
|
+
global logging_queue
|
|
44
|
+
if logging_queue is None:
|
|
45
|
+
logging_queue = multiprocessing.Manager().Queue(-1)
|
|
46
|
+
return logging_queue
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def configure_logging():
|
|
50
|
+
os.makedirs(os.path.dirname(logfile_name), exist_ok=True)
|
|
51
|
+
root = logging.getLogger()
|
|
52
|
+
root.setLevel(logging.INFO)
|
|
53
|
+
h = logging.handlers.RotatingFileHandler(logfile_name, "a", maxBytes=1000000, backupCount=5, encoding="utf-8")
|
|
54
|
+
f = logging.Formatter("%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s")
|
|
55
|
+
h.setFormatter(f)
|
|
56
|
+
root.handlers = []
|
|
57
|
+
root.addHandler(h)
|
|
58
|
+
|
|
59
|
+
h2 = logging.StreamHandler()
|
|
60
|
+
h2.setFormatter(f)
|
|
61
|
+
root.addHandler(h2)
|
|
62
|
+
|
|
63
|
+
# stdout_logger = logging.getLogger("STDOUT")
|
|
64
|
+
sl = StreamToLogger(root, logging.INFO)
|
|
65
|
+
sys.stdout = sl
|
|
66
|
+
|
|
67
|
+
# stderr_logger = logging.getLogger("STDERR")
|
|
68
|
+
sl = StreamToLogger(root, logging.ERROR)
|
|
69
|
+
sys.stderr = sl
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def worker_configurer(queue):
|
|
73
|
+
h = logging.handlers.QueueHandler(queue) # Just the one handler needed
|
|
74
|
+
root = logging.getLogger()
|
|
75
|
+
root.addHandler(h)
|
|
76
|
+
# send all messages, for demo; no other level or filter logic applied.
|
|
77
|
+
root.setLevel(logging.INFO)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def logger_thread(queue):
|
|
81
|
+
while True:
|
|
82
|
+
record = queue.get()
|
|
83
|
+
if record is None:
|
|
84
|
+
break
|
|
85
|
+
logger = logging.getLogger(record.name)
|
|
86
|
+
logger.handle(record)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def initialize_logging():
|
|
90
|
+
logging_thread = threading.Thread(target=logger_thread, args=(get_logging_queue(),), daemon=True)
|
|
91
|
+
logging_thread.start()
|
|
92
|
+
return logging_thread
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def shutdown_logging(logging_thread):
|
|
96
|
+
get_logging_queue().put(None)
|
|
97
|
+
logging_thread.join()
|