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,95 @@
1
+ """
2
+ Runtime hook for astroquery to prevent network downloads during import.
3
+ """
4
+ import os
5
+ import sys
6
+ import tempfile
7
+ import json
8
+ from pathlib import Path
9
+
10
+ def setup_astroquery_offline():
11
+ """Configure astroquery and astropy for offline operation."""
12
+
13
+ # Disable astropy data downloads
14
+ os.environ['ASTROPY_DOWNLOAD_CACHE_TIMEOUT'] = '0'
15
+ os.environ['ASTROPY_DOWNLOAD_BLOCK_SIZE'] = '0'
16
+ os.environ['ASTROQUERY_DOWNLOAD_CACHE_TIMEOUT'] = '0'
17
+
18
+ # Set up cache directories
19
+ if hasattr(sys, '_MEIPASS'):
20
+ # Running in PyInstaller bundle
21
+ cache_dir = os.path.join(tempfile.gettempdir(), 'astropy_cache')
22
+ os.makedirs(cache_dir, exist_ok=True)
23
+ os.environ['XDG_CACHE_HOME'] = cache_dir
24
+
25
+ # Create the missing query_criteria_fields.json file
26
+ try:
27
+ # Create minimal data that astroquery.simbad needs
28
+ query_criteria = {
29
+ "basic": [
30
+ "MAIN_ID", "RA", "DEC", "COO_ERR_MAJA", "COO_ERR_MINA",
31
+ "COO_ERR_ANGLE", "COO_QUAL", "COO_WAVELENGTH", "COO_BIBCODE"
32
+ ],
33
+ "ids": ["ID"],
34
+ "biblio": ["BIBCODELIST"],
35
+ "measurements": ["FLUX", "FLUXDATA"],
36
+ "note": "Minimal data for offline PyInstaller bundle"
37
+ }
38
+
39
+ # Create in multiple possible locations
40
+ locations = [
41
+ os.path.join(tempfile.gettempdir(), 'astropy_data'),
42
+ os.path.join(cache_dir, 'astropy', 'data'),
43
+ os.path.join(cache_dir, 'downloads'),
44
+ ]
45
+
46
+ for loc in locations:
47
+ os.makedirs(loc, exist_ok=True)
48
+ query_file = os.path.join(loc, 'query_criteria_fields.json')
49
+ with open(query_file, 'w') as f:
50
+ json.dump(query_criteria, f, indent=2)
51
+ print(f"Created astroquery data file: {query_file}")
52
+
53
+ except Exception as e:
54
+ print(f"Warning: Could not create astroquery data files: {e}")
55
+
56
+ # Monkey patch astropy.utils.data.download_file BEFORE any imports
57
+ def patched_download_file(*args, **kwargs):
58
+ """Patched download_file that returns a local fallback instead of downloading."""
59
+
60
+ # If it's asking for query_criteria_fields.json, return our local version
61
+ if len(args) > 0 and 'query_criteria_fields.json' in str(args[0]):
62
+ fallback_locations = [
63
+ os.path.join(tempfile.gettempdir(), 'astropy_data', 'query_criteria_fields.json'),
64
+ os.path.join(tempfile.gettempdir(), 'astropy_cache', 'astropy', 'data', 'query_criteria_fields.json'),
65
+ ]
66
+
67
+ for fallback_file in fallback_locations:
68
+ if os.path.exists(fallback_file):
69
+ print(f"Using local astroquery data: {fallback_file}")
70
+ return fallback_file
71
+
72
+ # For other files, create a dummy file to prevent crashes
73
+ dummy_file = os.path.join(tempfile.gettempdir(), 'astropy_dummy.json')
74
+ if not os.path.exists(dummy_file):
75
+ with open(dummy_file, 'w') as f:
76
+ json.dump({"note": "Dummy file for offline operation"}, f)
77
+
78
+ return dummy_file
79
+
80
+ # Apply patches before any astropy/astroquery imports
81
+ try:
82
+ setup_astroquery_offline()
83
+
84
+ # Patch the download function early
85
+ import astropy.utils.data
86
+ if not hasattr(astropy.utils.data, '_original_download_file'):
87
+ astropy.utils.data._original_download_file = astropy.utils.data.download_file
88
+ astropy.utils.data.download_file = patched_download_file
89
+ print("✓ Patched astropy.utils.data.download_file for offline operation")
90
+
91
+ except Exception as e:
92
+ print(f"Warning: Could not fully patch astropy for offline operation: {e}")
93
+ # Continue anyway - the app might still work
94
+
95
+ print("✓ astroquery runtime hook applied")
@@ -0,0 +1,331 @@
1
+ # pro/remove_green.py
2
+ from __future__ import annotations
3
+ import numpy as np
4
+
5
+ from PyQt6.QtCore import Qt
6
+ from PyQt6.QtWidgets import (
7
+ QDialog, QVBoxLayout, QLabel, QSlider, QHBoxLayout,
8
+ QPushButton, QMessageBox, QCheckBox, QComboBox
9
+ )
10
+ try:
11
+ import cv2
12
+ except Exception:
13
+ cv2 = None
14
+
15
+ # ---------- utils (now imported from shared location) ----------
16
+ from setiastro.saspro.widgets.image_utils import (
17
+ to_float01 as _to_float01,
18
+ extract_mask_from_document as _active_mask_array_from_doc
19
+ )
20
+
21
+ def _ensure_rgb(arr: np.ndarray) -> np.ndarray | None:
22
+ """Return float32 RGB [0..1] or None if impossible."""
23
+ a = _to_float01(arr)
24
+ if a.ndim == 2:
25
+ return None
26
+ if a.ndim == 3 and a.shape[2] == 1:
27
+ return None
28
+ if a.ndim == 3 and a.shape[2] >= 3:
29
+ return a[..., :3].astype(np.float32, copy=False)
30
+ return None
31
+
32
+ # ---------- SCNR core (with modes + preserve lightness) ----------
33
+ _SCNR_MODE_LABELS = {
34
+ "avg": "Average(R,B)",
35
+ "max": "Max(R,B)",
36
+ "min": "Min(R,B)",
37
+ }
38
+
39
+ def _compute_neutral(r: np.ndarray, b: np.ndarray, mode: str) -> np.ndarray:
40
+ if mode == "max":
41
+ return np.maximum(r, b)
42
+ elif mode == "min":
43
+ return np.minimum(r, b)
44
+ # default "avg"
45
+ return (r + b) * 0.5
46
+
47
+ def _apply_scnr_rgb(rgb: np.ndarray, amount: float, mode: str = "avg", preserve_lightness: bool = True) -> np.ndarray:
48
+ """
49
+ SCNR green suppression:
50
+ G' = G - amount * max(0, G - neutral)
51
+ where neutral is avg/max/min of (R,B) depending on mode.
52
+
53
+ If preserve_lightness=True:
54
+ compute per-pixel scale s = Y_before / Y_after (Rec.709 luma),
55
+ cap s so that no channel exceeds 1.0, and multiply ALL channels by s.
56
+ """
57
+ rgb = np.clip(rgb.astype(np.float32, copy=False), 0.0, 1.0)
58
+ R, G, B = rgb[..., 0], rgb[..., 1], rgb[..., 2]
59
+
60
+ # choose neutral comparator (avg/max/min of R,B)
61
+ neutral = _compute_neutral(R, B, mode)
62
+ excess = np.maximum(0.0, G - neutral)
63
+ G_new = np.clip(G - float(np.clip(amount, 0.0, 1.0)) * excess, 0.0, 1.0)
64
+
65
+ out = np.stack([R, G_new, B], axis=-1)
66
+
67
+ if not preserve_lightness:
68
+ return out
69
+
70
+ # ---- preserve perceived lightness (scale ALL channels equally) ----
71
+ wR, wG, wB = 0.2126, 0.7152, 0.0722
72
+ Y_before = wR * R + wG * G + wB * B
73
+ Y_after = wR * out[..., 0] + wG * out[..., 1] + wB * out[..., 2]
74
+
75
+ eps = 1e-8
76
+ scale = Y_before / np.maximum(Y_after, eps)
77
+
78
+ # highlight safety: prevent any channel from exceeding 1.0 after scaling
79
+ maxc = np.max(out, axis=2) # current per-pixel max channel
80
+ cap = np.where(maxc > 0.0, 1.0 / np.maximum(maxc, eps), 1.0)
81
+ scale = np.minimum(scale, cap)
82
+
83
+ out = np.clip(out * scale[..., None], 0.0, 1.0)
84
+ return out
85
+
86
+ # ---------- headless core ----------
87
+ def remove_green_headless(
88
+ doc,
89
+ amount: float = 1.0,
90
+ mode: str = "avg", # "avg" | "max" | "min"
91
+ preserve_lightness: bool = True,
92
+ ):
93
+ """
94
+ Run SCNR on doc.image (RGB only), blend with active mask if present, push as undoable edit.
95
+ """
96
+ if doc is None or getattr(doc, "image", None) is None:
97
+ return
98
+
99
+ src = np.asarray(doc.image)
100
+ rgb = _ensure_rgb(src)
101
+ if rgb is None:
102
+ try:
103
+ doc.apply_edit(src.astype(np.float32, copy=False),
104
+ metadata={"step_name": "Remove Green (no-op non-RGB)"},
105
+ step_name="Remove Green")
106
+ except Exception:
107
+ pass
108
+ return
109
+
110
+ amt = float(max(0.0, min(1.0, amount)))
111
+ mode = (mode or "avg").lower()
112
+ if mode not in ("avg", "max", "min"):
113
+ mode = "avg"
114
+
115
+ processed = _apply_scnr_rgb(rgb, amt, mode=mode, preserve_lightness=preserve_lightness)
116
+
117
+ # put processed back into original shape if source had >=3 channels
118
+ if src.ndim == 3 and src.shape[2] > 3:
119
+ out = src.astype(np.float32, copy=True)
120
+ out[..., :3] = processed
121
+ else:
122
+ out = processed
123
+
124
+ # mask-aware blend (mask from destination doc)
125
+ m = _active_mask_array_from_doc(doc)
126
+ if m is not None:
127
+ h, w = out.shape[:2]
128
+ if m.shape != (h, w):
129
+ if cv2 is not None:
130
+ m = cv2.resize(m, (w, h), interpolation=cv2.INTER_NEAREST)
131
+ else:
132
+ yi = (np.linspace(0, m.shape[0]-1, h)).astype(np.int32)
133
+ xi = (np.linspace(0, m.shape[1]-1, w)).astype(np.int32)
134
+ m = m[yi][:, xi]
135
+ if out.ndim == 3:
136
+ m = np.repeat(m[:, :, None], out.shape[2], axis=2)
137
+ src_f = _to_float01(src)
138
+ out = np.clip(src_f * (1.0 - m) + out * m, 0.0, 1.0)
139
+
140
+ meta = {
141
+ "step_name": "Remove Green",
142
+ "remove_green": {
143
+ "amount": amt,
144
+ "mode": mode,
145
+ "preserve_lightness": bool(preserve_lightness),
146
+ "mode_label": _SCNR_MODE_LABELS.get(mode, "Average(R,B)"),
147
+ },
148
+ "bit_depth": "32-bit floating point",
149
+ "is_mono": (out.ndim == 2),
150
+ }
151
+ doc.apply_edit(out.astype(np.float32, copy=False), metadata=meta, step_name="Remove Green")
152
+
153
+ # ---------- dialog ----------
154
+ class RemoveGreenDialog(QDialog):
155
+ def __init__(self, main, doc, parent=None):
156
+ super().__init__(parent)
157
+ self.main = main
158
+ self.doc = doc
159
+ self.setWindowTitle(self.tr("Remove Green (SCNR)"))
160
+ self.setWindowFlag(Qt.WindowType.Window, True)
161
+ self.setWindowModality(Qt.WindowModality.NonModal)
162
+ self.setModal(False)
163
+ self._build_ui()
164
+
165
+ def _build_ui(self):
166
+ lay = QVBoxLayout(self)
167
+ lay.addWidget(QLabel(self.tr("Select the amount to remove green noise:")))
168
+
169
+ # amount
170
+ self.slider = QSlider(Qt.Orientation.Horizontal)
171
+ self.slider.setRange(0, 100)
172
+ self.slider.setValue(100)
173
+ self.slider.setTickInterval(10)
174
+ self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
175
+ self.value_label = QLabel("Amount: 1.00")
176
+ self.slider.valueChanged.connect(lambda v: self.value_label.setText(f"Amount: {v/100.0:.2f}"))
177
+ lay.addWidget(self.slider)
178
+ lay.addWidget(self.value_label)
179
+
180
+ # mode dropdown
181
+ row_mode = QHBoxLayout()
182
+ row_mode.addWidget(QLabel(self.tr("Neutral mode:")))
183
+ self.mode_box = QComboBox()
184
+ # order: avg (default), max, min
185
+ self.mode_box.addItem(_SCNR_MODE_LABELS["avg"], userData="avg")
186
+ self.mode_box.addItem(_SCNR_MODE_LABELS["max"], userData="max")
187
+ self.mode_box.addItem(_SCNR_MODE_LABELS["min"], userData="min")
188
+ self.mode_box.setCurrentIndex(0)
189
+ row_mode.addWidget(self.mode_box)
190
+ row_mode.addStretch(1)
191
+ lay.addLayout(row_mode)
192
+
193
+ # preserve lightness
194
+ self.cb_preserve = QCheckBox(self.tr("Preserve lightness"))
195
+ self.cb_preserve.setChecked(True)
196
+ lay.addWidget(self.cb_preserve)
197
+
198
+ # buttons
199
+ row = QHBoxLayout()
200
+ btn_apply = QPushButton(self.tr("Apply")); btn_apply.clicked.connect(self._apply)
201
+ btn_cancel= QPushButton(self.tr("Cancel")); btn_cancel.clicked.connect(self.reject)
202
+ row.addStretch(1); row.addWidget(btn_apply); row.addWidget(btn_cancel)
203
+ lay.addLayout(row)
204
+
205
+ self.resize(460, 200)
206
+
207
+ def set_amount(self, amt: float):
208
+ try:
209
+ self.slider.setValue(int(round(max(0.0, min(1.0, float(amt))) * 100)))
210
+ except Exception:
211
+ pass
212
+
213
+ def set_mode(self, mode: str | None):
214
+ m = (mode or "avg").lower()
215
+ idx = {"avg":0, "max":1, "min":2}.get(m, 0)
216
+ try:
217
+ self.mode_box.setCurrentIndex(idx)
218
+ except Exception:
219
+ pass
220
+
221
+ def set_preserve_lightness(self, preserve: bool | None):
222
+ try:
223
+ self.cb_preserve.setChecked(True if preserve is None else bool(preserve))
224
+ except Exception:
225
+ pass
226
+
227
+ def _apply(self):
228
+ if self.doc is None or getattr(self.doc, "image", None) is None:
229
+ QMessageBox.warning(self, "Remove Green", "No image.")
230
+ return
231
+
232
+ amount = self.slider.value() / 100.0
233
+ mode = self.mode_box.currentData() or "avg"
234
+ preserve = self.cb_preserve.isChecked()
235
+
236
+ # Build a preset dict so headless + replay use the same schema
237
+ preset = {
238
+ "amount": float(amount),
239
+ "mode": str(mode),
240
+ "preserve_lightness": bool(preserve),
241
+ }
242
+
243
+ # Apply to this doc
244
+ remove_green_headless(self.doc, amount=amount, mode=mode, preserve_lightness=preserve)
245
+
246
+ # Log + record for Replay Last Action (if main supports it)
247
+ if hasattr(self.main, "_log"):
248
+ self.main._log(
249
+ f"Remove Green: amount={amount:.2f}, mode={mode}, "
250
+ f"preserve_lightness={preserve}"
251
+ )
252
+
253
+ try:
254
+ # stash last headless-style command
255
+ self.main._last_headless_command = {
256
+ "command_id": "remove_green",
257
+ "preset": dict(preset),
258
+ }
259
+ if hasattr(self.main, "_log"):
260
+ self.main._log(
261
+ f"[Replay] Recorded Remove Green preset "
262
+ f"(amount={amount:.2f}, mode={mode}, "
263
+ f"preserve_lightness={preserve})"
264
+ )
265
+ except Exception:
266
+ # Never let replay bookkeeping kill the dialog
267
+ pass
268
+
269
+ # Dialog stays open so user can apply to other images
270
+ # Refresh document reference for next operation
271
+ self._refresh_document_from_active()
272
+
273
+ def _refresh_document_from_active(self):
274
+ """
275
+ Refresh the dialog's document reference to the currently active document.
276
+ This allows reusing the same dialog on different images.
277
+ """
278
+ try:
279
+ if self.main and hasattr(self.main, "_active_doc"):
280
+ new_doc = self.main._active_doc()
281
+ if new_doc is not None and new_doc is not self.doc:
282
+ self.doc = new_doc
283
+ except Exception:
284
+ pass
285
+
286
+
287
+ # ---------- entry points used by main ----------
288
+ def open_remove_green_dialog(main, doc=None, preset: dict | None = None):
289
+ """
290
+ Open the Remove Green dialog for a specific document.
291
+
292
+ If doc is None, we fall back to main._active_doc for legacy callers.
293
+ """
294
+ from PyQt6.QtWidgets import QMessageBox
295
+
296
+ if doc is None:
297
+ doc = getattr(main, "_active_doc", None)
298
+ if callable(doc):
299
+ doc = doc()
300
+
301
+ if doc is None or getattr(doc, "image", None) is None:
302
+ QMessageBox.information(main, "Remove Green", "Open an image first.")
303
+ return
304
+
305
+ dlg = RemoveGreenDialog(main, doc, parent=main)
306
+
307
+ if preset:
308
+ amt = preset.get("amount", preset.get("strength", preset.get("value", None)))
309
+ if amt is not None:
310
+ dlg.set_amount(float(amt))
311
+
312
+ mode = preset.get("mode", preset.get("neutral_mode"))
313
+ if mode is not None:
314
+ dlg.set_mode(str(mode))
315
+
316
+ preserve = preset.get("preserve_lightness", preset.get("preserve", True))
317
+ dlg.set_preserve_lightness(bool(preserve))
318
+
319
+ dlg.show()
320
+
321
+
322
+ def apply_remove_green_preset_to_doc(main, doc, preset: dict):
323
+ amt = float(preset.get("amount", preset.get("strength", preset.get("value", 1.0))))
324
+ mode = str(preset.get("mode", preset.get("neutral_mode", "avg"))).lower()
325
+ preserve = bool(preset.get("preserve_lightness", preset.get("preserve", True)))
326
+ remove_green_headless(doc, amount=amt, mode=mode, preserve_lightness=preserve)
327
+ if hasattr(main, "_log"):
328
+ name = doc.display_name() if hasattr(doc, "display_name") else "Image"
329
+ main._log(
330
+ f"Remove Green (headless) on '{name}'; amount={amt:.2f}, mode={mode}, preserve_lightness={preserve}"
331
+ )