setiastrosuitepro 1.6.5.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.
Files changed (368) hide show
  1. setiastro/__init__.py +2 -0
  2. setiastro/data/SASP_data.fits +0 -0
  3. setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
  4. setiastro/data/catalogs/astrobin_filters.csv +890 -0
  5. setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
  6. setiastro/data/catalogs/cali2.csv +63 -0
  7. setiastro/data/catalogs/cali2color.csv +65 -0
  8. setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
  9. setiastro/data/catalogs/celestial_catalog.csv +24031 -0
  10. setiastro/data/catalogs/detected_stars.csv +24784 -0
  11. setiastro/data/catalogs/fits_header_data.csv +46 -0
  12. setiastro/data/catalogs/test.csv +8 -0
  13. setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
  14. setiastro/images/Astro_Spikes.png +0 -0
  15. setiastro/images/Background_startup.jpg +0 -0
  16. setiastro/images/HRDiagram.png +0 -0
  17. setiastro/images/LExtract.png +0 -0
  18. setiastro/images/LInsert.png +0 -0
  19. setiastro/images/Oxygenation-atm-2.svg.png +0 -0
  20. setiastro/images/RGB080604.png +0 -0
  21. setiastro/images/abeicon.png +0 -0
  22. setiastro/images/aberration.png +0 -0
  23. setiastro/images/andromedatry.png +0 -0
  24. setiastro/images/andromedatry_satellited.png +0 -0
  25. setiastro/images/annotated.png +0 -0
  26. setiastro/images/aperture.png +0 -0
  27. setiastro/images/astrosuite.ico +0 -0
  28. setiastro/images/astrosuite.png +0 -0
  29. setiastro/images/astrosuitepro.icns +0 -0
  30. setiastro/images/astrosuitepro.ico +0 -0
  31. setiastro/images/astrosuitepro.png +0 -0
  32. setiastro/images/background.png +0 -0
  33. setiastro/images/background2.png +0 -0
  34. setiastro/images/benchmark.png +0 -0
  35. setiastro/images/big_moon_stabilizer_timeline.png +0 -0
  36. setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
  37. setiastro/images/blaster.png +0 -0
  38. setiastro/images/blink.png +0 -0
  39. setiastro/images/clahe.png +0 -0
  40. setiastro/images/collage.png +0 -0
  41. setiastro/images/colorwheel.png +0 -0
  42. setiastro/images/contsub.png +0 -0
  43. setiastro/images/convo.png +0 -0
  44. setiastro/images/copyslot.png +0 -0
  45. setiastro/images/cosmic.png +0 -0
  46. setiastro/images/cosmicsat.png +0 -0
  47. setiastro/images/crop1.png +0 -0
  48. setiastro/images/cropicon.png +0 -0
  49. setiastro/images/curves.png +0 -0
  50. setiastro/images/cvs.png +0 -0
  51. setiastro/images/debayer.png +0 -0
  52. setiastro/images/denoise_cnn_custom.png +0 -0
  53. setiastro/images/denoise_cnn_graph.png +0 -0
  54. setiastro/images/disk.png +0 -0
  55. setiastro/images/dse.png +0 -0
  56. setiastro/images/exoicon.png +0 -0
  57. setiastro/images/eye.png +0 -0
  58. setiastro/images/fliphorizontal.png +0 -0
  59. setiastro/images/flipvertical.png +0 -0
  60. setiastro/images/font.png +0 -0
  61. setiastro/images/freqsep.png +0 -0
  62. setiastro/images/functionbundle.png +0 -0
  63. setiastro/images/graxpert.png +0 -0
  64. setiastro/images/green.png +0 -0
  65. setiastro/images/gridicon.png +0 -0
  66. setiastro/images/halo.png +0 -0
  67. setiastro/images/hdr.png +0 -0
  68. setiastro/images/histogram.png +0 -0
  69. setiastro/images/hubble.png +0 -0
  70. setiastro/images/imagecombine.png +0 -0
  71. setiastro/images/invert.png +0 -0
  72. setiastro/images/isophote.png +0 -0
  73. setiastro/images/isophote_demo_figure.png +0 -0
  74. setiastro/images/isophote_demo_image.png +0 -0
  75. setiastro/images/isophote_demo_model.png +0 -0
  76. setiastro/images/isophote_demo_residual.png +0 -0
  77. setiastro/images/jwstpupil.png +0 -0
  78. setiastro/images/linearfit.png +0 -0
  79. setiastro/images/livestacking.png +0 -0
  80. setiastro/images/mask.png +0 -0
  81. setiastro/images/maskapply.png +0 -0
  82. setiastro/images/maskcreate.png +0 -0
  83. setiastro/images/maskremove.png +0 -0
  84. setiastro/images/morpho.png +0 -0
  85. setiastro/images/mosaic.png +0 -0
  86. setiastro/images/multiscale_decomp.png +0 -0
  87. setiastro/images/nbtorgb.png +0 -0
  88. setiastro/images/neutral.png +0 -0
  89. setiastro/images/nuke.png +0 -0
  90. setiastro/images/openfile.png +0 -0
  91. setiastro/images/pedestal.png +0 -0
  92. setiastro/images/pen.png +0 -0
  93. setiastro/images/pixelmath.png +0 -0
  94. setiastro/images/platesolve.png +0 -0
  95. setiastro/images/ppp.png +0 -0
  96. setiastro/images/pro.png +0 -0
  97. setiastro/images/project.png +0 -0
  98. setiastro/images/psf.png +0 -0
  99. setiastro/images/redo.png +0 -0
  100. setiastro/images/redoicon.png +0 -0
  101. setiastro/images/rescale.png +0 -0
  102. setiastro/images/rgbalign.png +0 -0
  103. setiastro/images/rgbcombo.png +0 -0
  104. setiastro/images/rgbextract.png +0 -0
  105. setiastro/images/rotate180.png +0 -0
  106. setiastro/images/rotatearbitrary.png +0 -0
  107. setiastro/images/rotateclockwise.png +0 -0
  108. setiastro/images/rotatecounterclockwise.png +0 -0
  109. setiastro/images/satellite.png +0 -0
  110. setiastro/images/script.png +0 -0
  111. setiastro/images/selectivecolor.png +0 -0
  112. setiastro/images/simbad.png +0 -0
  113. setiastro/images/slot0.png +0 -0
  114. setiastro/images/slot1.png +0 -0
  115. setiastro/images/slot2.png +0 -0
  116. setiastro/images/slot3.png +0 -0
  117. setiastro/images/slot4.png +0 -0
  118. setiastro/images/slot5.png +0 -0
  119. setiastro/images/slot6.png +0 -0
  120. setiastro/images/slot7.png +0 -0
  121. setiastro/images/slot8.png +0 -0
  122. setiastro/images/slot9.png +0 -0
  123. setiastro/images/spcc.png +0 -0
  124. setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
  125. setiastro/images/spinner.gif +0 -0
  126. setiastro/images/stacking.png +0 -0
  127. setiastro/images/staradd.png +0 -0
  128. setiastro/images/staralign.png +0 -0
  129. setiastro/images/starnet.png +0 -0
  130. setiastro/images/starregistration.png +0 -0
  131. setiastro/images/starspike.png +0 -0
  132. setiastro/images/starstretch.png +0 -0
  133. setiastro/images/statstretch.png +0 -0
  134. setiastro/images/supernova.png +0 -0
  135. setiastro/images/uhs.png +0 -0
  136. setiastro/images/undoicon.png +0 -0
  137. setiastro/images/upscale.png +0 -0
  138. setiastro/images/viewbundle.png +0 -0
  139. setiastro/images/whitebalance.png +0 -0
  140. setiastro/images/wimi_icon_256x256.png +0 -0
  141. setiastro/images/wimilogo.png +0 -0
  142. setiastro/images/wims.png +0 -0
  143. setiastro/images/wrench_icon.png +0 -0
  144. setiastro/images/xisfliberator.png +0 -0
  145. setiastro/qml/ResourceMonitor.qml +126 -0
  146. setiastro/saspro/__init__.py +20 -0
  147. setiastro/saspro/__main__.py +958 -0
  148. setiastro/saspro/_generated/__init__.py +7 -0
  149. setiastro/saspro/_generated/build_info.py +3 -0
  150. setiastro/saspro/abe.py +1346 -0
  151. setiastro/saspro/abe_preset.py +196 -0
  152. setiastro/saspro/aberration_ai.py +698 -0
  153. setiastro/saspro/aberration_ai_preset.py +224 -0
  154. setiastro/saspro/accel_installer.py +218 -0
  155. setiastro/saspro/accel_workers.py +30 -0
  156. setiastro/saspro/add_stars.py +624 -0
  157. setiastro/saspro/astrobin_exporter.py +1010 -0
  158. setiastro/saspro/astrospike.py +153 -0
  159. setiastro/saspro/astrospike_python.py +1841 -0
  160. setiastro/saspro/autostretch.py +198 -0
  161. setiastro/saspro/backgroundneutral.py +611 -0
  162. setiastro/saspro/batch_convert.py +328 -0
  163. setiastro/saspro/batch_renamer.py +522 -0
  164. setiastro/saspro/blemish_blaster.py +491 -0
  165. setiastro/saspro/blink_comparator_pro.py +3149 -0
  166. setiastro/saspro/bundles.py +61 -0
  167. setiastro/saspro/bundles_dock.py +114 -0
  168. setiastro/saspro/cheat_sheet.py +213 -0
  169. setiastro/saspro/clahe.py +368 -0
  170. setiastro/saspro/comet_stacking.py +1442 -0
  171. setiastro/saspro/common_tr.py +107 -0
  172. setiastro/saspro/config.py +38 -0
  173. setiastro/saspro/config_bootstrap.py +40 -0
  174. setiastro/saspro/config_manager.py +316 -0
  175. setiastro/saspro/continuum_subtract.py +1617 -0
  176. setiastro/saspro/convo.py +1400 -0
  177. setiastro/saspro/convo_preset.py +414 -0
  178. setiastro/saspro/copyastro.py +190 -0
  179. setiastro/saspro/cosmicclarity.py +1589 -0
  180. setiastro/saspro/cosmicclarity_preset.py +407 -0
  181. setiastro/saspro/crop_dialog_pro.py +983 -0
  182. setiastro/saspro/crop_preset.py +189 -0
  183. setiastro/saspro/curve_editor_pro.py +2562 -0
  184. setiastro/saspro/curves_preset.py +375 -0
  185. setiastro/saspro/debayer.py +673 -0
  186. setiastro/saspro/debug_utils.py +29 -0
  187. setiastro/saspro/dnd_mime.py +35 -0
  188. setiastro/saspro/doc_manager.py +2664 -0
  189. setiastro/saspro/exoplanet_detector.py +2166 -0
  190. setiastro/saspro/file_utils.py +284 -0
  191. setiastro/saspro/fitsmodifier.py +748 -0
  192. setiastro/saspro/fix_bom.py +32 -0
  193. setiastro/saspro/free_torch_memory.py +48 -0
  194. setiastro/saspro/frequency_separation.py +1349 -0
  195. setiastro/saspro/function_bundle.py +1596 -0
  196. setiastro/saspro/generate_translations.py +3092 -0
  197. setiastro/saspro/ghs_dialog_pro.py +663 -0
  198. setiastro/saspro/ghs_preset.py +284 -0
  199. setiastro/saspro/graxpert.py +637 -0
  200. setiastro/saspro/graxpert_preset.py +287 -0
  201. setiastro/saspro/gui/__init__.py +0 -0
  202. setiastro/saspro/gui/main_window.py +8792 -0
  203. setiastro/saspro/gui/mixins/__init__.py +33 -0
  204. setiastro/saspro/gui/mixins/dock_mixin.py +375 -0
  205. setiastro/saspro/gui/mixins/file_mixin.py +450 -0
  206. setiastro/saspro/gui/mixins/geometry_mixin.py +503 -0
  207. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  208. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  209. setiastro/saspro/gui/mixins/menu_mixin.py +390 -0
  210. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  211. setiastro/saspro/gui/mixins/toolbar_mixin.py +1619 -0
  212. setiastro/saspro/gui/mixins/update_mixin.py +323 -0
  213. setiastro/saspro/gui/mixins/view_mixin.py +435 -0
  214. setiastro/saspro/gui/statistics_dialog.py +47 -0
  215. setiastro/saspro/halobgon.py +488 -0
  216. setiastro/saspro/header_viewer.py +448 -0
  217. setiastro/saspro/headless_utils.py +88 -0
  218. setiastro/saspro/histogram.py +756 -0
  219. setiastro/saspro/history_explorer.py +941 -0
  220. setiastro/saspro/i18n.py +168 -0
  221. setiastro/saspro/image_combine.py +417 -0
  222. setiastro/saspro/image_peeker_pro.py +1604 -0
  223. setiastro/saspro/imageops/__init__.py +37 -0
  224. setiastro/saspro/imageops/mdi_snap.py +292 -0
  225. setiastro/saspro/imageops/scnr.py +36 -0
  226. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  227. setiastro/saspro/imageops/stretch.py +236 -0
  228. setiastro/saspro/isophote.py +1182 -0
  229. setiastro/saspro/layers.py +208 -0
  230. setiastro/saspro/layers_dock.py +714 -0
  231. setiastro/saspro/lazy_imports.py +193 -0
  232. setiastro/saspro/legacy/__init__.py +2 -0
  233. setiastro/saspro/legacy/image_manager.py +2360 -0
  234. setiastro/saspro/legacy/numba_utils.py +3676 -0
  235. setiastro/saspro/legacy/xisf.py +1213 -0
  236. setiastro/saspro/linear_fit.py +537 -0
  237. setiastro/saspro/live_stacking.py +1854 -0
  238. setiastro/saspro/log_bus.py +5 -0
  239. setiastro/saspro/logging_config.py +460 -0
  240. setiastro/saspro/luminancerecombine.py +510 -0
  241. setiastro/saspro/main_helpers.py +201 -0
  242. setiastro/saspro/mask_creation.py +1086 -0
  243. setiastro/saspro/masks_core.py +56 -0
  244. setiastro/saspro/mdi_widgets.py +353 -0
  245. setiastro/saspro/memory_utils.py +666 -0
  246. setiastro/saspro/metadata_patcher.py +75 -0
  247. setiastro/saspro/mfdeconv.py +3909 -0
  248. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  249. setiastro/saspro/mfdeconvcudnn.py +3312 -0
  250. setiastro/saspro/mfdeconvsport.py +2459 -0
  251. setiastro/saspro/minorbodycatalog.py +567 -0
  252. setiastro/saspro/morphology.py +407 -0
  253. setiastro/saspro/multiscale_decomp.py +1747 -0
  254. setiastro/saspro/nbtorgb_stars.py +541 -0
  255. setiastro/saspro/numba_utils.py +3145 -0
  256. setiastro/saspro/numba_warmup.py +141 -0
  257. setiastro/saspro/ops/__init__.py +9 -0
  258. setiastro/saspro/ops/command_help_dialog.py +623 -0
  259. setiastro/saspro/ops/command_runner.py +217 -0
  260. setiastro/saspro/ops/commands.py +1594 -0
  261. setiastro/saspro/ops/script_editor.py +1105 -0
  262. setiastro/saspro/ops/scripts.py +1476 -0
  263. setiastro/saspro/ops/settings.py +637 -0
  264. setiastro/saspro/parallel_utils.py +554 -0
  265. setiastro/saspro/pedestal.py +121 -0
  266. setiastro/saspro/perfect_palette_picker.py +1105 -0
  267. setiastro/saspro/pipeline.py +110 -0
  268. setiastro/saspro/pixelmath.py +1604 -0
  269. setiastro/saspro/plate_solver.py +2445 -0
  270. setiastro/saspro/project_io.py +797 -0
  271. setiastro/saspro/psf_utils.py +136 -0
  272. setiastro/saspro/psf_viewer.py +549 -0
  273. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  274. setiastro/saspro/remove_green.py +331 -0
  275. setiastro/saspro/remove_stars.py +1599 -0
  276. setiastro/saspro/remove_stars_preset.py +446 -0
  277. setiastro/saspro/resources.py +503 -0
  278. setiastro/saspro/rgb_combination.py +208 -0
  279. setiastro/saspro/rgb_extract.py +19 -0
  280. setiastro/saspro/rgbalign.py +723 -0
  281. setiastro/saspro/runtime_imports.py +7 -0
  282. setiastro/saspro/runtime_torch.py +754 -0
  283. setiastro/saspro/save_options.py +73 -0
  284. setiastro/saspro/selective_color.py +1611 -0
  285. setiastro/saspro/sfcc.py +1472 -0
  286. setiastro/saspro/shortcuts.py +3116 -0
  287. setiastro/saspro/signature_insert.py +1102 -0
  288. setiastro/saspro/stacking_suite.py +19066 -0
  289. setiastro/saspro/star_alignment.py +7380 -0
  290. setiastro/saspro/star_alignment_preset.py +329 -0
  291. setiastro/saspro/star_metrics.py +49 -0
  292. setiastro/saspro/star_spikes.py +765 -0
  293. setiastro/saspro/star_stretch.py +507 -0
  294. setiastro/saspro/stat_stretch.py +538 -0
  295. setiastro/saspro/status_log_dock.py +78 -0
  296. setiastro/saspro/subwindow.py +3407 -0
  297. setiastro/saspro/supernovaasteroidhunter.py +1719 -0
  298. setiastro/saspro/swap_manager.py +134 -0
  299. setiastro/saspro/torch_backend.py +89 -0
  300. setiastro/saspro/torch_rejection.py +434 -0
  301. setiastro/saspro/translations/all_source_strings.json +4726 -0
  302. setiastro/saspro/translations/ar_translations.py +4096 -0
  303. setiastro/saspro/translations/de_translations.py +3728 -0
  304. setiastro/saspro/translations/es_translations.py +4169 -0
  305. setiastro/saspro/translations/fr_translations.py +4090 -0
  306. setiastro/saspro/translations/hi_translations.py +3803 -0
  307. setiastro/saspro/translations/integrate_translations.py +271 -0
  308. setiastro/saspro/translations/it_translations.py +4728 -0
  309. setiastro/saspro/translations/ja_translations.py +3834 -0
  310. setiastro/saspro/translations/pt_translations.py +3847 -0
  311. setiastro/saspro/translations/ru_translations.py +3082 -0
  312. setiastro/saspro/translations/saspro_ar.qm +0 -0
  313. setiastro/saspro/translations/saspro_ar.ts +16019 -0
  314. setiastro/saspro/translations/saspro_de.qm +0 -0
  315. setiastro/saspro/translations/saspro_de.ts +14548 -0
  316. setiastro/saspro/translations/saspro_es.qm +0 -0
  317. setiastro/saspro/translations/saspro_es.ts +16202 -0
  318. setiastro/saspro/translations/saspro_fr.qm +0 -0
  319. setiastro/saspro/translations/saspro_fr.ts +15870 -0
  320. setiastro/saspro/translations/saspro_hi.qm +0 -0
  321. setiastro/saspro/translations/saspro_hi.ts +14855 -0
  322. setiastro/saspro/translations/saspro_it.qm +0 -0
  323. setiastro/saspro/translations/saspro_it.ts +19046 -0
  324. setiastro/saspro/translations/saspro_ja.qm +0 -0
  325. setiastro/saspro/translations/saspro_ja.ts +14980 -0
  326. setiastro/saspro/translations/saspro_pt.qm +0 -0
  327. setiastro/saspro/translations/saspro_pt.ts +15024 -0
  328. setiastro/saspro/translations/saspro_ru.qm +0 -0
  329. setiastro/saspro/translations/saspro_ru.ts +11835 -0
  330. setiastro/saspro/translations/saspro_sw.qm +0 -0
  331. setiastro/saspro/translations/saspro_sw.ts +15237 -0
  332. setiastro/saspro/translations/saspro_uk.qm +0 -0
  333. setiastro/saspro/translations/saspro_uk.ts +15248 -0
  334. setiastro/saspro/translations/saspro_zh.qm +0 -0
  335. setiastro/saspro/translations/saspro_zh.ts +15289 -0
  336. setiastro/saspro/translations/sw_translations.py +3897 -0
  337. setiastro/saspro/translations/uk_translations.py +3929 -0
  338. setiastro/saspro/translations/zh_translations.py +3910 -0
  339. setiastro/saspro/versioning.py +77 -0
  340. setiastro/saspro/view_bundle.py +1558 -0
  341. setiastro/saspro/wavescale_hdr.py +645 -0
  342. setiastro/saspro/wavescale_hdr_preset.py +101 -0
  343. setiastro/saspro/wavescalede.py +680 -0
  344. setiastro/saspro/wavescalede_preset.py +230 -0
  345. setiastro/saspro/wcs_update.py +374 -0
  346. setiastro/saspro/whitebalance.py +513 -0
  347. setiastro/saspro/widgets/__init__.py +48 -0
  348. setiastro/saspro/widgets/common_utilities.py +306 -0
  349. setiastro/saspro/widgets/graphics_views.py +122 -0
  350. setiastro/saspro/widgets/image_utils.py +518 -0
  351. setiastro/saspro/widgets/minigame/game.js +991 -0
  352. setiastro/saspro/widgets/minigame/index.html +53 -0
  353. setiastro/saspro/widgets/minigame/style.css +241 -0
  354. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  355. setiastro/saspro/widgets/resource_monitor.py +263 -0
  356. setiastro/saspro/widgets/spinboxes.py +290 -0
  357. setiastro/saspro/widgets/themed_buttons.py +13 -0
  358. setiastro/saspro/widgets/wavelet_utils.py +331 -0
  359. setiastro/saspro/wimi.py +7996 -0
  360. setiastro/saspro/wims.py +578 -0
  361. setiastro/saspro/window_shelf.py +185 -0
  362. setiastro/saspro/xisf.py +1213 -0
  363. setiastrosuitepro-1.6.5.post3.dist-info/METADATA +278 -0
  364. setiastrosuitepro-1.6.5.post3.dist-info/RECORD +368 -0
  365. setiastrosuitepro-1.6.5.post3.dist-info/WHEEL +4 -0
  366. setiastrosuitepro-1.6.5.post3.dist-info/entry_points.txt +6 -0
  367. setiastrosuitepro-1.6.5.post3.dist-info/licenses/LICENSE +674 -0
  368. setiastrosuitepro-1.6.5.post3.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,446 @@
