setiastrosuitepro 1.7.5.post1__py3-none-any.whl → 1.8.0.post3__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.

Potentially problematic release.


This version of setiastrosuitepro might be problematic. Click here for more details.

Files changed (27) hide show
  1. setiastro/saspro/_generated/build_info.py +2 -2
  2. setiastro/saspro/accel_installer.py +21 -8
  3. setiastro/saspro/accel_workers.py +11 -12
  4. setiastro/saspro/comet_stacking.py +113 -85
  5. setiastro/saspro/cosmicclarity.py +604 -826
  6. setiastro/saspro/cosmicclarity_engines/benchmark_engine.py +732 -0
  7. setiastro/saspro/cosmicclarity_engines/darkstar_engine.py +576 -0
  8. setiastro/saspro/cosmicclarity_engines/denoise_engine.py +567 -0
  9. setiastro/saspro/cosmicclarity_engines/satellite_engine.py +620 -0
  10. setiastro/saspro/cosmicclarity_engines/sharpen_engine.py +587 -0
  11. setiastro/saspro/cosmicclarity_engines/superres_engine.py +412 -0
  12. setiastro/saspro/gui/main_window.py +14 -0
  13. setiastro/saspro/gui/mixins/menu_mixin.py +2 -0
  14. setiastro/saspro/model_manager.py +324 -0
  15. setiastro/saspro/model_workers.py +102 -0
  16. setiastro/saspro/ops/benchmark.py +320 -0
  17. setiastro/saspro/ops/settings.py +407 -10
  18. setiastro/saspro/remove_stars.py +424 -442
  19. setiastro/saspro/resources.py +73 -10
  20. setiastro/saspro/runtime_torch.py +107 -22
  21. setiastro/saspro/signature_insert.py +14 -3
  22. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/METADATA +2 -1
  23. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/RECORD +27 -18
  24. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/WHEEL +0 -0
  25. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/entry_points.txt +0 -0
  26. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/licenses/LICENSE +0 -0
  27. {setiastrosuitepro-1.7.5.post1.dist-info → setiastrosuitepro-1.8.0.post3.dist-info}/licenses/license.txt +0 -0
