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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from graxpert.astroimage import AstroImage
|
|
5
|
+
from graxpert.stretch import StretchParameters, calculate_mtf_stretch_parameters_for_image, stretch_all
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ImageTypes(StrEnum):
|
|
9
|
+
Original = "Original"
|
|
10
|
+
Gradient_Corrected = "Gradient-Corrected"
|
|
11
|
+
Background = "Background"
|
|
12
|
+
Deconvolved_Object_only = "Deconvolved Object-only"
|
|
13
|
+
Deconvolved_Stars_only = "Deconvolved Stars-only"
|
|
14
|
+
Denoised = "Denoised"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AstroImageRepository:
|
|
18
|
+
|
|
19
|
+
images: Dict = {
|
|
20
|
+
ImageTypes.Original: None,
|
|
21
|
+
ImageTypes.Gradient_Corrected: None,
|
|
22
|
+
ImageTypes.Background: None,
|
|
23
|
+
ImageTypes.Deconvolved_Object_only: None,
|
|
24
|
+
ImageTypes.Deconvolved_Stars_only: None,
|
|
25
|
+
ImageTypes.Denoised: None,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def set(self, type: ImageTypes, image: AstroImage):
|
|
29
|
+
self.images[type] = image
|
|
30
|
+
|
|
31
|
+
def get(self, type: ImageTypes):
|
|
32
|
+
return self.images[type]
|
|
33
|
+
|
|
34
|
+
def stretch_all(self, stretch_params: StretchParameters, saturation: float):
|
|
35
|
+
|
|
36
|
+
if self.get(ImageTypes.Original) is None:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
stretches = []
|
|
40
|
+
|
|
41
|
+
if not stretch_params.do_stretch:
|
|
42
|
+
for key, image in self.images.items():
|
|
43
|
+
if image is not None:
|
|
44
|
+
stretches.append(image.img_array)
|
|
45
|
+
|
|
46
|
+
else:
|
|
47
|
+
|
|
48
|
+
all_image_arrays = []
|
|
49
|
+
all_mtf_stretch_params = []
|
|
50
|
+
|
|
51
|
+
all_image_arrays.append(self.get(ImageTypes.Original).img_array)
|
|
52
|
+
all_mtf_stretch_params.append(calculate_mtf_stretch_parameters_for_image(stretch_params, self.get(ImageTypes.Original).img_array))
|
|
53
|
+
|
|
54
|
+
if self.get(ImageTypes.Gradient_Corrected) is not None and self.get(ImageTypes.Background) is not None:
|
|
55
|
+
all_image_arrays.append(self.get(ImageTypes.Gradient_Corrected).img_array)
|
|
56
|
+
all_mtf_stretch_params.append(calculate_mtf_stretch_parameters_for_image(stretch_params, self.get(ImageTypes.Gradient_Corrected).img_array))
|
|
57
|
+
|
|
58
|
+
all_image_arrays.append(self.get(ImageTypes.Background).img_array)
|
|
59
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[0])
|
|
60
|
+
|
|
61
|
+
if self.get(ImageTypes.Deconvolved_Object_only) is not None and self.get(ImageTypes.Gradient_Corrected) is None:
|
|
62
|
+
all_image_arrays.append(self.get(ImageTypes.Deconvolved_Object_only).img_array)
|
|
63
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[0])
|
|
64
|
+
|
|
65
|
+
elif self.get(ImageTypes.Deconvolved_Object_only) is not None and self.get(ImageTypes.Gradient_Corrected) is not None:
|
|
66
|
+
all_image_arrays.append(self.get(ImageTypes.Deconvolved_Object_only).img_array)
|
|
67
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[1])
|
|
68
|
+
|
|
69
|
+
if self.get(ImageTypes.Deconvolved_Stars_only) is not None and self.get(ImageTypes.Gradient_Corrected) is None:
|
|
70
|
+
all_image_arrays.append(self.get(ImageTypes.Deconvolved_Stars_only).img_array)
|
|
71
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[0])
|
|
72
|
+
|
|
73
|
+
elif self.get(ImageTypes.Deconvolved_Stars_only) is not None and self.get(ImageTypes.Gradient_Corrected) is not None:
|
|
74
|
+
all_image_arrays.append(self.get(ImageTypes.Deconvolved_Stars_only).img_array)
|
|
75
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[1])
|
|
76
|
+
|
|
77
|
+
if self.get(ImageTypes.Denoised) is not None and self.get(ImageTypes.Gradient_Corrected) is None:
|
|
78
|
+
all_image_arrays.append(self.get(ImageTypes.Denoised).img_array)
|
|
79
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[0])
|
|
80
|
+
|
|
81
|
+
elif self.get(ImageTypes.Denoised) is not None and self.get(ImageTypes.Gradient_Corrected) is not None:
|
|
82
|
+
all_image_arrays.append(self.get(ImageTypes.Denoised).img_array)
|
|
83
|
+
all_mtf_stretch_params.append(all_mtf_stretch_params[1])
|
|
84
|
+
|
|
85
|
+
stretches = stretch_all(all_image_arrays, all_mtf_stretch_params)
|
|
86
|
+
|
|
87
|
+
i = 0
|
|
88
|
+
for key, image in self.images.items():
|
|
89
|
+
if image is not None:
|
|
90
|
+
image.update_display_from_array(stretches[i], saturation)
|
|
91
|
+
i = i + 1
|
|
92
|
+
|
|
93
|
+
def crop_all(self, start_x: float, end_x: float, start_y: float, end_y: float):
|
|
94
|
+
for key, astroimg in self.images.items():
|
|
95
|
+
if astroimg is not None:
|
|
96
|
+
astroimg.crop(start_x, end_x, start_y, end_y)
|
|
97
|
+
|
|
98
|
+
def update_saturation(self, saturation):
|
|
99
|
+
for key, value in self.images.items():
|
|
100
|
+
if value is not None:
|
|
101
|
+
value.update_saturation(saturation)
|
|
102
|
+
|
|
103
|
+
def reset(self):
|
|
104
|
+
for key, value in self.images.items():
|
|
105
|
+
self.images[key] = None
|
|
106
|
+
|
|
107
|
+
def display_options(self):
|
|
108
|
+
display_options = []
|
|
109
|
+
|
|
110
|
+
for key, value in self.images.items():
|
|
111
|
+
if self.images[key] is not None:
|
|
112
|
+
display_options.append(key)
|
|
113
|
+
|
|
114
|
+
return display_options
|
graxpert/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import re
|
|
5
|
+
import shutil
|
|
6
|
+
import zipfile
|
|
7
|
+
|
|
8
|
+
from appdirs import user_data_dir
|
|
9
|
+
from minio import Minio
|
|
10
|
+
from packaging import version
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from graxpert.s3_secrets import endpoint, ro_access_key, ro_secret_key
|
|
14
|
+
|
|
15
|
+
client = Minio(endpoint, ro_access_key, ro_secret_key)
|
|
16
|
+
except Exception as e:
|
|
17
|
+
logging.exception(e)
|
|
18
|
+
client = None
|
|
19
|
+
|
|
20
|
+
from graxpert.ui.loadingframe import DynamicProgressThread
|
|
21
|
+
|
|
22
|
+
ai_models_dir = os.path.join(user_data_dir(appname="GraXpert"), "ai-models")
|
|
23
|
+
bge_ai_models_dir = os.path.join(user_data_dir(appname="GraXpert"), "bge-ai-models")
|
|
24
|
+
|
|
25
|
+
# old ai-models folder exists, rename to 'bge-ai-models'
|
|
26
|
+
if os.path.exists(ai_models_dir):
|
|
27
|
+
logging.warning(f"Older 'ai_models_dir' {ai_models_dir} exists. Renaming to {bge_ai_models_dir} due to introduction of new denoising models in GraXpert 3.")
|
|
28
|
+
try:
|
|
29
|
+
os.rename(ai_models_dir, bge_ai_models_dir)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
logging.error(f"Renaming {ai_models_dir} to {bge_ai_models_dir} failed. {bge_ai_models_dir} will be newly created. Consider deleting obsolete {ai_models_dir} manually.")
|
|
32
|
+
|
|
33
|
+
os.makedirs(bge_ai_models_dir, exist_ok=True)
|
|
34
|
+
|
|
35
|
+
deconvolution_object_ai_models_dir = os.path.join(user_data_dir(appname="GraXpert"), "deconvolution-object-ai-models")
|
|
36
|
+
os.makedirs(deconvolution_object_ai_models_dir, exist_ok=True)
|
|
37
|
+
deconvolution_stars_ai_models_dir = os.path.join(user_data_dir(appname="GraXpert"), "deconvolution-stars-ai-models")
|
|
38
|
+
os.makedirs(deconvolution_stars_ai_models_dir, exist_ok=True)
|
|
39
|
+
denoise_ai_models_dir = os.path.join(user_data_dir(appname="GraXpert"), "denoise-ai-models")
|
|
40
|
+
os.makedirs(denoise_ai_models_dir, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ui operations
|
|
44
|
+
def list_remote_versions(bucket_name):
|
|
45
|
+
if client is None:
|
|
46
|
+
return []
|
|
47
|
+
try:
|
|
48
|
+
objects = client.list_objects(bucket_name)
|
|
49
|
+
versions = []
|
|
50
|
+
|
|
51
|
+
for o in objects:
|
|
52
|
+
tags = client.get_object_tags(o.bucket_name, o.object_name)
|
|
53
|
+
if tags is not None and "ai-version" in tags:
|
|
54
|
+
versions.append(
|
|
55
|
+
{
|
|
56
|
+
"bucket": o.bucket_name,
|
|
57
|
+
"object": o.object_name,
|
|
58
|
+
"version": tags["ai-version"],
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
return versions
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logging.exception(e)
|
|
65
|
+
finally:
|
|
66
|
+
return versions
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def list_local_versions(ai_models_dir):
|
|
70
|
+
try:
|
|
71
|
+
model_dirs = [
|
|
72
|
+
{"path": os.path.join(ai_models_dir, f), "version": f}
|
|
73
|
+
for f in os.listdir(ai_models_dir)
|
|
74
|
+
if re.search(r"\d\.\d\.\d", f) and len(os.listdir(os.path.join(ai_models_dir, f))) > 0 # match semantic version
|
|
75
|
+
]
|
|
76
|
+
return model_dirs
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logging.exception(e)
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def latest_version(ai_models_dir, bucket_name):
|
|
83
|
+
try:
|
|
84
|
+
remote_versions = list_remote_versions(bucket_name)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
remote_versions = []
|
|
87
|
+
logging.exception(e)
|
|
88
|
+
try:
|
|
89
|
+
local_versions = list_local_versions(ai_models_dir)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
local_versions = []
|
|
92
|
+
logging.exception(e)
|
|
93
|
+
ai_options = set([])
|
|
94
|
+
ai_options.update([rv["version"] for rv in remote_versions])
|
|
95
|
+
ai_options.update(set([lv["version"] for lv in local_versions]))
|
|
96
|
+
ai_options = sorted(ai_options, key=lambda k: version.parse(k), reverse=True)
|
|
97
|
+
return ai_options[0]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def ai_model_path_from_version(ai_models_dir, local_version):
|
|
101
|
+
if local_version is None:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
return os.path.join(ai_models_dir, local_version, "model.onnx")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def compute_orphaned_local_versions(ai_models_dir):
|
|
108
|
+
remote_versions = list_remote_versions(ai_models_dir)
|
|
109
|
+
|
|
110
|
+
if remote_versions is None:
|
|
111
|
+
logging.warning("Could not fetch remote versions. Thus, aborting cleaning of local versions in {}. Consider manual cleaning".format(ai_models_dir))
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
local_versions = list_local_versions()
|
|
115
|
+
|
|
116
|
+
if local_versions is None:
|
|
117
|
+
logging.warning("Could not read local versions in {}. Thus, aborting cleaning. Consider manual cleaning".format(ai_models_dir))
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
orphaned_local_versions = [{"path": lv["path"], "version": lv["version"]} for lv in local_versions if lv["version"] not in [rv["version"] for rv in remote_versions]]
|
|
121
|
+
|
|
122
|
+
return orphaned_local_versions
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def cleanup_orphaned_local_versions(orphaned_local_versions):
|
|
126
|
+
for olv in orphaned_local_versions:
|
|
127
|
+
try:
|
|
128
|
+
shutil.rmtree(olv["path"])
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logging.exception(e)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def download_version(ai_models_dir, bucket_name, target_version, progress=None):
|
|
134
|
+
try:
|
|
135
|
+
remote_versions = list_remote_versions(bucket_name)
|
|
136
|
+
for r in remote_versions:
|
|
137
|
+
if target_version == r["version"]:
|
|
138
|
+
remote_version = r
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
ai_model_dir = os.path.join(ai_models_dir, "{}".format(remote_version["version"]))
|
|
142
|
+
os.makedirs(ai_model_dir, exist_ok=True)
|
|
143
|
+
|
|
144
|
+
ai_model_file = os.path.join(ai_model_dir, "model.onnx")
|
|
145
|
+
ai_model_zip = os.path.join(ai_model_dir, "model.zip")
|
|
146
|
+
client.fget_object(
|
|
147
|
+
remote_version["bucket"],
|
|
148
|
+
remote_version["object"],
|
|
149
|
+
ai_model_zip,
|
|
150
|
+
progress=DynamicProgressThread(callback=progress),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
with zipfile.ZipFile(ai_model_zip, "r") as zip_ref:
|
|
154
|
+
zip_ref.extractall(ai_model_dir)
|
|
155
|
+
|
|
156
|
+
if not os.path.isfile(ai_model_file):
|
|
157
|
+
raise ValueError(f"Could not find ai 'model.onnx' file after extracting {ai_model_zip}")
|
|
158
|
+
os.remove(ai_model_zip)
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
# try to delete (rollback) ai_model_dir in case of errors
|
|
162
|
+
logging.exception(e)
|
|
163
|
+
try:
|
|
164
|
+
shutil.rmtree(ai_model_dir)
|
|
165
|
+
except Exception as e2:
|
|
166
|
+
logging.exception(e2)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def validate_local_version(ai_models_dir, local_version):
|
|
170
|
+
return os.path.isfile(os.path.join(ai_models_dir, local_version, "model.onnx"))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_execution_providers_ordered(gpu_acceleration=True):
|
|
174
|
+
if gpu_acceleration:
|
|
175
|
+
supported_providers = [
|
|
176
|
+
(
|
|
177
|
+
"OpenVINOExecutionProvider",
|
|
178
|
+
{
|
|
179
|
+
# per https://onnxruntime.ai/docs/execution-providers/OpenVINO-ExecutionProvider.html#summary-of-options
|
|
180
|
+
# "device_type": "HETERO:GPU,CPU,NPU AUTO:GPU,CPU,NPU MULTI:GPU,CPU,NPU", # Will prefer dGPU, fallback to iGPU, NPU or CPU with extra Intel specific optimizations
|
|
181
|
+
"device_type": "AUTO:GPU,CPU", # Will prefer dGPU, fallback to iGPU, NPU or CPU with extra Intel specific optimizations
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
"ROCMExecutionProvider",
|
|
185
|
+
"DmlExecutionProvider",
|
|
186
|
+
(
|
|
187
|
+
"CoreMLExecutionProvider",
|
|
188
|
+
{
|
|
189
|
+
"flags": "COREML_FLAG_CREATE_MLPROGRAM",
|
|
190
|
+
},
|
|
191
|
+
),
|
|
192
|
+
"CUDAExecutionProvider",
|
|
193
|
+
"CPUExecutionProvider",
|
|
194
|
+
]
|
|
195
|
+
else:
|
|
196
|
+
supported_providers = ["CPUExecutionProvider"]
|
|
197
|
+
|
|
198
|
+
result = []
|
|
199
|
+
try:
|
|
200
|
+
import onnxruntime as ort
|
|
201
|
+
available = ort.get_available_providers()
|
|
202
|
+
for provider in supported_providers:
|
|
203
|
+
if isinstance(provider, tuple):
|
|
204
|
+
if provider[0] in available:
|
|
205
|
+
result.append(provider) # Append the entire tuple
|
|
206
|
+
else:
|
|
207
|
+
if provider in available:
|
|
208
|
+
result.append(provider)
|
|
209
|
+
return result
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logging.error("Critical error! The required ONNX Runtime (AI library) package is misconfigured.\n" \
|
|
212
|
+
"Please read the README.md to confirm that you've selected the correct build for your hardware.\n" \
|
|
213
|
+
"If you are using one of our prebuilt executables, please file a bug with the following information:\n"
|
|
214
|
+
"{}".format(e))
|
|
215
|
+
sys.exit(1)
|
graxpert/app_state.py
ADDED
|
File without changes
|