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.
Files changed (58) hide show
  1. graxpert/AstroImageRepository.py +114 -0
  2. graxpert/__init__.py +1 -0
  3. graxpert/ai_model_handling.py +215 -0
  4. graxpert/app_state.py +10 -0
  5. graxpert/application/__init__.py +0 -0
  6. graxpert/application/app.py +741 -0
  7. graxpert/application/app_events.py +96 -0
  8. graxpert/application/eventbus.py +24 -0
  9. graxpert/astroimage.py +308 -0
  10. graxpert/background_extraction.py +290 -0
  11. graxpert/background_flood_selection.py +240 -0
  12. graxpert/background_grid_selection.py +64 -0
  13. graxpert/cmdline_tools.py +547 -0
  14. graxpert/commands.py +200 -0
  15. graxpert/deconvolution.py +176 -0
  16. graxpert/denoising.py +178 -0
  17. graxpert/grid_utils.py +38 -0
  18. graxpert/img/GraXpert_LOGO_Hauptvariante.png +0 -0
  19. graxpert/img/Icon.png +0 -0
  20. graxpert/img/LDN1235_original.jpg +0 -0
  21. graxpert/img/LDN1235_processed.jpg +0 -0
  22. graxpert/img/__init__.py +0 -0
  23. graxpert/img/gfx_numbers.png +0 -0
  24. graxpert/img/gfx_numbers.svg +737 -0
  25. graxpert/img/graXpert_Startbadge_Umbriel.png +0 -0
  26. graxpert/img/hourglass.png +0 -0
  27. graxpert/locales/__init__.py +0 -0
  28. graxpert/locales/de_DE/LC_MESSAGES/base.mo +0 -0
  29. graxpert/localization.py +39 -0
  30. graxpert/main.py +441 -0
  31. graxpert/mp_logging.py +97 -0
  32. graxpert/parallel_processing.py +3 -0
  33. graxpert/preferences.py +139 -0
  34. graxpert/radialbasisinterpolation.py +215 -0
  35. graxpert/resource_utils.py +35 -0
  36. graxpert/s3_secrets.py +16 -0
  37. graxpert/skyall.py +152 -0
  38. graxpert/stretch.py +151 -0
  39. graxpert/theme/__init__.py +0 -0
  40. graxpert/theme/graxpert-dark-blue.json +367 -0
  41. graxpert/ui/__init__.py +1 -0
  42. graxpert/ui/application_frame.py +115 -0
  43. graxpert/ui/canvas.py +558 -0
  44. graxpert/ui/left_menu.py +437 -0
  45. graxpert/ui/loadingframe.py +142 -0
  46. graxpert/ui/right_menu.py +315 -0
  47. graxpert/ui/statusbar.py +102 -0
  48. graxpert/ui/styling.py +23 -0
  49. graxpert/ui/tooltip.py +194 -0
  50. graxpert/ui/ui_events.py +19 -0
  51. graxpert/ui/widgets.py +297 -0
  52. graxpert/ui_scaling.py +13 -0
  53. graxpert/version.py +2 -0
  54. graxpert-3.2.0a0.dev4.dist-info/METADATA +219 -0
  55. graxpert-3.2.0a0.dev4.dist-info/RECORD +58 -0
  56. graxpert-3.2.0a0.dev4.dist-info/WHEEL +5 -0
  57. graxpert-3.2.0a0.dev4.dist-info/entry_points.txt +2 -0
  58. graxpert-3.2.0a0.dev4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,139 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ import shutil