@@ -0,0 +1,320 @@
1
+ # src/setiastro/saspro/ops/benchmark.py
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from PyQt6.QtCore import QThread, pyqtSignal, QObject
6
+ from PyQt6.QtWidgets import (
7
+ QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox,
8
+ QProgressBar, QTextEdit, QMessageBox, QApplication
9
+ )
10
+
11
+ from setiastro.saspro.cosmicclarity_engines.benchmark_engine import (
12
+ benchmark_image_path, download_benchmark_image, run_benchmark, # keep your run_benchmark
13
+ )
14
+ from setiastro.saspro.cosmicclarity_engines.benchmark_engine import BENCHMARK_FITS_URL # or define here
15
+
16
+ class _BenchWorker(QObject):
17
+ log = pyqtSignal(str)
18
+ prog = pyqtSignal(int, int) # done,total
19
+ done = pyqtSignal(bool, dict, str) # ok, results, err
20
+
21
+ def __init__(self, mode: str, use_gpu: bool):
22
+ super().__init__()
23
+ self.mode = mode
24
+ self.use_gpu = use_gpu
25
+
26
+ def run(self):
27
+ try:
28
+ def status_cb(s: str):
29
+ self.log.emit(str(s))
30
+
31
+ def progress_cb(done: int, total: int) -> bool:
32
+ self.prog.emit(int(done), int(total))
33
+ return not QThread.currentThread().isInterruptionRequested()
34
+
35
+ results = run_benchmark(
36
+ mode=self.mode,
37
+ use_gpu=self.use_gpu,
38
+ status_cb=status_cb,
39
+ progress_cb=progress_cb,
40
+ )
41
+ self.done.emit(True, results, "")
42
+ except Exception as e:
43
+ msg = str(e)
44
+ if "Canceled" in msg or "cancel" in msg.lower():
45
+ self.done.emit(False, {}, "Canceled.")
46
+ else:
47
+ self.done.emit(False, {}, msg)
48
+
49
+
50
+ class _DownloadWorker(QObject):
51
+ log = pyqtSignal(str)
52
+ prog = pyqtSignal(int, int) # bytes_done, bytes_total
53
+ done = pyqtSignal(bool, str) # ok, message/path
54
+
55
+ def __init__(self, url: str):
56
+ super().__init__()
57
+ self.url = url
58
+
59
+ def run(self):
60
+ try:
61
+ def status_cb(s: str):
62
+ self.log.emit(str(s))
63
+
64
+ def progress_cb(done: int, total: int):
65
+ self.prog.emit(int(done), int(total))
66
+
67
+ def cancel_cb() -> bool:
68
+ return QThread.currentThread().isInterruptionRequested()
69
+
70
+ p = download_benchmark_image(
71
+ None,
72
+ status_cb=status_cb,
73
+ progress_cb=progress_cb,
74
+ cancel_cb=cancel_cb,
75
+ )
76
+ self.done.emit(True, str(p))
77
+ except Exception as e:
78
+ self.done.emit(False, str(e))
79
+
80
+
81
+ class BenchmarkDialog(QDialog):
82
+ def __init__(self, parent=None):
83
+ super().__init__(parent)
84
+ self.setWindowTitle("Seti Astro Benchmark")
85
+ self.setModal(False)
86
+ self.setMinimumSize(560, 520)
87
+
88
+ self._results = None
89
+ self._thread = None
90
+
91
+ outer = QVBoxLayout(self)
92
+ outer.setContentsMargins(10, 10, 10, 10)
93
+ outer.setSpacing(8)
94
+
95
+ # Top row: image status + download
96
+ top = QHBoxLayout()
97
+ self.lbl_img = QLabel(self)
98
+ self.btn_dl = QPushButton("Download Benchmark Image…", self)
99
+ self.btn_dl.clicked.connect(self._download_image)
100
+ top.addWidget(self.lbl_img, 1)
101
+ top.addWidget(self.btn_dl)
102
+ outer.addLayout(top)
103
+
104
+ # Mode row
105
+ row = QHBoxLayout()
106
+ row.addWidget(QLabel("Run:", self))
107
+ self.cmb = QComboBox(self)
108
+ self.cmb.addItems(["CPU", "GPU", "Both"])
109
+ self.cmb.setCurrentText("Both")
110
+ row.addWidget(self.cmb)
111
+
112
+ self.btn_run = QPushButton("Run Benchmark", self)
113
+ self.btn_run.clicked.connect(self._run_benchmark)
114
+ row.addWidget(self.btn_run)
115
+ row.addStretch(1)
116
+ outer.addLayout(row)
117
+
118
+ # Progress
119
+ self.pbar = QProgressBar(self)
120
+ self.pbar.setRange(0, 100)
121
+ outer.addWidget(self.pbar)
122
+
123
+ # Log / results
124
+ self.txt = QTextEdit(self)
125
+ self.txt.setReadOnly(True)
126
+ outer.addWidget(self.txt, 1)
127
+
128
+ # Bottom buttons
129
+ bot = QHBoxLayout()
130
+ self.btn_copy = QPushButton("Copy JSON", self)
131
+ self.btn_copy.setEnabled(False)
132
+ self.btn_copy.clicked.connect(self._copy_json)
133
+
134
+ self.btn_save = QPushButton("Save Locally", self)
135
+ self.btn_save.setEnabled(False)
136
+ self.btn_save.clicked.connect(self._save_local)
137
+
138
+ self.btn_submit = QPushButton("Submit…", self)
139
+ self.btn_submit.clicked.connect(self._submit)
140
+
141
+ self.btn_close = QPushButton("Close", self)
142
+ self.btn_close.clicked.connect(self.close)
143
+
144
+ bot.addWidget(self.btn_copy)
145
+ bot.addWidget(self.btn_save)
146
+ bot.addStretch(1)
147
+ bot.addWidget(self.btn_submit)
148
+ bot.addWidget(self.btn_close)
149
+ outer.addLayout(bot)
150
+
151
+ self.refresh_ui()
152
+
153
+ def refresh_ui(self):
154
+ p = benchmark_image_path()
155
+ if p.exists():
156
+ self.lbl_img.setText(f"Benchmark image: Ready ({p.name})")
157
+ self.btn_run.setEnabled(True)
158
+ else:
159
+ self.lbl_img.setText("Benchmark image: Not downloaded")
160
+ self.btn_run.setEnabled(False)
161
+
162
+ self.pbar.setValue(0)
163
+
164
+ # ---------- download ----------
165
+ def _download_image(self):
166
+ self._stop_thread_if_any()
167
+
168
+ self.txt.append("Starting download…")
169
+ self.btn_dl.setEnabled(False)
170
+ self.btn_run.setEnabled(False)
171
+ self.pbar.setValue(0)
172
+
173
+ t = QThread(self)
174
+ w = _DownloadWorker(BENCHMARK_FITS_URL)
175
+ w.moveToThread(t)
176
+
177
+ w.log.connect(self._log)
178
+ w.prog.connect(self._dl_progress)
179
+ w.done.connect(lambda ok, msg: self._dl_done(ok, msg, t, w))
180
+ t.started.connect(w.run)
181
+ t.start()
182
+ self._thread = t
183
+
184
+ def _dl_progress(self, done: int, total: int):
185
+ if total > 0:
186
+ pct = int(done * 100 / total)
187
+ self.pbar.setRange(0, 100)
188
+ self.pbar.setValue(max(0, min(100, pct)))
189
+
190
+ # 👇 add this
191
+ self.pbar.setFormat(f"{pct}% ({done/1e6:.0f}/{total/1e6:.0f} MB)")
192
+ else:
193
+ # unknown length
194
+ self.pbar.setRange(0, 0)
195
+ self.pbar.setFormat(f"{done/1e6:.0f} MB")
196
+
197
+ def _dl_done(self, ok: bool, msg: str, t: QThread, w: QObject):
198
+ t.quit(); t.wait()
199
+ self._thread = None
200
+
201
+ self.pbar.setRange(0, 100)
202
+ self.pbar.setFormat("%p%")
203
+ self.btn_dl.setEnabled(True)
204
+
205
+ if ok:
206
+ self._log(f"✅ Downloaded: {msg}")
207
+ self.refresh_ui()
208
+ else:
209
+ self._log(f"❌ Download failed: {msg}")
210
+ QMessageBox.warning(self, "Download failed", msg)
211
+ self.refresh_ui()
212
+
213
+ def closeEvent(self, e):
214
+ self._stop_thread_if_any()
215
+ self.pbar.setRange(0, 100)
216
+ super().closeEvent(e)
217
+
218
+
219
+ # ---------- run benchmark ----------
220
+ def _run_benchmark(self):
221
+ p = benchmark_image_path()
222
+ if not p.exists():
223
+ QMessageBox.information(self, "Benchmark image missing", "Please download the benchmark image first.")
224
+ return
225
+
226
+ self._stop_thread_if_any()
227
+ self.btn_run.setEnabled(False)
228
+ self.btn_dl.setEnabled(False)
229
+ self._results = None
230
+ self.btn_copy.setEnabled(False)
231
+ self.btn_save.setEnabled(False)
232
+
233
+ self.txt.clear()
234
+ self._log("Running benchmark…")
235
+ self.pbar.setValue(0)
236
+
237
+ mode = self.cmb.currentText()
238
+ use_gpu = True # benchmark engine will pick CPU if no CUDA/DML
239
+
240
+ t = QThread(self)
241
+ w = _BenchWorker(mode=mode, use_gpu=use_gpu)
242
+ w.moveToThread(t)
243
+
244
+ w.log.connect(self._log)
245
+ w.prog.connect(self._bench_progress)
246
+ w.done.connect(lambda ok, results, err: self._bench_done(ok, results, err, t, w))
247
+ t.started.connect(w.run)
248
+ t.start()
249
+ self._thread = t
250
+
251
+ def _bench_progress(self, done: int, total: int):
252
+ if total > 0:
253
+ self.pbar.setValue(int(done * 100 / total))
254
+ QApplication.processEvents()
255
+
256
+ def _bench_done(self, ok: bool, results: dict, err: str, t: QThread, w: QObject):
257
+ t.quit(); t.wait()
258
+ self._thread = None
259
+ self.pbar.setValue(100 if ok else 0)
260
+ self.btn_run.setEnabled(True)
261
+ self.btn_dl.setEnabled(True)
262
+ if not ok:
263
+ self._log(f"❌ Benchmark failed: {err}")
264
+ if str(err).strip().lower().startswith("canceled"):
265
+ # no scary dialog for user-cancel
266
+ self.pbar.setValue(0)
267
+ return
268
+ QMessageBox.warning(self, "Benchmark failed", err)
269
+ return
270
+
271
+ self._results = results
272
+ self._log("✅ Benchmark complete.\n")
273
+ self._log(json.dumps([results], indent=2))
274
+
275
+ self.btn_copy.setEnabled(True)
276
+ self.btn_save.setEnabled(True)
277
+
278
+ # ---------- actions ----------
279
+ def _copy_json(self):
280
+ if not self._results:
281
+ return
282
+ s = json.dumps([self._results], indent=4)
283
+ QApplication.clipboard().setText(s)
284
+ QMessageBox.information(self, "Copied", "Benchmark JSON copied to clipboard.")
285
+
286
+ def _save_local(self):
287
+ if not self._results:
288
+ return
289
+ # reuse your existing helper if you want; simplest local save:
290
+ import os, time
291
+ fn = "benchmark_results.json"
292
+ try:
293
+ if os.path.exists(fn):
294
+ with open(fn, "r", encoding="utf-8") as f:
295
+ try:
296
+ allr = json.load(f)
297
+ except Exception:
298
+ allr = []
299
+ else:
300
+ allr = []
301
+ allr.append(self._results)
302
+ with open(fn, "w", encoding="utf-8") as f:
303
+ json.dump(allr, f, indent=4)
304
+ self._log(f"\n✅ Saved to {fn}")
305
+ except Exception as e:
306
+ QMessageBox.warning(self, "Save failed", str(e))
307
+
308
+ def _submit(self):
309
+ import webbrowser
310
+ webbrowser.open("https://setiastro.com/benchmark-submit")
311
+
312
+ def _log(self, s: str):
313
+ self.txt.append(str(s))
314
+
315
+ def _stop_thread_if_any(self):
316
+ if self._thread is not None and self._thread.isRunning():
317
+ self._thread.requestInterruption()
318
+ self._thread.quit()
319
+ self._thread.wait()
320
+ self._thread = None