1
+ # pro/remove_stars_preset.py
2
+ from __future__ import annotations
3
+ import os
4
+ import platform
5
+ import shutil
6
+ import numpy as np
7
+ from PyQt6.QtCore import QTimer
8
+ from PyQt6.QtWidgets import QMessageBox, QDialog, QFormLayout, QDialogButtonBox, QComboBox, QCheckBox, QSpinBox, QLabel
9
+
10
+ from setiastro.saspro.legacy.image_manager import save_image, load_image
11
+
12
+ # Reuse helpers & plumbing from the interactive module
13
+ from .remove_stars import (
14
+ _ProcThread, _ProcDialog,
15
+ _mtf_params_unlinked, _apply_mtf_unlinked_rgb, _invert_mtf_unlinked_rgb,
16
+ _active_mask3_from_doc, _mask_blend_with_doc_mask, _push_as_new_doc,
17
+ _ensure_exec_bit,
18
+ )
19
+
20
+ # ---------- Headless public entry ----------
21
+ def run_remove_stars_via_preset(main, doc_or_preset=None, preset: dict | None = None, target_doc=None):
22
+ """
23
+ Headless star removal from a shortcut preset.
24
+
25
+ Supports BOTH call shapes:
26
+ 1) New CommandRunner shape:
27
+ run_remove_stars_via_preset(main, target_doc, preset)
28
+ 2) Legacy shape:
29
+ run_remove_stars_via_preset(main, preset_dict, target_doc=doc)
30
+ run_remove_stars_via_preset(main, preset_dict)
31
+ """
32
+ from PyQt6.QtWidgets import QMessageBox
33
+ from PyQt6.QtCore import QTimer
34
+ import os
35
+ import platform
36
+
37
+ # ---- Interpret arguments for backward compat / new executor ----
38
+ if preset is None and isinstance(doc_or_preset, dict):
39
+ # Legacy: (main, preset_dict, target_doc=?)
40
+ p = dict(doc_or_preset or {})
41
+ doc = target_doc
42
+ else:
43
+ # New executor: (main, doc, preset_dict)
44
+ p = dict(preset or {})
45
+ doc = target_doc if target_doc is not None else doc_or_preset
46
+
47
+ # Resolve active doc if still None
48
+ if doc is None:
49
+ d = getattr(main, "_active_doc", None)
50
+ doc = d() if callable(d) else d
51
+
52
+ if doc is None or getattr(doc, "image", None) is None:
53
+ QMessageBox.warning(main, "Remove Stars", "Load an image first.")
54
+ return
55
+
56
+ tool = str(p.get("tool", "starnet")).lower()
57
+
58
+ # mark headless + short cool-down to block the interactive path
59
+ setattr(main, "_remove_stars_headless_running", True)
60
+ setattr(main, "_remove_stars_guard", True)
61
+
62
+ def _clear_flags():
63
+ for name in ("_remove_stars_headless_running", "_remove_stars_guard"):
64
+ try:
65
+ delattr(main, name)
66
+ except Exception:
67
+ setattr(main, name, False)
68
+
69
+ try:
70
+ if tool in ("starnet", "star_net", "sn"):
71
+ _run_starnet_headless(main, doc, p)
72
+ elif tool in ("darkstar", "cosmicclarity", "cosmic_clarity"):
73
+ _run_darkstar_headless(main, doc, p)
74
+ else:
75
+ QMessageBox.critical(main, "Remove Stars", f"Unknown tool '{tool}'.")
76
+ finally:
77
+ QTimer.singleShot(1200, _clear_flags)
78
+
79
+
80
+ def apply_remove_stars_to_doc(parent, target_doc, preset: dict | None):
81
+ """
82
+ Replay helper: apply Remove Stars to a specific doc (base/ROI).
83
+ """
84
+ if parent is None:
85
+ return
86
+ main = parent
87
+ # walk up to main window if needed
88
+ while main is not None and not hasattr(main, "doc_manager"):
89
+ main = main.parent() if hasattr(main, "parent") else None
90
+ if main is None:
91
+ main = parent
92
+
93
+ run_remove_stars_via_preset(main, preset, target_doc=target_doc)
94
+
95
+
96
+ # ---------- StarNet headless ----------
97
+ def _resolve_starnet_exe_headless(main, override: str | None) -> str | None:
98
+ if override and os.path.exists(override):
99
+ if platform.system() in ("Darwin", "Linux"): _ensure_exec_bit(override)
100
+ return override
101
+ s = getattr(main, "settings", None)
102
+ exe = None
103
+ if s:
104
+ for key in ("starnet/exe_path", "paths/starnet"):
105
+ try:
106
+ val = s.value(key, "", type=str)
107
+ except Exception:
108
+ val = s.value(key, "")
109
+ if isinstance(val, str) and val.strip() and os.path.exists(val):
110
+ exe = val.strip(); break
111
+ if exe and platform.system() in ("Darwin", "Linux"): _ensure_exec_bit(exe)
112
+ return exe
113
+
114
+ def _run_starnet_headless(main, doc, p):
115
+ exe = _resolve_starnet_exe_headless(main, p.get("starnet_exe") or p.get("exe"))
116
+ if not exe:
117
+ QMessageBox.warning(main, "StarNet", "StarNet path not set. Open the interactive tool once to set it.")
118
+ return
119
+
120
+ # RGB float32 [0..1]
121
+ src = np.asarray(doc.image)
122
+ if src.ndim == 2: processing_image = np.stack([src]*3, axis=-1)
123
+ elif src.ndim == 3 and src.shape[2] == 1: processing_image = np.repeat(src, 3, axis=2)
124
+ else: processing_image = src
125
+ processing_image = processing_image.astype(np.float32, copy=False)
126
+
127
+ is_linear = bool(p.get("linear", True))
128
+ did_stretch = is_linear
129
+
130
+ # sanitize + normalize if needed (keep exactly like interactive)
131
+ processing_image = np.nan_to_num(processing_image, nan=0.0, posinf=0.0, neginf=0.0).astype(np.float32, copy=False)
132
+
133
+ scale_factor = float(np.max(processing_image)) if processing_image.size else 1.0
134
+ processing_norm = (processing_image / scale_factor) if scale_factor > 1.0 else processing_image
135
+ processing_norm = np.clip(processing_norm, 0.0, 1.0)
136
+
137
+ img_for_starnet = processing_norm
138
+
139
+ if is_linear:
140
+ mtf_params = _mtf_params_unlinked(processing_norm, shadows_clipping=-2.8, targetbg=0.25)
141
+ img_for_starnet = _apply_mtf_unlinked_rgb(processing_norm, mtf_params)
142
+
143
+ # stash for inverse step (same keys as interactive)
144
+ try:
145
+ setattr(main, "_starnet_stat_meta", {
146
+ "scheme": "siril_mtf",
147
+ "s": np.asarray(mtf_params["s"], dtype=np.float32),
148
+ "m": np.asarray(mtf_params["m"], dtype=np.float32),
149
+ "h": np.asarray(mtf_params["h"], dtype=np.float32),
150
+ "scale": float(scale_factor),
151
+ })
152
+ except Exception:
153
+ pass
154
+ else:
155
+ try:
156
+ if hasattr(main, "_starnet_stat_meta"):
157
+ delattr(main, "_starnet_stat_meta")
158
+ except Exception:
159
+ pass
160
+
161
+
162
+ starnet_dir = os.path.dirname(exe) or os.getcwd()
163
+ in_path = os.path.join(starnet_dir, "imagetoremovestars.tif")
164
+ out_path = os.path.join(starnet_dir, "starless.tif")
165
+
166
+ try:
167
+ save_image(img_for_starnet, in_path, original_format="tif",
168
+ bit_depth="16-bit", original_header=None, is_mono=False,
169
+ image_meta=None, file_meta=None)
170
+ except Exception as e:
171
+ QMessageBox.critical(main, "StarNet", f"Failed to write input TIFF:\n{e}")
172
+ return
173
+
174
+ exe_name = os.path.basename(exe).lower()
175
+ sysname = platform.system()
176
+ if sysname in ("Windows", "Linux"):
177
+ command = [exe, in_path, out_path, "256"]
178
+ else:
179
+ if "starnet2" in exe_name:
180
+ command = [exe, "--input", in_path, "--output", out_path]
181
+ else:
182
+ command = [exe, in_path, out_path]
183
+
184
+ dlg = _ProcDialog(main, title="StarNet Progress")
185
+ thr = _ProcThread(command, cwd=starnet_dir)
186
+ thr.output_signal.connect(dlg.append_text)
187
+ thr.finished_signal.connect(lambda rc: _finish_starnet(main, doc, rc, dlg, in_path, out_path, did_stretch))
188
+ dlg.cancel_button.clicked.connect(thr.terminate)
189
+ dlg.show(); thr.start(); dlg.exec()
190
+
191
+ def _finish_starnet(main, doc, rc, dlg, in_path, out_path, did_stretch):
192
+ if rc != 0 or not os.path.exists(out_path):
193
+ QMessageBox.critical(main, "StarNet", "StarNet failed or no output image produced.")
194
+ _safe_rm(in_path); _safe_rm(out_path); dlg.close(); return
195
+
196
+ starless_rgb, _, _, _ = load_image(out_path)
197
+ if starless_rgb is None:
198
+ QMessageBox.critical(main, "StarNet", "Failed to load starless image.")
199
+ _safe_rm(in_path); _safe_rm(out_path); dlg.close(); return
200
+
201
+ if starless_rgb.ndim == 2 or (starless_rgb.ndim == 3 and starless_rgb.shape[2] == 1):
202
+ starless_rgb = np.stack([starless_rgb]*3, axis=-1)
203
+ starless_rgb = starless_rgb.astype(np.float32, copy=False)
204
+
205
+ if did_stretch:
206
+ meta = getattr(main, "_starnet_stat_meta", None)
207
+ if isinstance(meta, dict) and meta.get("scheme") == "siril_mtf":
208
+ try:
209
+ p = {
210
+ "s": np.asarray(meta.get("s"), dtype=np.float32),
211
+ "m": np.asarray(meta.get("m"), dtype=np.float32),
212
+ "h": np.asarray(meta.get("h"), dtype=np.float32),
213
+ }
214
+ inv = _invert_mtf_unlinked_rgb(starless_rgb, p)
215
+ sc = float(meta.get("scale", 1.0))
216
+ if sc > 1.0:
217
+ inv *= sc
218
+ starless_rgb = np.clip(inv, 0.0, 1.0).astype(np.float32, copy=False)
219
+ except Exception:
220
+ pass
221
+
222
+ # cleanup so it can't leak
223
+ try:
224
+ if hasattr(main, "_starnet_stat_meta"):
225
+ delattr(main, "_starnet_stat_meta")
226
+ except Exception:
227
+ pass
228
+
229
+
230
+ # original as RGB
231
+ orig = np.asarray(doc.image)
232
+ if orig.ndim == 2: original_rgb = np.stack([orig]*3, axis=-1)
233
+ elif orig.ndim == 3 and orig.shape[2] == 1: original_rgb = np.repeat(orig, 3, axis=2)
234
+ else: original_rgb = orig
235
+ original_rgb = original_rgb.astype(np.float32, copy=False)
236
+
237
+ # Stars-Only (same as interactive)
238
+ with np.errstate(divide='ignore', invalid='ignore'):
239
+ stars_only = (original_rgb - starless_rgb) / np.clip(1.0 - starless_rgb, 1e-6, None)
240
+ stars_only = np.nan_to_num(stars_only, nan=0.0, posinf=0.0, neginf=0.0)
241
+ stars_only = np.clip(stars_only, 0.0, 1.0)
242
+ m3 = _active_mask3_from_doc(doc, stars_only.shape[1], stars_only.shape[0])
243
+ if m3 is not None: stars_only *= m3
244
+ _push_as_new_doc(main, doc, stars_only, title_suffix="_stars", source="Stars-Only (StarNet)")
245
+
246
+ # mask-blend starless, then commit
247
+ final_starless = _mask_blend_with_doc_mask(doc, starless_rgb, original_rgb)
248
+ try:
249
+ meta = {"step_name": "Stars Removed", "bit_depth": "32-bit floating point", "is_mono": False}
250
+ doc.apply_edit(final_starless.astype(np.float32, copy=False), metadata=meta, step_name="Stars Removed")
251
+ if hasattr(main, "_log"): main._log("Stars Removed (StarNet, headless)")
252
+ except Exception as e:
253
+ QMessageBox.critical(main, "StarNet", f"Failed to apply starless result:\n{e}")
254
+
255
+ _safe_rm(in_path); _safe_rm(out_path); dlg.close()
256
+
257
+
258
+ # ---------- DarkStar headless ----------
259
+ def _resolve_darkstar_exe_headless(main, override: str | None) -> tuple[str | None, str | None]:
260
+ if override and os.path.exists(override):
261
+ base = os.path.dirname(override)
262
+ if platform.system() in ("Darwin","Linux"): _ensure_exec_bit(override)
263
+ return override, base
264
+ s = getattr(main, "settings", None)
265
+ base = None
266
+ if s:
267
+ for key in ("paths/cosmic_clarity", "cosmic_clarity_folder"):
268
+ try:
269
+ v = s.value(key, "", type=str)
270
+ except Exception:
271
+ v = s.value(key, "")
272
+ if isinstance(v, str) and v.strip() and os.path.isdir(v):
273
+ base = v.strip(); break
274
+ if not base: return None, None
275
+ exe = os.path.join(base, "setiastrocosmicclarity_darkstar.exe" if platform.system()=="Windows"
276
+ else "setiastrocosmicclarity_darkstar")
277
+ if not os.path.exists(exe): return None, None
278
+ if platform.system() in ("Darwin","Linux"): _ensure_exec_bit(exe)
279
+ return exe, base
280
+
281
+ def _run_darkstar_headless(main, doc, p):
282
+ exe, base = _resolve_darkstar_exe_headless(main, p.get("darkstar_exe") or p.get("exe"))
283
+ if not exe or not base:
284
+ QMessageBox.warning(main, "CosmicClarity DarkStar", "DarkStar path not set. Open the interactive tool once to set it.")
285
+ return
286
+
287
+ input_dir = os.path.join(base, "input")
288
+ output_dir = os.path.join(base, "output")
289
+ os.makedirs(input_dir, exist_ok=True)
290
+ os.makedirs(output_dir, exist_ok=True)
291
+
292
+ in_path = os.path.join(input_dir, "imagetoremovestars.tif")
293
+ try:
294
+ save_image(doc.image, in_path, original_format="tif",
295
+ bit_depth="32-bit floating point",
296
+ original_header=None, is_mono=False, image_meta=None, file_meta=None)
297
+ except Exception as e:
298
+ QMessageBox.critical(main, "CosmicClarity DarkStar", f"Failed to write input TIFF:\n{e}")
299
+ return
300
+
301
+ disable_gpu = bool(p.get("disable_gpu", False))
302
+ mode = str(p.get("mode", "unscreen"))
303
+ show = bool(p.get("show_extracted_stars", True))
304
+ stride = int(p.get("stride", 512))
305
+
306
+ args = []
307
+ if disable_gpu: args.append("--disable_gpu")
308
+ args += ["--star_removal_mode", mode]
309
+ if show: args.append("--show_extracted_stars")
310
+ args += ["--chunk_size", str(stride)]
311
+
312
+ command = [exe] + args
313
+
314
+ dlg = _ProcDialog(main, title="CosmicClarityDarkStar Progress")
315
+ thr = _ProcThread(command, cwd=base)
316
+ thr.output_signal.connect(dlg.append_text)
317
+ thr.finished_signal.connect(lambda rc: _finish_darkstar(main, doc, rc, dlg, in_path, output_dir))
318
+ dlg.cancel_button.clicked.connect(thr.terminate)
319
+ dlg.show(); thr.start(); dlg.exec()
320
+
321
+ def _finish_darkstar(main, doc, rc, dlg, in_path, output_dir):
322
+ if rc != 0:
323
+ QMessageBox.critical(main, "CosmicClarity DarkStar", "Process failed.")
324
+ _safe_rm(in_path); dlg.close(); return
325
+
326
+ starless_path = os.path.join(output_dir, "imagetoremovestars_starless.tif")
327
+ if not os.path.exists(starless_path):
328
+ QMessageBox.critical(main, "CosmicClarity DarkStar", "Starless image not found.")
329
+ _safe_rm(in_path); dlg.close(); return
330
+
331
+ starless, _, _, _ = load_image(starless_path)
332
+ if starless is None:
333
+ QMessageBox.critical(main, "CosmicClarity DarkStar", "Failed to load starless image.")
334
+ _safe_rm(in_path); dlg.close(); return
335
+
336
+ if starless.ndim == 2 or (starless.ndim == 3 and starless.shape[2] == 1):
337
+ starless_rgb = np.stack([starless]*3, axis=-1)
338
+ else:
339
+ starless_rgb = starless
340
+ starless_rgb = starless_rgb.astype(np.float32, copy=False)
341
+
342
+ src = np.asarray(doc.image)
343
+ if src.ndim == 2: original_rgb = np.stack([src]*3, axis=-1)
344
+ elif src.ndim == 3 and src.shape[2] == 1: original_rgb = np.repeat(src, 3, axis=2)
345
+ else: original_rgb = src
346
+ original_rgb = original_rgb.astype(np.float32, copy=False)
347
+
348
+ # stars-only (if DarkStar produced it)
349
+ stars_path = os.path.join(output_dir, "imagetoremovestars_stars_only.tif")
350
+ if os.path.exists(stars_path):
351
+ stars_only, _, _, _ = load_image(stars_path)
352
+ if stars_only is not None:
353
+ if stars_only.ndim == 2 or (stars_only.ndim == 3 and stars_only.shape[2] == 1):
354
+ stars_only = np.stack([stars_only]*3, axis=-1)
355
+ stars_only = stars_only.astype(np.float32, copy=False)
356
+ m3 = _active_mask3_from_doc(doc, stars_only.shape[1], stars_only.shape[0])
357
+ if m3 is not None: stars_only *= m3
358
+ _push_as_new_doc(main, doc, stars_only, title_suffix="_stars", source="Stars-Only (DarkStar)")
359
+
360
+ final_starless = _mask_blend_with_doc_mask(doc, starless_rgb, original_rgb)
361
+ try:
362
+ meta = {"step_name": "Stars Removed", "bit_depth": "32-bit floating point", "is_mono": False}
363
+ doc.apply_edit(final_starless.astype(np.float32, copy=False), metadata=meta, step_name="Stars Removed")
364
+ if hasattr(main, "_log"): main._log("Stars Removed (DarkStar, headless)")
365
+ except Exception as e:
366
+ QMessageBox.critical(main, "CosmicClarity DarkStar", f"Failed to apply starless result:\n{e}")
367
+
368
+ # cleanup
369
+ try:
370
+ _safe_rm(in_path)
371
+ for fn in ("imagetoremovestars_starless.tif","imagetoremovestars_stars_only.tif"):
372
+ p = os.path.join(output_dir, fn)
373
+ if os.path.exists(p): _safe_rm(p)
374
+ except Exception:
375
+ pass
376
+ dlg.close()
377
+
378
+
379
+ # ---------- Simple preset editor (for the shortcut button) ----------
380
+ class RemoveStarsPresetDialog(QDialog):
381
+ def __init__(self, parent=None, initial: dict | None = None):
382
+ super().__init__(parent)
383
+ self.setWindowTitle("Remove Stars — Preset")
384
+ p = dict(initial or {})
385
+
386
+ self.cmb_tool = QComboBox()
387
+ self.cmb_tool.addItems(["StarNet", "CosmicClarity DarkStar"])
388
+ self.cmb_tool.setCurrentIndex(0 if str(p.get("tool","starnet")).lower().startswith("star") else 1)
389
+
390
+ # StarNet options
391
+ self.chk_linear = QCheckBox("Image is linear (apply temporary stretch)")
392
+ self.chk_linear.setChecked(bool(p.get("linear", True)))
393
+
394
+ # DarkStar options
395
+ self.chk_disable_gpu = QCheckBox("Disable GPU")
396
+ self.chk_disable_gpu.setChecked(bool(p.get("disable_gpu", False)))
397
+ self.cmb_mode = QComboBox(); self.cmb_mode.addItems(["unscreen","additive"])
398
+ self.cmb_mode.setCurrentText(str(p.get("mode","unscreen")))
399
+ self.chk_show = QCheckBox("Show extracted stars")
400
+ self.chk_show.setChecked(bool(p.get("show_extracted_stars", True)))
401
+ self.cmb_stride = QComboBox()
402
+ for v in (64,128,256,512,1024): self.cmb_stride.addItem(str(v), v)
403
+ self.cmb_stride.setCurrentText(str(int(p.get("stride", 512))))
404
+
405
+ form = QFormLayout(self)
406
+ form.addRow("Tool:", self.cmb_tool)
407
+ form.addRow(QLabel("— StarNet —"))
408
+ form.addRow("", self.chk_linear)
409
+ form.addRow(QLabel("— DarkStar —"))
410
+ form.addRow("Mode:", self.cmb_mode)
411
+ form.addRow("Stride:", self.cmb_stride)
412
+ form.addRow("", self.chk_disable_gpu)
413
+ form.addRow("", self.chk_show)
414
+
415
+ btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, parent=self)
416
+ btns.accepted.connect(self.accept); btns.rejected.connect(self.reject)
417
+ form.addRow(btns)
418
+
419
+ # show/hide sections depending on tool
420
+ def _toggle():
421
+ star = self.cmb_tool.currentIndex() == 0
422
+ self.chk_linear.setEnabled(star)
423
+ for w in (self.cmb_mode, self.cmb_stride, self.chk_disable_gpu, self.chk_show):
424
+ w.setEnabled(not star)
425
+ self.cmb_tool.currentIndexChanged.connect(lambda _: _toggle())
426
+ _toggle()
427
+
428
+ def result_dict(self) -> dict:
429
+ if self.cmb_tool.currentIndex() == 0:
430
+ return {"tool": "starnet", "linear": bool(self.chk_linear.isChecked())}
431
+ return {
432
+ "tool": "darkstar",
433
+ "disable_gpu": bool(self.chk_disable_gpu.isChecked()),
434
+ "mode": self.cmb_mode.currentText(),
435
+ "show_extracted_stars": bool(self.chk_show.isChecked()),
436
+ "stride": int(self.cmb_stride.currentData()),
437
+ }
438
+
439
+
440
+ # ---------- local util ----------
441
+ def _safe_rm(p):
442
+ try:
443
+ if p and os.path.exists(p): os.remove(p)
444
+ except Exception as e:
445
+ import logging
446
+ logging.debug(f"Exception suppressed: {type(e).__name__}: {e}")