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
graxpert/commands.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from graxpert.app_state import INITIAL_STATE, AppState
|
|
10
|
+
from graxpert.background_grid_selection import background_grid_selection
|
|
11
|
+
from graxpert.background_flood_selection import background_flood_selection
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ICommandHandler(ABC):
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def undo(
|
|
21
|
+
self, cur_state: AppState, prev_state: AppState, cmd_args: Dict
|
|
22
|
+
) -> AppState:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def redo(
|
|
27
|
+
self, cur_state: AppState, next_state: AppState, cmd_args: Dict
|
|
28
|
+
) -> AppState:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def progress(self) -> float:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Command:
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self, handler: ICommandHandler, prev: Command = None, **kwargs
|
|
40
|
+
) -> None:
|
|
41
|
+
self.prev: Command = prev
|
|
42
|
+
self.next: Command = None
|
|
43
|
+
self.handler: ICommandHandler = handler
|
|
44
|
+
self.app_state: AppState = None
|
|
45
|
+
self.cmd_args = kwargs
|
|
46
|
+
|
|
47
|
+
def execute(self) -> AppState:
|
|
48
|
+
if self.prev is None:
|
|
49
|
+
app_state = INITIAL_STATE
|
|
50
|
+
else:
|
|
51
|
+
app_state = self.prev.app_state
|
|
52
|
+
self.app_state = self.handler.execute(app_state, self.cmd_args)
|
|
53
|
+
if self.prev is not None:
|
|
54
|
+
self.prev.next = self
|
|
55
|
+
return self.app_state
|
|
56
|
+
|
|
57
|
+
def undo(self) -> Command:
|
|
58
|
+
if self.prev is None:
|
|
59
|
+
prev_state = INITIAL_STATE
|
|
60
|
+
return self
|
|
61
|
+
else:
|
|
62
|
+
prev_state = self.prev.app_state
|
|
63
|
+
self.prev.app_state = self.handler.undo(self.app_state, prev_state, self.cmd_args)
|
|
64
|
+
return self.prev
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def redo(self) -> Command:
|
|
68
|
+
assert self.next is not None
|
|
69
|
+
next_state = self.next.app_state
|
|
70
|
+
self.next.app_state = self.handler.redo(self.app_state, next_state, self.cmd_args)
|
|
71
|
+
return self.next
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class InitHandler(ICommandHandler):
|
|
75
|
+
|
|
76
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
77
|
+
state = INITIAL_STATE
|
|
78
|
+
state.background_points = cmd_args["background_points"]
|
|
79
|
+
return state
|
|
80
|
+
|
|
81
|
+
def undo(
|
|
82
|
+
self, cur_state: AppState, prev_state: AppState, cmd_args: Dict
|
|
83
|
+
) -> AppState:
|
|
84
|
+
state = INITIAL_STATE
|
|
85
|
+
state.background_points = cmd_args["background_points"]
|
|
86
|
+
return state
|
|
87
|
+
|
|
88
|
+
def redo(
|
|
89
|
+
self, cur_state: AppState, next_state: AppState, cmd_args: Dict
|
|
90
|
+
) -> AppState:
|
|
91
|
+
return deepcopy(next_state)
|
|
92
|
+
|
|
93
|
+
def progress(self) -> float:
|
|
94
|
+
return 1.0
|
|
95
|
+
|
|
96
|
+
class PointHandler(ICommandHandler):
|
|
97
|
+
def undo(
|
|
98
|
+
self, cur_state: AppState, prev_state: AppState, cmd_args: Dict
|
|
99
|
+
) -> AppState:
|
|
100
|
+
app_state_copy = deepcopy(cur_state)
|
|
101
|
+
prev_background_points = deepcopy(prev_state.background_points)
|
|
102
|
+
app_state_copy.background_points = prev_background_points
|
|
103
|
+
return app_state_copy
|
|
104
|
+
|
|
105
|
+
def redo(
|
|
106
|
+
self, cur_state: AppState, next_state: AppState, cmd_args: Dict
|
|
107
|
+
) -> AppState:
|
|
108
|
+
app_state_copy = deepcopy(cur_state)
|
|
109
|
+
next_background_points = deepcopy(next_state.background_points)
|
|
110
|
+
app_state_copy.background_points = next_background_points
|
|
111
|
+
return app_state_copy
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class AddPointHandler(PointHandler):
|
|
115
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
116
|
+
app_state_copy = deepcopy(app_state)
|
|
117
|
+
point = cmd_args["point"]
|
|
118
|
+
app_state_copy.background_points.append(point)
|
|
119
|
+
return app_state_copy
|
|
120
|
+
|
|
121
|
+
def progress(self) -> float:
|
|
122
|
+
return 1.0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class AddPointsHandler(PointHandler):
|
|
126
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
127
|
+
app_state_copy = deepcopy(app_state)
|
|
128
|
+
point = cmd_args["point"]
|
|
129
|
+
background_points = app_state_copy.background_points
|
|
130
|
+
tol = cmd_args["tol"]
|
|
131
|
+
bg_pts = cmd_args["bg_pts"]
|
|
132
|
+
sample_size = cmd_args["sample_size"]
|
|
133
|
+
image = cmd_args["image"]
|
|
134
|
+
new_points = background_flood_selection(point, background_points, tol, bg_pts, sample_size, image)
|
|
135
|
+
app_state_copy.background_points.extend(new_points)
|
|
136
|
+
return app_state_copy
|
|
137
|
+
|
|
138
|
+
def progress(self) -> float:
|
|
139
|
+
return 1.0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class RemovePointHandler(PointHandler):
|
|
143
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
144
|
+
app_state_copy = deepcopy(app_state)
|
|
145
|
+
idx = cmd_args["idx"]
|
|
146
|
+
app_state_copy.background_points.pop(idx)
|
|
147
|
+
return app_state_copy
|
|
148
|
+
|
|
149
|
+
def progress(self) -> float:
|
|
150
|
+
return 1.0
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class MovePointHandler(PointHandler):
|
|
154
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
155
|
+
app_state_copy = deepcopy(app_state)
|
|
156
|
+
idx = cmd_args["idx"]
|
|
157
|
+
new_point = cmd_args["new_point"]
|
|
158
|
+
|
|
159
|
+
if len(new_point) == 0:
|
|
160
|
+
app_state_copy.background_points.pop(idx)
|
|
161
|
+
else:
|
|
162
|
+
app_state_copy.background_points[idx] = new_point
|
|
163
|
+
|
|
164
|
+
return app_state_copy
|
|
165
|
+
|
|
166
|
+
def progress(self) -> float:
|
|
167
|
+
return 1.0
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class SelectPointsHandler(PointHandler):
|
|
171
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
172
|
+
app_state_copy = deepcopy(app_state)
|
|
173
|
+
data = cmd_args["data"]
|
|
174
|
+
num_pts = cmd_args["num_pts"]
|
|
175
|
+
tol = cmd_args["tol"]
|
|
176
|
+
sample_size = cmd_args["sample_size"]
|
|
177
|
+
automatic_points = background_grid_selection(data, num_pts, tol, sample_size)
|
|
178
|
+
app_state_copy.background_points = automatic_points
|
|
179
|
+
return app_state_copy
|
|
180
|
+
|
|
181
|
+
def progress(self) -> float:
|
|
182
|
+
return 1.0
|
|
183
|
+
|
|
184
|
+
class ResetPointsHandler(PointHandler):
|
|
185
|
+
def execute(self, app_state: AppState, cmd_args: Dict) -> AppState:
|
|
186
|
+
app_state_copy = deepcopy(app_state)
|
|
187
|
+
app_state_copy.background_points.clear()
|
|
188
|
+
return app_state_copy
|
|
189
|
+
|
|
190
|
+
def progress(self) -> float:
|
|
191
|
+
return 1.0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
INIT_HANDLER = InitHandler()
|
|
195
|
+
ADD_POINT_HANDLER = AddPointHandler()
|
|
196
|
+
ADD_POINTS_HANDLER = AddPointsHandler()
|
|
197
|
+
RM_POINT_HANDLER = RemovePointHandler()
|
|
198
|
+
MOVE_POINT_HANDLER = MovePointHandler()
|
|
199
|
+
SEL_POINTS_HANDLER = SelectPointsHandler()
|
|
200
|
+
RESET_POINTS_HANDLER = ResetPointsHandler()
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from graxpert.ai_model_handling import get_execution_providers_ordered
|
|
7
|
+
from graxpert.application.app_events import AppEvents
|
|
8
|
+
from graxpert.application.eventbus import eventbus
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def deconvolve(image, ai_path, strength, psfsize, batch_size=4, window_size=512, stride=448, progress=None, ai_gpu_acceleration=True):
|
|
12
|
+
logging.info("Starting deconvolution")
|
|
13
|
+
if "stars" in ai_path:
|
|
14
|
+
type = "Stellar"
|
|
15
|
+
elif "obj" in ai_path:
|
|
16
|
+
type = "Obj"
|
|
17
|
+
strength = 0.95 * strength # TODO : strenght of exactly 1.0 brings no results, to fix
|
|
18
|
+
|
|
19
|
+
if type == "Stellar":
|
|
20
|
+
psfsize = np.clip((psfsize / 2.355 - 1.5) / 3.0, 0.05, 0.95) # Stellar
|
|
21
|
+
else:
|
|
22
|
+
if "1.0.0" in ai_path:
|
|
23
|
+
psfsize = np.clip((psfsize / 2.355 - 1.0) / 5.0, 0.05, 0.95) # Object v1.0.0
|
|
24
|
+
else:
|
|
25
|
+
psfsize = np.clip((psfsize / 2.355 - 0.5) / 5.5, 0.05, 0.95) # Object v1.0.1
|
|
26
|
+
|
|
27
|
+
logging.info(f"Calculated normalized PSFsize value: {psfsize}")
|
|
28
|
+
|
|
29
|
+
if batch_size < 1:
|
|
30
|
+
logging.info(f"mapping batch_size of {batch_size} to 1")
|
|
31
|
+
batch_size = 1
|
|
32
|
+
elif batch_size > 32:
|
|
33
|
+
logging.info(f"mapping batch_size of {batch_size} to 32")
|
|
34
|
+
batch_size = 32
|
|
35
|
+
elif not (batch_size & (batch_size - 1) == 0): # check if batch_size is power of two
|
|
36
|
+
logging.info(f"mapping batch_size of {batch_size} to {2 ** (batch_size).bit_length() // 2}")
|
|
37
|
+
batch_size = 2 ** (batch_size).bit_length() // 2 # map batch_size to power of two
|
|
38
|
+
|
|
39
|
+
if batch_size >= 4 and image.shape[-1] == 3:
|
|
40
|
+
batch_size = batch_size // 4
|
|
41
|
+
|
|
42
|
+
num_colors = image.shape[-1]
|
|
43
|
+
|
|
44
|
+
H, W, _ = image.shape
|
|
45
|
+
offset = int((window_size - stride) / 2)
|
|
46
|
+
|
|
47
|
+
h, w, _ = image.shape
|
|
48
|
+
|
|
49
|
+
ith = int(h / stride) + 1
|
|
50
|
+
itw = int(w / stride) + 1
|
|
51
|
+
|
|
52
|
+
dh = ith * stride - h
|
|
53
|
+
dw = itw * stride - w
|
|
54
|
+
|
|
55
|
+
image = np.concatenate((image, image[(h - dh) :, :, :]), axis=0)
|
|
56
|
+
image = np.concatenate((image, image[:, (w - dw) :, :]), axis=1)
|
|
57
|
+
|
|
58
|
+
h, w, _ = image.shape
|
|
59
|
+
image = np.concatenate((image, image[(h - offset) :, :, :]), axis=0)
|
|
60
|
+
image = np.concatenate((image[:offset, :, :], image), axis=0)
|
|
61
|
+
image = np.concatenate((image, image[:, (w - offset) :, :]), axis=1)
|
|
62
|
+
image = np.concatenate((image[:, :offset, :], image), axis=1)
|
|
63
|
+
|
|
64
|
+
output = copy.deepcopy(image)
|
|
65
|
+
|
|
66
|
+
providers = get_execution_providers_ordered(ai_gpu_acceleration)
|
|
67
|
+
import onnxruntime as ort # Must be after get_execution_providers_ordered
|
|
68
|
+
session = ort.InferenceSession(ai_path, providers=providers)
|
|
69
|
+
|
|
70
|
+
logging.info(f"Available inference providers : {providers}")
|
|
71
|
+
logging.info(f"Used inference providers : {session.get_providers()}")
|
|
72
|
+
|
|
73
|
+
cancel_flag = False
|
|
74
|
+
|
|
75
|
+
def cancel_listener(event):
|
|
76
|
+
nonlocal cancel_flag
|
|
77
|
+
cancel_flag = True
|
|
78
|
+
|
|
79
|
+
eventbus.add_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
80
|
+
|
|
81
|
+
last_progress = 0
|
|
82
|
+
for b in range(0, ith * itw + batch_size, batch_size):
|
|
83
|
+
|
|
84
|
+
if cancel_flag:
|
|
85
|
+
logging.info("Deconvolution cancelled")
|
|
86
|
+
eventbus.remove_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
input_tiles = []
|
|
90
|
+
input_tile_copies = []
|
|
91
|
+
params = []
|
|
92
|
+
for t_idx in range(0, batch_size):
|
|
93
|
+
|
|
94
|
+
index = b + t_idx
|
|
95
|
+
i = index % ith
|
|
96
|
+
j = index // ith
|
|
97
|
+
|
|
98
|
+
if i >= ith or j >= itw:
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
x = stride * i
|
|
102
|
+
y = stride * j
|
|
103
|
+
|
|
104
|
+
tile = image[x : x + window_size, y : y + window_size, :]
|
|
105
|
+
|
|
106
|
+
_min = np.min(tile, axis=(0, 1))
|
|
107
|
+
tile = tile - _min + 1e-5
|
|
108
|
+
tile = np.log(tile)
|
|
109
|
+
|
|
110
|
+
_mean = tile.mean()
|
|
111
|
+
_std = tile.std()
|
|
112
|
+
_mean, _std = _mean.astype(np.float32), _std.astype(np.float32)
|
|
113
|
+
tile = (tile - _mean) / _std * 0.1
|
|
114
|
+
params.append([_mean, _std, _min])
|
|
115
|
+
|
|
116
|
+
input_tile_copies.append(np.copy(tile))
|
|
117
|
+
|
|
118
|
+
input_tiles.append(tile)
|
|
119
|
+
|
|
120
|
+
if not input_tiles:
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
input_tiles = np.array(input_tiles)
|
|
124
|
+
input_tiles = np.moveaxis(input_tiles, -1, 1)
|
|
125
|
+
input_tiles = np.reshape(input_tiles, [input_tiles.shape[0] * num_colors, 1, window_size, window_size])
|
|
126
|
+
|
|
127
|
+
output_tiles = []
|
|
128
|
+
sigma = np.full(shape=(input_tiles.shape[0], 1), fill_value=psfsize, dtype=np.float32)
|
|
129
|
+
strenght_p = np.full(shape=(input_tiles.shape[0], 1), fill_value=strength, dtype=np.float32)
|
|
130
|
+
conds = np.concatenate([sigma, strenght_p], axis=-1)
|
|
131
|
+
if type == "Obj" and "1.0.0" in ai_path:
|
|
132
|
+
session_result = session.run(None, {"gen_input_image": input_tiles, "sigma": sigma, "strenght": strenght_p})[0]
|
|
133
|
+
else:
|
|
134
|
+
session_result = session.run(None, {"gen_input_image": input_tiles, "params": conds})[0]
|
|
135
|
+
for e in session_result:
|
|
136
|
+
output_tiles.append(e)
|
|
137
|
+
|
|
138
|
+
output_tiles = np.array(output_tiles)
|
|
139
|
+
output_tiles = input_tiles - output_tiles
|
|
140
|
+
output_tiles = np.reshape(output_tiles, [output_tiles.shape[0] // num_colors, num_colors, window_size, window_size])
|
|
141
|
+
output_tiles = np.moveaxis(output_tiles, 1, -1)
|
|
142
|
+
|
|
143
|
+
for idx in range(len(params)):
|
|
144
|
+
output_tiles[idx] = output_tiles[idx] * params[idx][1] / 0.1 + params[idx][0]
|
|
145
|
+
output_tiles[idx] = np.exp(output_tiles[idx])
|
|
146
|
+
output_tiles[idx] = output_tiles[idx] + params[idx][2] - 1e-5
|
|
147
|
+
|
|
148
|
+
for t_idx, tile in enumerate(output_tiles):
|
|
149
|
+
|
|
150
|
+
index = b + t_idx
|
|
151
|
+
i = index % ith
|
|
152
|
+
j = index // ith
|
|
153
|
+
|
|
154
|
+
if i >= ith or j >= itw:
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
x = stride * i
|
|
158
|
+
y = stride * j
|
|
159
|
+
tile = tile[offset : offset + stride, offset : offset + stride, :]
|
|
160
|
+
output[x + offset : stride * (i + 1) + offset, y + offset : stride * (j + 1) + offset, :] = tile
|
|
161
|
+
|
|
162
|
+
p = int(b / (ith * itw + batch_size) * 100)
|
|
163
|
+
if p > last_progress:
|
|
164
|
+
if progress is not None:
|
|
165
|
+
progress.update(p - last_progress)
|
|
166
|
+
else:
|
|
167
|
+
logging.info(f"Progress: {p}%")
|
|
168
|
+
last_progress = p
|
|
169
|
+
|
|
170
|
+
output = output[offset : H + offset, offset : W + offset, :]
|
|
171
|
+
output = np.clip(output, 0.0, 1.0)
|
|
172
|
+
|
|
173
|
+
eventbus.remove_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
174
|
+
logging.info("Finished deconvolution")
|
|
175
|
+
|
|
176
|
+
return output
|
graxpert/denoising.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import logging
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from graxpert.ai_model_handling import get_execution_providers_ordered
|
|
8
|
+
from graxpert.application.app_events import AppEvents
|
|
9
|
+
from graxpert.application.eventbus import eventbus
|
|
10
|
+
from graxpert.ui.ui_events import UiEvents
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def denoise(image, ai_path, strength, batch_size=4, window_size=256, stride=128, progress=None, ai_gpu_acceleration=True):
|
|
14
|
+
logging.info("Starting denoising")
|
|
15
|
+
|
|
16
|
+
if batch_size < 1:
|
|
17
|
+
logging.info(f"mapping batch_size of {batch_size} to 1")
|
|
18
|
+
batch_size = 1
|
|
19
|
+
elif batch_size > 32:
|
|
20
|
+
logging.info(f"mapping batch_size of {batch_size} to 32")
|
|
21
|
+
batch_size = 32
|
|
22
|
+
elif not (batch_size & (batch_size - 1) == 0): # check if batch_size is power of two
|
|
23
|
+
logging.info(f"mapping batch_size of {batch_size} to {2 ** (batch_size).bit_length() // 2}")
|
|
24
|
+
batch_size = 2 ** (batch_size).bit_length() // 2 # map batch_size to power of two
|
|
25
|
+
|
|
26
|
+
input = copy.deepcopy(image)
|
|
27
|
+
|
|
28
|
+
median = np.median(image[::4, ::4, :], axis=[0, 1])
|
|
29
|
+
mad = np.median(np.abs(image[::4, ::4, :] - median), axis=[0, 1])
|
|
30
|
+
|
|
31
|
+
if "1.0.0" in ai_path or "1.1.0" in ai_path:
|
|
32
|
+
model_threshold = 1.0
|
|
33
|
+
else:
|
|
34
|
+
model_threshold = 10.0
|
|
35
|
+
|
|
36
|
+
global cached_denoised_image
|
|
37
|
+
if cached_denoised_image is not None:
|
|
38
|
+
return blend_images(input, cached_denoised_image, strength, model_threshold, median, mad)
|
|
39
|
+
|
|
40
|
+
num_colors = image.shape[-1]
|
|
41
|
+
if num_colors == 1:
|
|
42
|
+
image = np.array([image[:, :, 0], image[:, :, 0], image[:, :, 0]])
|
|
43
|
+
image = np.moveaxis(image, 0, -1)
|
|
44
|
+
|
|
45
|
+
H, W, _ = image.shape
|
|
46
|
+
offset = int((window_size - stride) / 2)
|
|
47
|
+
|
|
48
|
+
h, w, _ = image.shape
|
|
49
|
+
|
|
50
|
+
ith = int(h / stride) + 1
|
|
51
|
+
itw = int(w / stride) + 1
|
|
52
|
+
|
|
53
|
+
dh = ith * stride - h
|
|
54
|
+
dw = itw * stride - w
|
|
55
|
+
|
|
56
|
+
image = np.concatenate((image, image[(h - dh) :, :, :]), axis=0)
|
|
57
|
+
image = np.concatenate((image, image[:, (w - dw) :, :]), axis=1)
|
|
58
|
+
|
|
59
|
+
h, w, _ = image.shape
|
|
60
|
+
image = np.concatenate((image, image[(h - offset) :, :, :]), axis=0)
|
|
61
|
+
image = np.concatenate((image[:offset, :, :], image), axis=0)
|
|
62
|
+
image = np.concatenate((image, image[:, (w - offset) :, :]), axis=1)
|
|
63
|
+
image = np.concatenate((image[:, :offset, :], image), axis=1)
|
|
64
|
+
|
|
65
|
+
output = copy.deepcopy(image)
|
|
66
|
+
|
|
67
|
+
providers = get_execution_providers_ordered(ai_gpu_acceleration)
|
|
68
|
+
import onnxruntime as ort # Must be after get_execution_providers_ordered
|
|
69
|
+
session = ort.InferenceSession(ai_path, providers=providers)
|
|
70
|
+
|
|
71
|
+
logging.info(f"Available inference providers : {providers}")
|
|
72
|
+
logging.info(f"Used inference providers : {session.get_providers()}")
|
|
73
|
+
|
|
74
|
+
cancel_flag = False
|
|
75
|
+
|
|
76
|
+
def cancel_listener(event):
|
|
77
|
+
nonlocal cancel_flag
|
|
78
|
+
cancel_flag = True
|
|
79
|
+
|
|
80
|
+
eventbus.add_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
81
|
+
|
|
82
|
+
last_progress = 0
|
|
83
|
+
for b in range(0, ith * itw + batch_size, batch_size):
|
|
84
|
+
|
|
85
|
+
if cancel_flag:
|
|
86
|
+
logging.info("Denoising cancelled")
|
|
87
|
+
eventbus.remove_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
input_tiles = []
|
|
91
|
+
input_tile_copies = []
|
|
92
|
+
for t_idx in range(0, batch_size):
|
|
93
|
+
|
|
94
|
+
index = b + t_idx
|
|
95
|
+
i = index % ith
|
|
96
|
+
j = index // ith
|
|
97
|
+
|
|
98
|
+
if i >= ith or j >= itw:
|
|
99
|
+
break
|
|
100
|
+
|
|
101
|
+
x = stride * i
|
|
102
|
+
y = stride * j
|
|
103
|
+
|
|
104
|
+
tile = image[x : x + window_size, y : y + window_size, :]
|
|
105
|
+
tile = (tile - median) / mad * 0.04
|
|
106
|
+
input_tile_copies.append(np.copy(tile))
|
|
107
|
+
tile = np.clip(tile, -model_threshold, model_threshold)
|
|
108
|
+
|
|
109
|
+
input_tiles.append(tile)
|
|
110
|
+
|
|
111
|
+
if not input_tiles:
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
input_tiles = np.array(input_tiles)
|
|
115
|
+
|
|
116
|
+
output_tiles = []
|
|
117
|
+
session_result = session.run(None, {"gen_input_image": input_tiles})[0]
|
|
118
|
+
for e in session_result:
|
|
119
|
+
output_tiles.append(e)
|
|
120
|
+
|
|
121
|
+
output_tiles = np.array(output_tiles)
|
|
122
|
+
|
|
123
|
+
for t_idx, tile in enumerate(output_tiles):
|
|
124
|
+
|
|
125
|
+
index = b + t_idx
|
|
126
|
+
i = index % ith
|
|
127
|
+
j = index // ith
|
|
128
|
+
|
|
129
|
+
if i >= ith or j >= itw:
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
x = stride * i
|
|
133
|
+
y = stride * j
|
|
134
|
+
tile = np.where(input_tile_copies[t_idx] < model_threshold, tile, input_tile_copies[t_idx])
|
|
135
|
+
tile = tile / 0.04 * mad + median
|
|
136
|
+
tile = tile[offset : offset + stride, offset : offset + stride, :]
|
|
137
|
+
output[x + offset : stride * (i + 1) + offset, y + offset : stride * (j + 1) + offset, :] = tile
|
|
138
|
+
|
|
139
|
+
p = int(b / (ith * itw + batch_size) * 100)
|
|
140
|
+
if p > last_progress:
|
|
141
|
+
if progress is not None:
|
|
142
|
+
progress.update(p - last_progress)
|
|
143
|
+
else:
|
|
144
|
+
logging.info(f"Progress: {p}%")
|
|
145
|
+
last_progress = p
|
|
146
|
+
|
|
147
|
+
output = output[offset : H + offset, offset : W + offset, :]
|
|
148
|
+
|
|
149
|
+
if num_colors == 1:
|
|
150
|
+
output = np.array([output[:, :, 0]])
|
|
151
|
+
output = np.moveaxis(output, 0, -1)
|
|
152
|
+
|
|
153
|
+
cached_denoised_image = output
|
|
154
|
+
output = blend_images(input, output, strength, model_threshold, median, mad)
|
|
155
|
+
|
|
156
|
+
eventbus.remove_listener(AppEvents.CANCEL_PROCESSING, cancel_listener)
|
|
157
|
+
logging.info("Finished denoising")
|
|
158
|
+
|
|
159
|
+
return output
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def blend_images(original_image, denoised_image, strength, threshold, median, mad):
|
|
163
|
+
threshold = threshold / 0.04 * mad + median
|
|
164
|
+
blend = np.where(original_image < threshold, denoised_image, original_image)
|
|
165
|
+
blend = blend * strength + original_image * (1 - strength)
|
|
166
|
+
return np.clip(blend, 0, 1)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def reset_cached_denoised_image(event):
|
|
170
|
+
global cached_denoised_image
|
|
171
|
+
cached_denoised_image = None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
cached_denoised_image = None
|
|
175
|
+
eventbus.add_listener(AppEvents.LOAD_IMAGE_REQUEST, reset_cached_denoised_image)
|
|
176
|
+
eventbus.add_listener(AppEvents.CALCULATE_REQUEST, reset_cached_denoised_image)
|
|
177
|
+
eventbus.add_listener(UiEvents.APPLY_CROP_REQUEST, reset_cached_denoised_image)
|
|
178
|
+
eventbus.add_listener(AppEvents.DENOISE_AI_VERSION_CHANGED, reset_cached_denoised_image)
|
graxpert/grid_utils.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def find_darkest_quadrant(x, y, data_padded, sample_size):
|
|
8
|
+
|
|
9
|
+
cords = [
|
|
10
|
+
[x + 0, y + 0],
|
|
11
|
+
[x + sample_size, y + sample_size],
|
|
12
|
+
[x - sample_size, y + sample_size],
|
|
13
|
+
[x + sample_size, y - sample_size],
|
|
14
|
+
[x - sample_size, y - sample_size],
|
|
15
|
+
]
|
|
16
|
+
median = []
|
|
17
|
+
for point in cords:
|
|
18
|
+
if (
|
|
19
|
+
point[0] < 0
|
|
20
|
+
or point[0] > (data_padded.shape[1] - 2 * sample_size)
|
|
21
|
+
or point[1] < 0
|
|
22
|
+
or point[1] > (data_padded.shape[0] - 2 * sample_size)
|
|
23
|
+
):
|
|
24
|
+
median.append(2)
|
|
25
|
+
else:
|
|
26
|
+
m = np.median(
|
|
27
|
+
data_padded[
|
|
28
|
+
point[1] : point[1] + 2 * sample_size,
|
|
29
|
+
point[0] : point[0] + 2 * sample_size,
|
|
30
|
+
]
|
|
31
|
+
)
|
|
32
|
+
if math.isnan(m):
|
|
33
|
+
logging.error("computed median is NaN", stack_info=True)
|
|
34
|
+
median.append(m)
|
|
35
|
+
|
|
36
|
+
min_idx = np.argmin(median)
|
|
37
|
+
|
|
38
|
+
return cords[min_idx], median[min_idx]
|
|
Binary file
|
graxpert/img/Icon.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
graxpert/img/__init__.py
ADDED
|
File without changes
|
|
Binary file
|