5
+ from dataclasses import asdict, dataclass, field, fields
6
+ from datetime import datetime
7
+ from typing import AnyStr, List
8
+
9
+ import numpy as np
10
+
11
+ from graxpert.app_state import AppState
12
+ from graxpert.version import version as graxpert_version
13
+
14
+
15
+ @dataclass
16
+ class Prefs:
17
+ working_dir: AnyStr = os.getcwd()
18
+ width: int = None
19
+ height: int = None
20
+ background_points: List = field(default_factory=list)
21
+ bg_flood_selection_option: bool = False
22
+ bg_pts_option: int = 15
23
+ stretch_option: AnyStr = "No Stretch"
24
+ saturation: float = 1.0
25
+ channels_linked_option: bool = False
26
+ images_linked_option: bool = False
27
+ display_pts: bool = True
28
+ bg_tol_option: float = 1.0
29
+ interpol_type_option: AnyStr = "RBF"
30
+ smoothing_option: float = 0.0
31
+ saveas_option: AnyStr = "32 bit Tiff"
32
+ saveas_stretched: bool = False
33
+ sample_size: int = 25
34
+ sample_color: int = 55
35
+ RBF_kernel: AnyStr = "thin_plate"
36
+ spline_order: int = 3
37
+ lang: AnyStr = None
38
+ corr_type: AnyStr = "Subtraction"
39
+ scaling: float = 1.0
40
+ bge_ai_version: AnyStr = None
41
+ deconvolution_type_option: AnyStr = "Object-only"
42
+ deconvolution_object_ai_version: AnyStr = None
43
+ deconvolution_stars_ai_version: AnyStr = None
44
+ denoise_ai_version: AnyStr = None
45
+ graxpert_version: AnyStr = graxpert_version
46
+ deconvolution_strength: float = 0.5
47
+ deconvolution_psfsize: float = 5.0
48
+ denoise_strength: float = 0.5
49
+ ai_batch_size: int = 4
50
+ ai_gpu_acceleration: bool = True
51
+
52
+
53
+ def app_state_2_prefs(prefs: Prefs, app_state: AppState) -> Prefs:
54
+ prefs.background_points = [p.tolist() for p in app_state.background_points]
55
+ return prefs
56
+
57
+
58
+ def prefs_2_app_state(prefs: Prefs, app_state: AppState) -> AppState:
59
+ app_state.background_points = [np.array(p) for p in prefs.background_points]
60
+ return app_state
61
+
62
+
63
+ def merge_json(prefs: Prefs, json) -> Prefs:
64
+ for f in fields(prefs):
65
+ if f.name in json:
66
+ setattr(prefs, f.name, json[f.name])
67
+ return prefs
68
+
69
+
70
+ def load_preferences(prefs_filename) -> Prefs:
71
+ prefs = Prefs()
72
+ try:
73
+ if os.path.isfile(prefs_filename):
74
+ with open(prefs_filename) as f:
75
+ json_prefs = json.load(f)
76
+
77
+ if "ai_version" in json_prefs:
78
+ logging.warning(f"Obsolete key 'ai_version' found in {prefs_filename}. Renaming it to 'bge_ai_version.")
79
+ json_prefs = {"bge_ai_version" if k == "ai_version" else k: v for k, v in json_prefs.items()}
80
+
81
+ prefs = merge_json(prefs, json_prefs)
82
+
83
+ if not "graxpert_version" in json_prefs: # reset scaling in case we start from GraXpert < 2.1.0
84
+ prefs.scaling = 1.0
85
+ else:
86
+ logging.info("{} appears to be missing. it will be created after program shutdown".format(prefs_filename))
87
+ except:
88
+ logging.exception("could not load preferences.json from {}".format(prefs_filename))
89
+ if os.path.isfile(prefs_filename):
90
+ # make a backup of the old preferences file so we don't loose it
91
+ backup_filename = os.path.join(os.path.dirname(prefs_filename), datetime.now().strftime("%m-%d-%Y_%H-%M-%S_{}".format(os.path.basename(prefs_filename))))
92
+ shutil.copyfile(prefs_filename, backup_filename)
93
+ return prefs
94
+
95
+
96
+ def save_preferences(prefs_filename, prefs):
97
+ try:
98
+ os.makedirs(os.path.dirname(prefs_filename), exist_ok=True)
99
+ with open(prefs_filename, "w") as f:
100
+ json.dump(asdict(prefs), f)
101
+ except OSError as err:
102
+ logging.exception("error serializing preferences")
103
+
104
+
105
+ def app_state_2_fitsheader(prefs: Prefs, app_state: AppState, fits_header):
106
+ fits_header["INTP-OPT"] = prefs.interpol_type_option
107
+ fits_header["SMOOTH"] = prefs.smoothing_option
108
+ fits_header["CORRTYPE"] = prefs.corr_type
109
+
110
+ if prefs.interpol_type_option == "AI":
111
+ fits_header["BGAI_VER"] = prefs.bge_ai_version
112
+
113
+ if prefs.interpol_type_option != "AI":
114
+ fits_header["SAMPSIZE"] = prefs.sample_size
115
+ fits_header["RBFKRNL"] = prefs.RBF_kernel
116
+ fits_header["SPLNORDR"] = prefs.spline_order
117
+ fits_header["BG-PTS"] = str(list(map(lambda e: e.tolist(), app_state.background_points)))
118
+
119
+ return fits_header
120
+
121
+
122
+ def fitsheader_2_app_state(prefs: Prefs, app_state: AppState, fits_header):
123
+ if "BG-PTS" in fits_header.keys():
124
+ try:
125
+ app_state.background_points = [np.array(p) for p in json.loads(fits_header["BG-PTS"])]
126
+ except:
127
+ logging.warning("Could not transfer background points from fits header to application state", stack_info=True)
128
+
129
+ if "INTP-OPT" in fits_header.keys():
130
+ prefs.interpol_type_option = fits_header["INTP-OPT"]
131
+ prefs.smoothing_option = fits_header["SMOOTH"]
132
+ prefs.corr_type = fits_header["CORRTYPE"]
133
+
134
+ if fits_header["INTP-OPT"] != "AI":
135
+ prefs.sample_size = fits_header["SAMPSIZE"]
136
+ prefs.RBF_kernel = fits_header["RBFKRNL"]
137
+ prefs.spline_order = fits_header["SPLNORDR"]
138
+
139
+ return app_state
@@ -0,0 +1,215 @@
1
+ import sys
2
+ if sys.version_info[0] >2:
3
+ unicode = str
4
+
5
+ import numpy as np
6
+ import scipy.spatial
7
+ from scipy.special import xlogy
8
+ import scipy.linalg
9
+
10
+
11
+ class RadialBasisInterpolation:
12
+ """
13
+ Augmented Radial Basis function interpolation with (optional) polynomial
14
+ augmentation. The construction follows [1] with code borrowed from [2]
15
+ for the kernel (which they call the "function")
16
+
17
+ Inputs
18
+ ------
19
+ X (array like)
20
+ (N,ndim) array or (N,) vector of build points
21
+
22
+ f (array like)
23
+ (N,) vector of function values
24
+
25
+ Options:
26
+ --------
27
+ degree [0]
28
+ Use an augmented RBF. Specify the *total* degree fit if given as an
29
+ integer. If given as a matrix, must be (P+1,ndim) shape. This is the
30
+ degree of the augmenting polynomial.
31
+
32
+ kernel : ['gaussian']
33
+ The radial basis kernel, based on the radius, r
34
+ If string:
35
+ 'multiquadric': sqrt((r/epsilon)**2 + 1)
36
+ 'inverse': 1.0/sqrt((r/epsilon)**2 + 1)
37
+ 'gaussian': exp(-(r/epsilon)**2)
38
+ 'linear': r
39
+ 'cubic': r**3
40
+ 'quintic': r**5
41
+ 'thin_plate': r**2 * log(r)
42
+ If callable
43
+ must take the arguments (r,epsilon) and ignore epsilon if
44
+ not needed. Tip: If epsilon is not needed, set it to
45
+ 0 to avoid trying to optimize it
46
+ If integer:
47
+ Spline of the form: r**kernel if kernel is odd else r**kernel*log(r)
48
+
49
+ epsilon [None]
50
+ Adjustable parameter for some kernels.
51
+
52
+ smooth : float, optional
53
+ Values greater than zero increase the smoothness of the
54
+ approximation. 0 is for interpolation (default). Actually set to
55
+ max(smooth,1e-10 * ||f||) for numerical precision
56
+
57
+ Theory:
58
+ ------
59
+ The RBF is based on a radial distance
60
+
61
+ r_j = ||x - x_j|| (1)
62
+
63
+ with the kernel phi(r) and an optional "Agumenting" polynomial of set
64
+ degree. The interpolant is of the form: [1]
65
+
66
+ s(x) = sum_{j=1}^N c_j * phi(r_j) + sum_{k \in P} g_k * p_k (2)
67
+
68
+ where `P` is a polynomial index and `p_k` is the corresponding
69
+ polynomial (e.g. k = {1,2,0} then p_k = x*y^2*z^0).
70
+
71
+ In order to solve for c_j and g_k we enforce
72
+ (a) s(x_j) = f_j for all j (i.e. exact interpolation)
73
+ (b) sum_{j=1}^N c_j*p_k(x_j) = 0 for all k=1,...,N
74
+
75
+ That results in a matrix system:
76
+ ____________________________________________________ ___ ___
77
+ | | | | | | |
78
+ | phi(||x_i - x_j||) (N,N) | p_k(x_j) (N,len(K)) | | c | | f |
79
+ | | | | | = | | (3)
80
+ |----------------------------|-----------------------| |---| |---|
81
+ | p_k(x_j).T | 0 | | g | | 0 |
82
+ ---------------------------------------------------- --- ---
83
+
84
+ which is then solved. Given c and g, (2) can be used to solve for
85
+ any given x
86
+
87
+ References:
88
+ ----------
89
+ [1] G. B. Wright. Radial Basis Function Interpolation: Numerical and
90
+ Analytical Developments. PhD thesis, University of Colorado, 2003.
91
+
92
+ [2] SciPy version 1.1.0
93
+ https://github.com/scipy/scipy/blob/v1.1.0/scipy/interpolate/rbf.py
94
+
95
+ [3] S. Rippa. An algorithm for selecting a good value for the parameter
96
+ c in radial basis function interpolation. Advances in Computational
97
+ Mathematics, 11(2-3):193–210, 1999.
98
+
99
+ [4] J. D. Martin and T. W. Simpson. Use of Kriging Models to Approximate
100
+ Deterministic Computer Models. AIAA journal, 43(4):853–863, 2005.
101
+
102
+ """
103
+ def __init__(self,X,f,degree=0,
104
+ epsilon=1,smooth=0,
105
+ kernel='gaussian',_solve=True):
106
+ self.X = X = np.atleast_2d(X)
107
+ self.N,self.ndim = X.shape
108
+ self.f = f = np.ravel(f)
109
+ self.degree = degree
110
+ self.kernel = kernel
111
+ self.epsilon = epsilon
112
+
113
+
114
+ r = scipy.spatial.distance.cdist(X,X)
115
+
116
+ self.smooth = max(float(smooth),1e-10)
117
+
118
+ K = self._kernel(r) + np.eye(self.N)*self.smooth*np.mean(self._kernel(r))
119
+ P = RadialBasisInterpolation.vandermond(X,degree=self.degree)
120
+
121
+ # Build the matrix
122
+ Z = np.zeros([P.shape[1]]*2)
123
+ z = np.zeros(P.shape[1])
124
+ KP = np.block([[K , P],
125
+ [P.T , Z]])
126
+ b = np.hstack([f,z])
127
+ coef = scipy.linalg.solve(KP,b)
128
+ self.rbf_coef = coef[:self.N]
129
+ self.poly_coef = coef[self.N:]
130
+
131
+
132
+ def __call__(self,X):
133
+ X = np.atleast_2d(X)
134
+ step_size = int(10*X.shape[0]/self.X.size + 1)
135
+ i = 0
136
+
137
+ result = np.empty(X.shape[0])
138
+
139
+ while(i < X.shape[0]):
140
+ start = i
141
+ end = np.minimum(i+step_size,X.shape[0])
142
+ K = self._kernel( scipy.spatial.distance.cdist(X[start:end],self.X) )
143
+ P = RadialBasisInterpolation.vandermond(X[start:end],degree=self.degree)
144
+
145
+ result[start:end] = K.dot(self.rbf_coef) + P.dot(self.poly_coef)
146
+ i = i + step_size
147
+
148
+ return result
149
+
150
+ def _kernel(self,r):
151
+ r = np.asarray(r)
152
+ if callable(self.kernel):
153
+ return self.kernel(r,self.epsilon)
154
+ elif isinstance(self.kernel,(int,np.integer)):
155
+ if np.mod(self.kernel,2) == 0:
156
+ return xlogy(r**self.kernel,r)
157
+ else:
158
+ return r**self.kernel
159
+ elif not isinstance(self.kernel,(str,unicode)):
160
+ raise ValueError('Kernel must be a callable with signature (r,epsilon), an integer (spline) or a valid string')
161
+
162
+ kernel = self.kernel.lower().replace(' ','_').replace('-','_')
163
+
164
+ if kernel in ['multiquadric']:
165
+ return np.sqrt((r/self.epsilon)**2 + 1)
166
+ elif kernel in ['inverse_multiquadric','inverse']:
167
+ return 1.0/np.sqrt((1.0/self.epsilon*r)**2 + 1)
168
+ elif kernel in ['gaussian','squared_exponential']:
169
+ return np.exp(-(1.0/self.epsilon*r)**2)
170
+ elif kernel in ['linear']:
171
+ return r
172
+ elif kernel in ['cubic']:
173
+ return r**3
174
+ elif kernel in ['quintic']:
175
+ return r**5
176
+ elif kernel in ['thin_plate']:
177
+ return xlogy(r**2, r)
178
+ else:
179
+ raise ValueError('Not valid kernel name')
180
+
181
+ @staticmethod
182
+ def vandermond(X,degree):
183
+ """
184
+ Return a Vandermond matrix of X up to the
185
+ *total* order degree
186
+ """
187
+ X = np.atleast_2d(X)
188
+ ndim = X.shape[1]
189
+ index = np.asarray(RadialBasisInterpolation.total_index(degree,ndim),dtype=int)
190
+ V = np.ones([X.shape[0],len(index)])
191
+ for d in range(ndim):
192
+ v = np.fliplr(np.vander(X[:,d],N=degree+1))
193
+ V *= v[:,index[:,d]]
194
+ return V
195
+
196
+
197
+ @staticmethod
198
+ def total_index(P,ndim,sort=True):
199
+ P = tuple([P]*ndim)
200
+ curr = [0]*ndim
201
+ ind = []
202
+ ind.append(tuple(curr))
203
+ while True:
204
+ for d in range(ndim):
205
+ if sum(curr) < P[0]:
206
+ curr[d] += 1
207
+ break
208
+ else:
209
+ curr[d] = 0
210
+ else:
211
+ break
212
+ ind.append(tuple(curr))
213
+ if sort:
214
+ ind.sort(key=lambda a:(sum(a),(np.array(a)**2).sum(),a[::-1]))
215
+ return ind
@@ -0,0 +1,35 @@
1
+ # from appdirs import
2
+ import os
3
+
4
+ from tempfile import TemporaryDirectory
5
+ from importlib import resources
6
+
7
+ temp_resource_dir = TemporaryDirectory()
8
+
9
+ def temp_cleanup():
10
+ temp_resource_dir.cleanup()
11
+
12
+ def resource_path(relative_path):
13
+ base_path = os.path.join(os.path.dirname(os.path.dirname(__file__)))
14
+ return os.path.join(base_path, relative_path)
15
+
16
+
17
+ def temp_resource_path(relative_path):
18
+ return os.path.join(temp_resource_dir.name, relative_path)
19
+
20
+
21
+ def resource_bytestream(name, dir = 'img'):
22
+ return resources.files(f'graxpert.{dir}').joinpath(name).open('rb')
23
+
24
+
25
+ def resource_image(name):
26
+ from PIL import Image
27
+ with resources.as_file(resources.files('graxpert.img').joinpath(name)) as file_path:
28
+ return Image.open(file_path)
29
+
30
+
31
+ # Unfortunately tk.PhotoImage doesn't accept file-like objects, so we need to use a temporary file
32
+ def resource_photoimage(resource_name):
33
+ import tkinter as tk
34
+ with resources.as_file(resources.files('graxpert.img').joinpath(resource_name)) as file_path:
35
+ return tk.PhotoImage(file=file_path)
graxpert/s3_secrets.py ADDED
@@ -0,0 +1,16 @@
1
+ # Decompiled with PyLingual (https://pylingual.io)
2
+ # Internal filename: /home/runner/work/GraXpert/GraXpert/graxpert/s3_secrets.py
3
+ # Bytecode version: 3.10.0rc2 (3439)
4
+ # Source timestamp: 2024-05-03 17:20:08 UTC (1714756808)
5
+
6
+ endpoint = 'minio.schmelly.de'
7
+ ro_access_key = 'IynFklnoqVLigqvqkBR5'
8
+ ro_secret_key = '1250a0FYS2WlUF78aZk0yoeT5daJQkRsNiIjLlhQ'
9
+ bucket_name = 'graxpert-ai'
10
+ bge_bucket_name = 'graxpert-bge-ai-onnx'
11
+ denoise_bucket_name = 'graxpert-denoise-ai-onnx'
12
+
13
+ deconvolution_object_bucket_name = 'graxpert-deconv-object'
14
+ deconvolution_stars_bucket_name = 'graxpert-deconvolution-stars'
15
+ deconvolution_object_bucket_name = "graxpert-deconvolution-object-ai-onnx"
16
+ deconvolution_stars_bucket_name = "graxpert-deconvolution-stars-ai-onnx"
graxpert/skyall.py ADDED
@@ -0,0 +1,152 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ """
6
+ Find the mode of a distribution using a route based on the SKYALL as described in
7
+ https://articles.adsabs.harvard.edu//full/1993MNRAS.265..641A/0000643.000.html
8
+ """
9
+
10
+ def mode(distribution):
11
+
12
+ # Find appropriate intensity range
13
+
14
+ clip = np.logical_and(distribution > 0.0, distribution < 1.0)
15
+
16
+ median = np.median(distribution[clip])
17
+
18
+ left_of_median = np.where(distribution < median)
19
+ right_of_median = np.where(distribution > median)
20
+
21
+ rms_left = np.sqrt(np.sum((distribution[left_of_median] - median)**2)/distribution.size)
22
+ rms_right= np.sqrt(np.sum((distribution[right_of_median] - median)**2)/distribution.size)
23
+
24
+ rms = np.min([rms_left,rms_right])
25
+
26
+ lower_bound = np.max([0.0, median - 2*rms])
27
+ upper_bound = np.min([1.0, median + 2*rms])
28
+
29
+
30
+ # Set initial number of bins
31
+
32
+ num_bins = 8
33
+ histo, bin_edges = np.histogram(distribution, bins=num_bins, range=(lower_bound,upper_bound), density=True)
34
+
35
+ found = False
36
+ iterations = 0
37
+
38
+ while(not found and iterations <= 20):
39
+
40
+ max_bin = np.argmax(histo)
41
+ max_value = histo[max_bin]
42
+
43
+ left_pointer = max_bin
44
+ right_pointer = max_bin
45
+
46
+ while(histo[left_pointer] > max_value/1.75):
47
+ left_pointer = left_pointer - 1
48
+ if(left_pointer < 0):
49
+ left_pointer = 0
50
+ break
51
+
52
+ while(histo[right_pointer] > max_value/1.75):
53
+ right_pointer = right_pointer + 1
54
+ if(right_pointer >= bin_edges.size - 1):
55
+ right_pointer = bin_edges.size - 2
56
+ break
57
+
58
+ if(right_pointer - left_pointer > 5):
59
+ found = True
60
+
61
+ else:
62
+ found = False
63
+ num_bins = int(num_bins * 1.5)
64
+ histo, bin_edges = np.histogram(distribution, bins=num_bins, range=(lower_bound,upper_bound), density=True)
65
+
66
+ iterations = iterations + 1
67
+
68
+ if(iterations > 20):
69
+ logging.debug("More than 20 iterations in second step of SKYALL. Return median instead.")
70
+ return median
71
+
72
+ #print(bin_edges[left_pointer])
73
+ #print(bin_edges[right_pointer])
74
+ lower_bound = (bin_edges[left_pointer] + bin_edges[left_pointer+1])/2
75
+ upper_bound = (bin_edges[right_pointer] + bin_edges[right_pointer+1])/2
76
+ histo, bin_edges = np.histogram(distribution, bins=num_bins, range=(lower_bound,upper_bound), density=True)
77
+
78
+ # Find best fit
79
+
80
+ best_coeff = np.array([0,0,0])
81
+ best_err = -1
82
+
83
+ iterations = 0
84
+
85
+ while(iterations < 20):
86
+
87
+ x = averaged_histo(distribution, bin_edges)
88
+ coeff, err, misc1, misc2, misc3 = np.polyfit(x, histo, deg=2, full=True)
89
+ err = err/x.size
90
+
91
+ if(err < best_err or best_err == -1):
92
+ best_err = err
93
+ best_coeff = coeff
94
+
95
+ if(err > 1.3*best_err and best_err != -1):
96
+ break
97
+
98
+ iterations = iterations + 1
99
+
100
+ if(iterations >= 20):
101
+
102
+ logging.debug("More than 20 iterations in third step of SKYAL. Return median instead.")
103
+ return median
104
+
105
+ num_bins = int(num_bins*1.5)
106
+ histo, bin_edges = np.histogram(distribution, bins=num_bins, range=(lower_bound,upper_bound), density=True)
107
+
108
+
109
+
110
+ mode = -best_coeff[1]/2/best_coeff[0]
111
+
112
+ # Testing
113
+ #plt.hist(distribution.ravel(),50,range=(0.1,0.5), density=True)
114
+ #plt.hist(distribution.ravel(),10,range=(bin_edges[0],bin_edges[-1]), density=True)
115
+ #plt.plot(np.arange(bin_edges[0],bin_edges[-1],0.01), f(np.arange(bin_edges[0],bin_edges[-1],0.01),best_coeff), color="g")
116
+ #plt.axvline(np.mean(distribution), color='r', label="Mean")
117
+ #plt.axvline(np.median(distribution), color='k', label="Median")
118
+ #plt.axvline(mode, color='g', label="Mode")
119
+ #plt.legend()
120
+ #plt.savefig("vergleich.jpg")
121
+ return mode
122
+
123
+
124
+
125
+
126
+ def averaged_histo(distribution, bin_edges):
127
+
128
+ x = np.zeros(bin_edges.shape[0]-1)
129
+
130
+ for i in range(x.shape[0]):
131
+ idx = np.logical_and(distribution >= bin_edges[i], distribution < bin_edges[i+1])
132
+
133
+ if(distribution[idx].shape[0] == 0):
134
+ x[i] = (bin_edges[i] + bin_edges[i+1])/2.0
135
+ else:
136
+ x[i] = np.mean(distribution[idx])
137
+
138
+ return x
139
+
140
+ def f(x, coeff):
141
+ return coeff[0]*x**2 + coeff[1]*x + coeff[2]
142
+
143
+
144
+
145
+ # Testing
146
+ # from skimage import img_as_float32, io
147
+ #y=3100
148
+ #x=483
149
+ #size=25
150
+ #im = img_as_float32(io.imread("../../milchstr.tif")[x-size:x+size,y-size:y+size,0])
151
+
152
+ #mode(im)