datalab-platform 0.0.1.dev0__py3-none-any.whl → 1.0.1__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 (496) hide show
  1. datalab/__init__.py +35 -2
  2. datalab/adapters_metadata/__init__.py +31 -0
  3. datalab/adapters_metadata/base_adapter.py +316 -0
  4. datalab/adapters_metadata/common.py +422 -0
  5. datalab/adapters_metadata/geometry_adapter.py +98 -0
  6. datalab/adapters_metadata/table_adapter.py +84 -0
  7. datalab/adapters_plotpy/__init__.py +54 -0
  8. datalab/adapters_plotpy/annotations.py +124 -0
  9. datalab/adapters_plotpy/base.py +110 -0
  10. datalab/adapters_plotpy/converters.py +86 -0
  11. datalab/adapters_plotpy/factories.py +80 -0
  12. datalab/adapters_plotpy/objects/__init__.py +0 -0
  13. datalab/adapters_plotpy/objects/base.py +197 -0
  14. datalab/adapters_plotpy/objects/image.py +157 -0
  15. datalab/adapters_plotpy/objects/scalar.py +565 -0
  16. datalab/adapters_plotpy/objects/signal.py +264 -0
  17. datalab/adapters_plotpy/roi/__init__.py +0 -0
  18. datalab/adapters_plotpy/roi/base.py +146 -0
  19. datalab/adapters_plotpy/roi/factory.py +93 -0
  20. datalab/adapters_plotpy/roi/image.py +207 -0
  21. datalab/adapters_plotpy/roi/signal.py +72 -0
  22. datalab/app.py +98 -0
  23. datalab/config.py +817 -0
  24. datalab/control/__init__.py +0 -0
  25. datalab/control/baseproxy.py +776 -0
  26. datalab/control/proxy.py +343 -0
  27. datalab/control/remote.py +1005 -0
  28. datalab/data/doc/DataLab_en.pdf +0 -0
  29. datalab/data/doc/DataLab_fr.pdf +0 -0
  30. datalab/data/icons/analysis/delete_results.svg +109 -0
  31. datalab/data/icons/analysis/fw1e2.svg +156 -0
  32. datalab/data/icons/analysis/fwhm.svg +156 -0
  33. datalab/data/icons/analysis/histogram.svg +49 -0
  34. datalab/data/icons/analysis/peak_detect.svg +160 -0
  35. datalab/data/icons/analysis/plot_results.svg +151 -0
  36. datalab/data/icons/analysis/show_results.svg +83 -0
  37. datalab/data/icons/analysis/stats.svg +49 -0
  38. datalab/data/icons/analysis.svg +120 -0
  39. datalab/data/icons/apply.svg +3 -0
  40. datalab/data/icons/check_all.svg +15 -0
  41. datalab/data/icons/collapse.svg +44 -0
  42. datalab/data/icons/collapse_selection.svg +63 -0
  43. datalab/data/icons/console.svg +101 -0
  44. datalab/data/icons/create/1d-normal.svg +8 -0
  45. datalab/data/icons/create/1d-poisson.svg +9 -0
  46. datalab/data/icons/create/1d-uniform.svg +8 -0
  47. datalab/data/icons/create/1d-zero.svg +57 -0
  48. datalab/data/icons/create/2d-gaussian.svg +56 -0
  49. datalab/data/icons/create/2d-normal.svg +38 -0
  50. datalab/data/icons/create/2d-poisson.svg +38 -0
  51. datalab/data/icons/create/2d-ramp.svg +90 -0
  52. datalab/data/icons/create/2d-sinc.svg +62 -0
  53. datalab/data/icons/create/2d-uniform.svg +38 -0
  54. datalab/data/icons/create/2d-zero.svg +13 -0
  55. datalab/data/icons/create/checkerboard.svg +39 -0
  56. datalab/data/icons/create/cosine.svg +12 -0
  57. datalab/data/icons/create/exponential.svg +55 -0
  58. datalab/data/icons/create/gaussian.svg +12 -0
  59. datalab/data/icons/create/grating.svg +29 -0
  60. datalab/data/icons/create/linear_chirp.svg +7 -0
  61. datalab/data/icons/create/logistic.svg +7 -0
  62. datalab/data/icons/create/lorentzian.svg +12 -0
  63. datalab/data/icons/create/planck.svg +12 -0
  64. datalab/data/icons/create/polynomial.svg +7 -0
  65. datalab/data/icons/create/pulse.svg +12 -0
  66. datalab/data/icons/create/ring.svg +18 -0
  67. datalab/data/icons/create/sawtooth.svg +7 -0
  68. datalab/data/icons/create/siemens.svg +35 -0
  69. datalab/data/icons/create/sinc.svg +12 -0
  70. datalab/data/icons/create/sine.svg +7 -0
  71. datalab/data/icons/create/square.svg +7 -0
  72. datalab/data/icons/create/square_pulse.svg +7 -0
  73. datalab/data/icons/create/step.svg +7 -0
  74. datalab/data/icons/create/step_pulse.svg +12 -0
  75. datalab/data/icons/create/triangle.svg +7 -0
  76. datalab/data/icons/create/voigt.svg +12 -0
  77. datalab/data/icons/edit/annotations.svg +72 -0
  78. datalab/data/icons/edit/annotations_copy.svg +114 -0
  79. datalab/data/icons/edit/annotations_delete.svg +83 -0
  80. datalab/data/icons/edit/annotations_edit.svg +98 -0
  81. datalab/data/icons/edit/annotations_export.svg +85 -0
  82. datalab/data/icons/edit/annotations_import.svg +85 -0
  83. datalab/data/icons/edit/annotations_paste.svg +100 -0
  84. datalab/data/icons/edit/copy_titles.svg +109 -0
  85. datalab/data/icons/edit/delete.svg +84 -0
  86. datalab/data/icons/edit/delete_all.svg +214 -0
  87. datalab/data/icons/edit/duplicate.svg +64 -0
  88. datalab/data/icons/edit/goto_source.svg +60 -0
  89. datalab/data/icons/edit/metadata.svg +60 -0
  90. datalab/data/icons/edit/metadata_add.svg +80 -0
  91. datalab/data/icons/edit/metadata_copy.svg +96 -0
  92. datalab/data/icons/edit/metadata_delete.svg +62 -0
  93. datalab/data/icons/edit/metadata_export.svg +68 -0
  94. datalab/data/icons/edit/metadata_import.svg +68 -0
  95. datalab/data/icons/edit/metadata_paste.svg +79 -0
  96. datalab/data/icons/edit/move_down.svg +55 -0
  97. datalab/data/icons/edit/move_up.svg +54 -0
  98. datalab/data/icons/edit/new_group.svg +76 -0
  99. datalab/data/icons/edit/recompute.svg +60 -0
  100. datalab/data/icons/edit/rename.svg +49 -0
  101. datalab/data/icons/edit.svg +16 -0
  102. datalab/data/icons/expand.svg +44 -0
  103. datalab/data/icons/expand_selection.svg +63 -0
  104. datalab/data/icons/fit/cdf_fit.svg +56 -0
  105. datalab/data/icons/fit/exponential_fit.svg +55 -0
  106. datalab/data/icons/fit/gaussian_fit.svg +62 -0
  107. datalab/data/icons/fit/interactive_fit.svg +101 -0
  108. datalab/data/icons/fit/linear_fit.svg +57 -0
  109. datalab/data/icons/fit/lorentzian_fit.svg +209 -0
  110. datalab/data/icons/fit/multigaussian_fit.svg +85 -0
  111. datalab/data/icons/fit/multilorentzian_fit.svg +85 -0
  112. datalab/data/icons/fit/piecewiseexponential_fit.svg +209 -0
  113. datalab/data/icons/fit/planckian_fit.svg +62 -0
  114. datalab/data/icons/fit/polynomial_fit.svg +59 -0
  115. datalab/data/icons/fit/sigmoid_fit.svg +56 -0
  116. datalab/data/icons/fit/sinusoidal_fit.svg +72 -0
  117. datalab/data/icons/fit/twohalfgaussian_fit.svg +63 -0
  118. datalab/data/icons/fit/voigt_fit.svg +57 -0
  119. datalab/data/icons/group.svg +56 -0
  120. datalab/data/icons/h5/h5array.svg +59 -0
  121. datalab/data/icons/h5/h5attrs.svg +75 -0
  122. datalab/data/icons/h5/h5browser.svg +133 -0
  123. datalab/data/icons/h5/h5file.svg +69 -0
  124. datalab/data/icons/h5/h5group.svg +49 -0
  125. datalab/data/icons/h5/h5scalar.svg +1 -0
  126. datalab/data/icons/help_pdf.svg +46 -0
  127. datalab/data/icons/history.svg +7 -0
  128. datalab/data/icons/image.svg +135 -0
  129. datalab/data/icons/io/fileopen_directory.svg +60 -0
  130. datalab/data/icons/io/fileopen_h5.svg +84 -0
  131. datalab/data/icons/io/fileopen_ima.svg +187 -0
  132. datalab/data/icons/io/fileopen_py.svg +123 -0
  133. datalab/data/icons/io/fileopen_sig.svg +138 -0
  134. datalab/data/icons/io/filesave_h5.svg +97 -0
  135. datalab/data/icons/io/filesave_ima.svg +200 -0
  136. datalab/data/icons/io/filesave_py.svg +136 -0
  137. datalab/data/icons/io/filesave_sig.svg +151 -0
  138. datalab/data/icons/io/import_text.svg +144 -0
  139. datalab/data/icons/io/save_to_directory.svg +134 -0
  140. datalab/data/icons/io.svg +84 -0
  141. datalab/data/icons/libre-camera-flash-off.svg +1 -0
  142. datalab/data/icons/libre-camera-flash-on.svg +1 -0
  143. datalab/data/icons/libre-gui-about.svg +1 -0
  144. datalab/data/icons/libre-gui-action-delete.svg +1 -0
  145. datalab/data/icons/libre-gui-add.svg +1 -0
  146. datalab/data/icons/libre-gui-arrow-down.svg +1 -0
  147. datalab/data/icons/libre-gui-arrow-left.svg +1 -0
  148. datalab/data/icons/libre-gui-arrow-right.svg +1 -0
  149. datalab/data/icons/libre-gui-arrow-up.svg +1 -0
  150. datalab/data/icons/libre-gui-close.svg +40 -0
  151. datalab/data/icons/libre-gui-cogs.svg +1 -0
  152. datalab/data/icons/libre-gui-globe.svg +1 -0
  153. datalab/data/icons/libre-gui-help.svg +1 -0
  154. datalab/data/icons/libre-gui-link.svg +1 -0
  155. datalab/data/icons/libre-gui-menu.svg +1 -0
  156. datalab/data/icons/libre-gui-pencil.svg +1 -0
  157. datalab/data/icons/libre-gui-plugin.svg +1 -0
  158. datalab/data/icons/libre-gui-questions.svg +1 -0
  159. datalab/data/icons/libre-gui-settings.svg +1 -0
  160. datalab/data/icons/libre-gui-unlink.svg +1 -0
  161. datalab/data/icons/libre-tech-ram.svg +1 -0
  162. datalab/data/icons/libre-toolbox.svg +1 -0
  163. datalab/data/icons/logs.svg +1 -0
  164. datalab/data/icons/markers.svg +74 -0
  165. datalab/data/icons/menu.svg +13 -0
  166. datalab/data/icons/new_ima.svg +148 -0
  167. datalab/data/icons/new_sig.svg +123 -0
  168. datalab/data/icons/operations/abs.svg +116 -0
  169. datalab/data/icons/operations/arithmetic.svg +123 -0
  170. datalab/data/icons/operations/average.svg +124 -0
  171. datalab/data/icons/operations/complex_from_magnitude_phase.svg +116 -0
  172. datalab/data/icons/operations/complex_from_real_imag.svg +124 -0
  173. datalab/data/icons/operations/constant.svg +116 -0
  174. datalab/data/icons/operations/constant_add.svg +109 -0
  175. datalab/data/icons/operations/constant_divide.svg +109 -0
  176. datalab/data/icons/operations/constant_multiply.svg +109 -0
  177. datalab/data/icons/operations/constant_subtract.svg +109 -0
  178. datalab/data/icons/operations/convert_dtype.svg +117 -0
  179. datalab/data/icons/operations/convolution.svg +46 -0
  180. datalab/data/icons/operations/deconvolution.svg +57 -0
  181. datalab/data/icons/operations/derivative.svg +127 -0
  182. datalab/data/icons/operations/difference.svg +52 -0
  183. datalab/data/icons/operations/division.svg +139 -0
  184. datalab/data/icons/operations/exp.svg +116 -0
  185. datalab/data/icons/operations/flip_horizontally.svg +69 -0
  186. datalab/data/icons/operations/flip_vertically.svg +74 -0
  187. datalab/data/icons/operations/im.svg +124 -0
  188. datalab/data/icons/operations/integral.svg +50 -0
  189. datalab/data/icons/operations/inverse.svg +143 -0
  190. datalab/data/icons/operations/log10.svg +109 -0
  191. datalab/data/icons/operations/phase.svg +116 -0
  192. datalab/data/icons/operations/power.svg +118 -0
  193. datalab/data/icons/operations/product.svg +124 -0
  194. datalab/data/icons/operations/profile.svg +379 -0
  195. datalab/data/icons/operations/profile_average.svg +399 -0
  196. datalab/data/icons/operations/profile_radial.svg +261 -0
  197. datalab/data/icons/operations/profile_segment.svg +262 -0
  198. datalab/data/icons/operations/quadratic_difference.svg +84 -0
  199. datalab/data/icons/operations/re.svg +124 -0
  200. datalab/data/icons/operations/rotate_left.svg +72 -0
  201. datalab/data/icons/operations/rotate_right.svg +72 -0
  202. datalab/data/icons/operations/signals_to_image.svg +314 -0
  203. datalab/data/icons/operations/sqrt.svg +110 -0
  204. datalab/data/icons/operations/std.svg +124 -0
  205. datalab/data/icons/operations/sum.svg +102 -0
  206. datalab/data/icons/play_demo.svg +9 -0
  207. datalab/data/icons/processing/axis_transform.svg +62 -0
  208. datalab/data/icons/processing/bandpass.svg +79 -0
  209. datalab/data/icons/processing/bandstop.svg +71 -0
  210. datalab/data/icons/processing/binning.svg +126 -0
  211. datalab/data/icons/processing/clip.svg +119 -0
  212. datalab/data/icons/processing/detrending.svg +173 -0
  213. datalab/data/icons/processing/distribute_on_grid.svg +769 -0
  214. datalab/data/icons/processing/edge_detection.svg +46 -0
  215. datalab/data/icons/processing/erase.svg +1 -0
  216. datalab/data/icons/processing/exposure.svg +143 -0
  217. datalab/data/icons/processing/fourier.svg +104 -0
  218. datalab/data/icons/processing/highpass.svg +59 -0
  219. datalab/data/icons/processing/interpolation.svg +71 -0
  220. datalab/data/icons/processing/level_adjustment.svg +70 -0
  221. datalab/data/icons/processing/lowpass.svg +60 -0
  222. datalab/data/icons/processing/morphology.svg +49 -0
  223. datalab/data/icons/processing/noise_addition.svg +114 -0
  224. datalab/data/icons/processing/noise_reduction.svg +38 -0
  225. datalab/data/icons/processing/normalize.svg +84 -0
  226. datalab/data/icons/processing/offset_correction.svg +131 -0
  227. datalab/data/icons/processing/resampling1d.svg +101 -0
  228. datalab/data/icons/processing/resampling2d.svg +240 -0
  229. datalab/data/icons/processing/reset_positions.svg +185 -0
  230. datalab/data/icons/processing/resize.svg +9 -0
  231. datalab/data/icons/processing/reverse_signal_x.svg +171 -0
  232. datalab/data/icons/processing/stability.svg +11 -0
  233. datalab/data/icons/processing/swap_x_y.svg +65 -0
  234. datalab/data/icons/processing/thresholding.svg +63 -0
  235. datalab/data/icons/processing/windowing.svg +45 -0
  236. datalab/data/icons/properties.svg +26 -0
  237. datalab/data/icons/reset.svg +9 -0
  238. datalab/data/icons/restore.svg +40 -0
  239. datalab/data/icons/roi/roi.svg +76 -0
  240. datalab/data/icons/roi/roi_coordinate.svg +78 -0
  241. datalab/data/icons/roi/roi_copy.svg +112 -0
  242. datalab/data/icons/roi/roi_delete.svg +81 -0
  243. datalab/data/icons/roi/roi_export.svg +87 -0
  244. datalab/data/icons/roi/roi_graphical.svg +78 -0
  245. datalab/data/icons/roi/roi_grid.svg +67 -0
  246. datalab/data/icons/roi/roi_ima.svg +188 -0
  247. datalab/data/icons/roi/roi_import.svg +87 -0
  248. datalab/data/icons/roi/roi_new.svg +81 -0
  249. datalab/data/icons/roi/roi_new_circle.svg +95 -0
  250. datalab/data/icons/roi/roi_new_polygon.svg +110 -0
  251. datalab/data/icons/roi/roi_new_rectangle.svg +70 -0
  252. datalab/data/icons/roi/roi_paste.svg +98 -0
  253. datalab/data/icons/roi/roi_sig.svg +124 -0
  254. datalab/data/icons/shapes.svg +134 -0
  255. datalab/data/icons/signal.svg +103 -0
  256. datalab/data/icons/table.svg +85 -0
  257. datalab/data/icons/table_unavailable.svg +102 -0
  258. datalab/data/icons/to_signal.svg +124 -0
  259. datalab/data/icons/tour/next.svg +44 -0
  260. datalab/data/icons/tour/previous.svg +44 -0
  261. datalab/data/icons/tour/rewind.svg +51 -0
  262. datalab/data/icons/tour/stop.svg +47 -0
  263. datalab/data/icons/tour/tour.svg +16 -0
  264. datalab/data/icons/uncheck_all.svg +78 -0
  265. datalab/data/icons/view/curve_antialiasing.svg +50 -0
  266. datalab/data/icons/view/new_window.svg +98 -0
  267. datalab/data/icons/view/refresh-auto.svg +57 -0
  268. datalab/data/icons/view/refresh-manual.svg +51 -0
  269. datalab/data/icons/view/reset_curve_styles.svg +96 -0
  270. datalab/data/icons/view/show_first.svg +55 -0
  271. datalab/data/icons/view/show_titles.svg +46 -0
  272. datalab/data/icons/visualization.svg +51 -0
  273. datalab/data/logo/DataLab-Banner-150.png +0 -0
  274. datalab/data/logo/DataLab-Banner-200.png +0 -0
  275. datalab/data/logo/DataLab-Banner2-100.png +0 -0
  276. datalab/data/logo/DataLab-Splash.png +0 -0
  277. datalab/data/logo/DataLab-watermark.png +0 -0
  278. datalab/data/logo/DataLab.svg +83 -0
  279. datalab/data/tests/reordering_test.h5 +0 -0
  280. datalab/data/tutorials/fabry_perot/fabry-perot1.jpg +0 -0
  281. datalab/data/tutorials/fabry_perot/fabry-perot2.jpg +0 -0
  282. datalab/data/tutorials/laser_beam/TEM00_z_13.jpg +0 -0
  283. datalab/data/tutorials/laser_beam/TEM00_z_18.jpg +0 -0
  284. datalab/data/tutorials/laser_beam/TEM00_z_23.jpg +0 -0
  285. datalab/data/tutorials/laser_beam/TEM00_z_30.jpg +0 -0
  286. datalab/data/tutorials/laser_beam/TEM00_z_35.jpg +0 -0
  287. datalab/data/tutorials/laser_beam/TEM00_z_40.jpg +0 -0
  288. datalab/data/tutorials/laser_beam/TEM00_z_45.jpg +0 -0
  289. datalab/data/tutorials/laser_beam/TEM00_z_50.jpg +0 -0
  290. datalab/data/tutorials/laser_beam/TEM00_z_55.jpg +0 -0
  291. datalab/data/tutorials/laser_beam/TEM00_z_60.jpg +0 -0
  292. datalab/data/tutorials/laser_beam/TEM00_z_65.jpg +0 -0
  293. datalab/data/tutorials/laser_beam/TEM00_z_70.jpg +0 -0
  294. datalab/data/tutorials/laser_beam/TEM00_z_75.jpg +0 -0
  295. datalab/data/tutorials/laser_beam/TEM00_z_80.jpg +0 -0
  296. datalab/env.py +542 -0
  297. datalab/gui/__init__.py +89 -0
  298. datalab/gui/actionhandler.py +1701 -0
  299. datalab/gui/docks.py +473 -0
  300. datalab/gui/h5io.py +150 -0
  301. datalab/gui/macroeditor.py +310 -0
  302. datalab/gui/main.py +2081 -0
  303. datalab/gui/newobject.py +217 -0
  304. datalab/gui/objectview.py +766 -0
  305. datalab/gui/panel/__init__.py +48 -0
  306. datalab/gui/panel/base.py +3254 -0
  307. datalab/gui/panel/image.py +157 -0
  308. datalab/gui/panel/macro.py +607 -0
  309. datalab/gui/panel/signal.py +164 -0
  310. datalab/gui/plothandler.py +800 -0
  311. datalab/gui/processor/__init__.py +84 -0
  312. datalab/gui/processor/base.py +2456 -0
  313. datalab/gui/processor/catcher.py +75 -0
  314. datalab/gui/processor/image.py +1214 -0
  315. datalab/gui/processor/signal.py +755 -0
  316. datalab/gui/profiledialog.py +333 -0
  317. datalab/gui/roieditor.py +633 -0
  318. datalab/gui/roigrideditor.py +208 -0
  319. datalab/gui/settings.py +612 -0
  320. datalab/gui/tour.py +908 -0
  321. datalab/h5/__init__.py +12 -0
  322. datalab/h5/common.py +314 -0
  323. datalab/h5/generic.py +580 -0
  324. datalab/h5/native.py +39 -0
  325. datalab/h5/utils.py +95 -0
  326. datalab/objectmodel.py +640 -0
  327. datalab/plugins/_readme_.txt +9 -0
  328. datalab/plugins/datalab_imageformats.py +175 -0
  329. datalab/plugins/datalab_testdata.py +190 -0
  330. datalab/plugins.py +355 -0
  331. datalab/tests/__init__.py +199 -0
  332. datalab/tests/backbone/__init__.py +1 -0
  333. datalab/tests/backbone/config_unit_test.py +170 -0
  334. datalab/tests/backbone/config_versioning_unit_test.py +34 -0
  335. datalab/tests/backbone/dictlistserial_app_test.py +38 -0
  336. datalab/tests/backbone/errorcatcher_unit_test.py +69 -0
  337. datalab/tests/backbone/errormsgbox_unit_test.py +50 -0
  338. datalab/tests/backbone/execenv_unit.py +262 -0
  339. datalab/tests/backbone/loadtest_gdi.py +147 -0
  340. datalab/tests/backbone/long_callback.py +96 -0
  341. datalab/tests/backbone/main_app_test.py +137 -0
  342. datalab/tests/backbone/memory_leak.py +43 -0
  343. datalab/tests/backbone/procisolation1_unit.py +128 -0
  344. datalab/tests/backbone/procisolation2_unit.py +171 -0
  345. datalab/tests/backbone/procisolation_unit_test.py +22 -0
  346. datalab/tests/backbone/profiling_app.py +27 -0
  347. datalab/tests/backbone/strings_unit_test.py +65 -0
  348. datalab/tests/backbone/title_formatting_unit_test.py +82 -0
  349. datalab/tests/conftest.py +131 -0
  350. datalab/tests/features/__init__.py +1 -0
  351. datalab/tests/features/applauncher/__init__.py +1 -0
  352. datalab/tests/features/applauncher/launcher1_app_test.py +28 -0
  353. datalab/tests/features/applauncher/launcher2_app_test.py +30 -0
  354. datalab/tests/features/common/__init__.py +1 -0
  355. datalab/tests/features/common/add_metadata_app_test.py +134 -0
  356. datalab/tests/features/common/add_metadata_unit_test.py +267 -0
  357. datalab/tests/features/common/annotations_management_unit_test.py +152 -0
  358. datalab/tests/features/common/auto_analysis_recompute_unit_test.py +240 -0
  359. datalab/tests/features/common/createobject_unit_test.py +50 -0
  360. datalab/tests/features/common/geometry_results_app_test.py +135 -0
  361. datalab/tests/features/common/interactive_processing_test.py +1109 -0
  362. datalab/tests/features/common/io_app_test.py +75 -0
  363. datalab/tests/features/common/large_results_app_test.py +187 -0
  364. datalab/tests/features/common/metadata_all_patterns_test.py +103 -0
  365. datalab/tests/features/common/metadata_app_test.py +139 -0
  366. datalab/tests/features/common/metadata_io_unit_test.py +60 -0
  367. datalab/tests/features/common/misc_app_test.py +236 -0
  368. datalab/tests/features/common/multiple_geometry_results_unit_test.py +122 -0
  369. datalab/tests/features/common/multiple_table_results_unit_test.py +64 -0
  370. datalab/tests/features/common/operation_modes_app_test.py +392 -0
  371. datalab/tests/features/common/plot_results_app_test.py +278 -0
  372. datalab/tests/features/common/reorder_app_test.py +75 -0
  373. datalab/tests/features/common/result_deletion_unit_test.py +96 -0
  374. datalab/tests/features/common/result_merged_label_unit_test.py +154 -0
  375. datalab/tests/features/common/result_shape_settings_unit_test.py +223 -0
  376. datalab/tests/features/common/roi_plotitem_unit_test.py +64 -0
  377. datalab/tests/features/common/roieditor_unit_test.py +102 -0
  378. datalab/tests/features/common/save_to_dir_app_test.py +163 -0
  379. datalab/tests/features/common/save_to_dir_unit_test.py +474 -0
  380. datalab/tests/features/common/stat_app_test.py +40 -0
  381. datalab/tests/features/common/stats_tools_unit_test.py +77 -0
  382. datalab/tests/features/common/table_results_app_test.py +52 -0
  383. datalab/tests/features/common/textimport_unit_test.py +131 -0
  384. datalab/tests/features/common/uuid_preservation_test.py +281 -0
  385. datalab/tests/features/common/worker_unit_test.py +402 -0
  386. datalab/tests/features/control/__init__.py +1 -0
  387. datalab/tests/features/control/connect_dialog.py +28 -0
  388. datalab/tests/features/control/embedded1_unit_test.py +304 -0
  389. datalab/tests/features/control/embedded2_unit_test.py +52 -0
  390. datalab/tests/features/control/remoteclient_app_test.py +219 -0
  391. datalab/tests/features/control/remoteclient_unit.py +75 -0
  392. datalab/tests/features/control/simpleclient_unit_test.py +321 -0
  393. datalab/tests/features/hdf5/__init__.py +1 -0
  394. datalab/tests/features/hdf5/h5browser1_unit_test.py +31 -0
  395. datalab/tests/features/hdf5/h5browser2_unit.py +55 -0
  396. datalab/tests/features/hdf5/h5browser_app_test.py +77 -0
  397. datalab/tests/features/hdf5/h5import_app_test.py +25 -0
  398. datalab/tests/features/hdf5/h5importer_app_test.py +34 -0
  399. datalab/tests/features/image/__init__.py +1 -0
  400. datalab/tests/features/image/annotations_app_test.py +28 -0
  401. datalab/tests/features/image/annotations_unit_test.py +80 -0
  402. datalab/tests/features/image/average_app_test.py +46 -0
  403. datalab/tests/features/image/background_dialog_test.py +70 -0
  404. datalab/tests/features/image/blobs_app_test.py +50 -0
  405. datalab/tests/features/image/contour_app_test.py +42 -0
  406. datalab/tests/features/image/contour_fabryperot_app_test.py +51 -0
  407. datalab/tests/features/image/denoise_app_test.py +31 -0
  408. datalab/tests/features/image/distribute_on_grid_app_test.py +95 -0
  409. datalab/tests/features/image/edges_app_test.py +31 -0
  410. datalab/tests/features/image/erase_app_test.py +21 -0
  411. datalab/tests/features/image/fft2d_app_test.py +27 -0
  412. datalab/tests/features/image/flatfield_app_test.py +40 -0
  413. datalab/tests/features/image/geometry_transform_unit_test.py +396 -0
  414. datalab/tests/features/image/imagetools_app_test.py +51 -0
  415. datalab/tests/features/image/imagetools_unit_test.py +27 -0
  416. datalab/tests/features/image/load_app_test.py +73 -0
  417. datalab/tests/features/image/morph_app_test.py +32 -0
  418. datalab/tests/features/image/offsetcorrection_app_test.py +30 -0
  419. datalab/tests/features/image/peak2d_app_test.py +53 -0
  420. datalab/tests/features/image/profile_app_test.py +73 -0
  421. datalab/tests/features/image/profile_dialog_test.py +56 -0
  422. datalab/tests/features/image/roi_app_test.py +98 -0
  423. datalab/tests/features/image/roi_circ_app_test.py +62 -0
  424. datalab/tests/features/image/roi_manipulation_app_test.py +268 -0
  425. datalab/tests/features/image/roigrid_unit_test.py +60 -0
  426. datalab/tests/features/image/side_by_side_app_test.py +52 -0
  427. datalab/tests/features/macro/__init__.py +1 -0
  428. datalab/tests/features/macro/macro_app_test.py +28 -0
  429. datalab/tests/features/macro/macroeditor_unit_test.py +102 -0
  430. datalab/tests/features/signal/__init__.py +1 -0
  431. datalab/tests/features/signal/baseline_dialog_test.py +53 -0
  432. datalab/tests/features/signal/deltax_dialog_unit_test.py +34 -0
  433. datalab/tests/features/signal/fft1d_app_test.py +26 -0
  434. datalab/tests/features/signal/filter_app_test.py +44 -0
  435. datalab/tests/features/signal/fitdialog_unit_test.py +50 -0
  436. datalab/tests/features/signal/interpolation_app_test.py +110 -0
  437. datalab/tests/features/signal/loadbigsignal_app_test.py +80 -0
  438. datalab/tests/features/signal/multiple_rois_unit_test.py +132 -0
  439. datalab/tests/features/signal/pulse_features_app_test.py +118 -0
  440. datalab/tests/features/signal/pulse_features_roi_app_test.py +55 -0
  441. datalab/tests/features/signal/roi_app_test.py +78 -0
  442. datalab/tests/features/signal/roi_manipulation_app_test.py +261 -0
  443. datalab/tests/features/signal/select_xy_cursor_unit_test.py +46 -0
  444. datalab/tests/features/signal/signalpeakdetection_dialog_test.py +33 -0
  445. datalab/tests/features/signal/signals_to_image_app_test.py +98 -0
  446. datalab/tests/features/signal/xarray_compat_app_test.py +128 -0
  447. datalab/tests/features/tour_unit_test.py +22 -0
  448. datalab/tests/features/utilities/__init__.py +1 -0
  449. datalab/tests/features/utilities/installconf_unit_test.py +21 -0
  450. datalab/tests/features/utilities/logview_app_test.py +21 -0
  451. datalab/tests/features/utilities/logview_error.py +24 -0
  452. datalab/tests/features/utilities/logview_unit_test.py +21 -0
  453. datalab/tests/features/utilities/memstatus_app_test.py +42 -0
  454. datalab/tests/features/utilities/settings_unit_test.py +88 -0
  455. datalab/tests/scenarios/__init__.py +1 -0
  456. datalab/tests/scenarios/beautiful_app.py +121 -0
  457. datalab/tests/scenarios/common.py +463 -0
  458. datalab/tests/scenarios/demo.py +212 -0
  459. datalab/tests/scenarios/example_app_test.py +47 -0
  460. datalab/tests/scenarios/scenario_h5_app_test.py +75 -0
  461. datalab/tests/scenarios/scenario_ima1_app_test.py +34 -0
  462. datalab/tests/scenarios/scenario_ima2_app_test.py +34 -0
  463. datalab/tests/scenarios/scenario_mac_app_test.py +58 -0
  464. datalab/tests/scenarios/scenario_sig1_app_test.py +36 -0
  465. datalab/tests/scenarios/scenario_sig2_app_test.py +35 -0
  466. datalab/utils/__init__.py +1 -0
  467. datalab/utils/conf.py +304 -0
  468. datalab/utils/dephash.py +105 -0
  469. datalab/utils/qthelpers.py +633 -0
  470. datalab/utils/strings.py +34 -0
  471. datalab/utils/tests.py +0 -0
  472. datalab/widgets/__init__.py +1 -0
  473. datalab/widgets/connection.py +138 -0
  474. datalab/widgets/filedialog.py +91 -0
  475. datalab/widgets/fileviewer.py +84 -0
  476. datalab/widgets/fitdialog.py +788 -0
  477. datalab/widgets/h5browser.py +1048 -0
  478. datalab/widgets/imagebackground.py +111 -0
  479. datalab/widgets/instconfviewer.py +175 -0
  480. datalab/widgets/logviewer.py +80 -0
  481. datalab/widgets/signalbaseline.py +90 -0
  482. datalab/widgets/signalcursor.py +208 -0
  483. datalab/widgets/signaldeltax.py +151 -0
  484. datalab/widgets/signalpeak.py +199 -0
  485. datalab/widgets/status.py +249 -0
  486. datalab/widgets/textimport.py +786 -0
  487. datalab/widgets/warningerror.py +223 -0
  488. datalab/widgets/wizard.py +286 -0
  489. datalab_platform-1.0.1.dist-info/METADATA +121 -0
  490. datalab_platform-1.0.1.dist-info/RECORD +494 -0
  491. datalab_platform-0.0.1.dev0.dist-info/METADATA +0 -67
  492. datalab_platform-0.0.1.dev0.dist-info/RECORD +0 -7
  493. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/WHEEL +0 -0
  494. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/entry_points.txt +0 -0
  495. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/licenses/LICENSE +0 -0
  496. {datalab_platform-0.0.1.dev0.dist-info → datalab_platform-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1214 @@
1
+ # Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2
+
3
+ """
4
+ .. Image processor object (see parent package :mod:`datalab.gui.processor`)
5
+ """
6
+
7
+ # pylint: disable=invalid-name # Allows short reference names like x, y, ...
8
+
9
+ from __future__ import annotations
10
+
11
+ import importlib
12
+ from typing import Literal
13
+
14
+ import numpy as np
15
+ import sigima.params
16
+ import sigima.proc.base as sipb
17
+ import sigima.proc.image as sipi
18
+ from guidata.qthelpers import exec_dialog
19
+ from plotpy.widgets.resizedialog import ResizeDialog
20
+ from qtpy import QtWidgets as QW
21
+ from sigima.objects import (
22
+ ImageObj,
23
+ ImageROI,
24
+ NormalDistributionParam,
25
+ PoissonDistributionParam,
26
+ ROI2DParam,
27
+ UniformDistributionParam,
28
+ )
29
+ from sigima.objects.scalar import GeometryResult, TableResult
30
+ from sigima.proc.decorator import ComputationMetadata
31
+
32
+ from datalab.adapters_metadata import GeometryAdapter
33
+ from datalab.config import APP_NAME, _
34
+ from datalab.gui.processor.base import BaseProcessor
35
+ from datalab.gui.profiledialog import ProfileExtractionDialog
36
+ from datalab.gui.roigrideditor import ImageGridROIEditor
37
+ from datalab.objectmodel import get_uuid
38
+ from datalab.utils.qthelpers import create_progress_bar, qt_try_except
39
+ from datalab.widgets import imagebackground
40
+
41
+
42
+ def apply_geometry_transform(
43
+ obj: ImageObj,
44
+ operation: Literal[
45
+ "translate", "scale", "rotate90", "rotate270", "fliph", "flipv", "transpose"
46
+ ],
47
+ **kwargs,
48
+ ) -> None:
49
+ """Apply a geometric transformation to all geometry results in an object.
50
+
51
+ This uses the Sigima transformation system for proper geometric operations.
52
+ For image objects, rotations are performed around the image center to match
53
+ how the image data is transformed.
54
+
55
+ Args:
56
+ obj: The object containing geometry results to transform
57
+ operation: The transformation operation name
58
+ **kwargs: Optional parameters for the transformation (e.g., angle for rotate)
59
+ """
60
+ assert operation in [
61
+ "translate",
62
+ "scale",
63
+ "rotate90",
64
+ "rotate270",
65
+ "fliph",
66
+ "flipv",
67
+ "transpose",
68
+ ], f"Unknown operation: {operation}"
69
+ if operation == "translate":
70
+ if not kwargs or "dx" not in kwargs or "dy" not in kwargs:
71
+ raise ValueError("translate operation requires 'dx' and 'dy' parameters")
72
+ dx, dy = kwargs["dx"], kwargs["dy"]
73
+ elif operation == "scale":
74
+ if not kwargs or "sx" not in kwargs or "sy" not in kwargs:
75
+ raise ValueError("scale operation requires 'sx' and 'sy' parameters")
76
+ sx, sy = kwargs["sx"], kwargs["sy"]
77
+ for adapter in list(GeometryAdapter.iterate_from_obj(obj)):
78
+ geom = adapter.result
79
+ assert geom is not None, "Geometry should not be None"
80
+ assert len(geom.coords) > 0, "Geometry coordinates should not be empty"
81
+ if operation == "translate":
82
+ tr_geom = sipi.transformer.translate(geom, dx, dy)
83
+ elif operation == "scale":
84
+ tr_geom = sipi.transformer.scale(geom, sx, sy, (obj.xc, obj.yc))
85
+ elif operation == "rotate90":
86
+ tr_geom = sipi.transformer.rotate(geom, -np.pi / 2, (obj.xc, obj.yc))
87
+ elif operation == "rotate270":
88
+ tr_geom = sipi.transformer.rotate(geom, np.pi / 2, (obj.xc, obj.yc))
89
+ elif operation == "fliph":
90
+ tr_geom = sipi.transformer.fliph(geom, obj.xc)
91
+ elif operation == "flipv":
92
+ tr_geom = sipi.transformer.flipv(geom, obj.yc)
93
+ elif operation == "transpose":
94
+ tr_geom = sipi.transformer.transpose(geom)
95
+ else:
96
+ raise ValueError(f"Unknown operation: {operation}")
97
+
98
+ # Remove the old geometry and add the transformed one
99
+ adapter.remove_from(obj)
100
+ tr_adapter = GeometryAdapter(tr_geom)
101
+ tr_adapter.add_to(obj)
102
+
103
+
104
+ class GeometricTransformWrapper:
105
+ """Pickleable wrapper for geometric transformation functions.
106
+
107
+ This class creates a callable wrapper that can be pickled, unlike nested functions.
108
+ Instead of storing the function directly, it stores the module path and function
109
+ name to allow proper pickling.
110
+ """
111
+
112
+ def __init__(self, func, operation: str):
113
+ self.operation = operation
114
+
115
+ # Store function reference for execution
116
+ self.func = func
117
+
118
+ # Store function module and name for pickling
119
+ self.__module__ = func.__module__
120
+ self.__qualname__ = func.__qualname__
121
+ self.__annotations__ = getattr(func, "__annotations__", {})
122
+ self.__name__ = getattr(func, "__name__", str(func))
123
+
124
+ # Copy the __wrapped__ attribute if it exists (for Sigima compatibility)
125
+ # Note: We don't copy __wrapped__ as it may contain unpickleable references
126
+ # The wrapper functionality will still work without it
127
+
128
+ # Copy Sigima computation metadata (required for validation)
129
+ computation_metadata_attr = "__computation_function_metadata"
130
+ if hasattr(func, computation_metadata_attr):
131
+ setattr(
132
+ self,
133
+ computation_metadata_attr,
134
+ getattr(func, computation_metadata_attr),
135
+ )
136
+
137
+ def __call__(self, src_obj, param=None):
138
+ """Call the wrapped function and apply geometry transformations."""
139
+ # Call the original function
140
+ if param is not None:
141
+ dst_obj = self.func(src_obj, param)
142
+ else:
143
+ dst_obj = self.func(src_obj)
144
+ apply_geometry_transform(dst_obj, operation=self.operation)
145
+ return dst_obj
146
+
147
+ def __getstate__(self):
148
+ """Custom pickling: exclude the function reference."""
149
+ # Build state manually to avoid any problematic attributes
150
+ state = {
151
+ "operation": self.operation,
152
+ "__module__": self.__module__,
153
+ "__qualname__": self.__qualname__,
154
+ "__annotations__": self.__annotations__,
155
+ "__name__": self.__name__,
156
+ }
157
+
158
+ # Store function information for reconstruction
159
+ if hasattr(self, "func"):
160
+ state["_func_module"] = self.func.__module__
161
+ state["_func_name"] = self.func.__name__
162
+
163
+ # Note: We don't copy __wrapped__ as it may contain unpickleable references
164
+
165
+ # Copy computation metadata safely
166
+ computation_metadata_attr = "__computation_function_metadata"
167
+ if hasattr(self, computation_metadata_attr):
168
+ metadata = getattr(self, computation_metadata_attr)
169
+ # Store as a dict to avoid any pickling issues with the object itself
170
+ if hasattr(metadata, "__dict__"):
171
+ state[computation_metadata_attr] = metadata.__dict__.copy()
172
+ else:
173
+ state[computation_metadata_attr] = metadata
174
+
175
+ return state
176
+
177
+ def __setstate__(self, state):
178
+ """Custom unpickling: restore the function reference."""
179
+ self.__dict__.update(state)
180
+ # Restore function from module and name
181
+ if "_func_module" in state and "_func_name" in state:
182
+ module = importlib.import_module(state["_func_module"])
183
+ self.func = getattr(module, state["_func_name"])
184
+
185
+ # Reconstruct computation metadata if it was stored as dict
186
+ computation_metadata_attr = "__computation_function_metadata"
187
+ if computation_metadata_attr in state:
188
+ metadata_dict = state[computation_metadata_attr]
189
+ if isinstance(metadata_dict, dict):
190
+ metadata = ComputationMetadata(**metadata_dict)
191
+ setattr(self, computation_metadata_attr, metadata)
192
+
193
+
194
+ class ImageProcessor(BaseProcessor[ImageROI, ROI2DParam]):
195
+ """Object handling image processing: operations, processing, analysis"""
196
+
197
+ # pylint: disable=duplicate-code
198
+
199
+ def _wrap_geometric_transform(self, func, operation: str):
200
+ """Wrap a geometric transformation function to apply geometry transforms.
201
+
202
+ Args:
203
+ func: The original Sigima function
204
+ operation: The operation name for geometry transformation
205
+
206
+ Returns:
207
+ Pickleable wrapped function that applies geometry transformations
208
+ """
209
+ return GeometricTransformWrapper(func, operation)
210
+
211
+ def postprocess_1_to_0_result(
212
+ self, obj: ImageObj, result: GeometryResult | TableResult
213
+ ) -> bool:
214
+ """Post-process results from 1-to-0 operations for images.
215
+
216
+ For image objects with geometry results, applies detection ROIs if requested
217
+ in the result metadata (via DetectionROIParam).
218
+
219
+ Args:
220
+ obj: The image object that was analyzed
221
+ result: The analysis result (GeometryResult or TableResult)
222
+
223
+ Returns:
224
+ True if the object was modified and needs refresh, False otherwise
225
+ """
226
+ if isinstance(result, GeometryResult):
227
+ return sipi.apply_detection_rois(obj, result)
228
+ return False
229
+
230
+ def register_operations(self) -> None:
231
+ """Register operations."""
232
+ self.register_n_to_1(sipi.addition, _("Sum"), icon_name="sum.svg")
233
+ self.register_n_to_1(sipi.average, _("Average"), icon_name="average.svg")
234
+ self.register_n_to_1(
235
+ sipi.standard_deviation,
236
+ _("Standard deviation"),
237
+ icon_name="std.svg",
238
+ )
239
+ self.register_2_to_1(
240
+ sipi.difference,
241
+ _("Difference"),
242
+ icon_name="difference.svg",
243
+ obj2_name=_("image to subtract"),
244
+ )
245
+ self.register_2_to_1(
246
+ sipi.quadratic_difference,
247
+ _("Quadratic Difference"),
248
+ icon_name="quadratic_difference.svg",
249
+ obj2_name=_("image to subtract"),
250
+ )
251
+ self.register_n_to_1(sipi.product, _("Product"), icon_name="product.svg")
252
+ self.register_2_to_1(
253
+ sipi.division,
254
+ _("Division"),
255
+ icon_name="division.svg",
256
+ obj2_name=_("divider"),
257
+ )
258
+ self.register_1_to_1(sipi.inverse, _("Inverse"), icon_name="inverse.svg")
259
+ self.register_2_to_1(
260
+ sipi.arithmetic,
261
+ _("Arithmetic"),
262
+ paramclass=sipb.ArithmeticParam,
263
+ icon_name="arithmetic.svg",
264
+ obj2_name=_("signal to operate with"),
265
+ )
266
+ self.register_1_to_1(
267
+ sipi.addition_constant,
268
+ _("Add constant"),
269
+ paramclass=sipb.ConstantParam,
270
+ icon_name="constant_add.svg",
271
+ )
272
+ self.register_1_to_1(
273
+ sipi.difference_constant,
274
+ _("Subtract constant"),
275
+ paramclass=sipb.ConstantParam,
276
+ icon_name="constant_subtract.svg",
277
+ )
278
+ self.register_1_to_1(
279
+ sipi.product_constant,
280
+ _("Multiply by constant"),
281
+ paramclass=sipb.ConstantParam,
282
+ icon_name="constant_multiply.svg",
283
+ )
284
+ self.register_1_to_1(
285
+ sipi.division_constant,
286
+ _("Divide by constant"),
287
+ paramclass=sipb.ConstantParam,
288
+ icon_name="constant_divide.svg",
289
+ )
290
+ self.register_1_to_1(sipi.absolute, _("Absolute value"), icon_name="abs.svg")
291
+ self.register_1_to_1(
292
+ sipi.phase,
293
+ _("Phase"),
294
+ paramclass=sigima.params.PhaseParam,
295
+ icon_name="phase.svg",
296
+ )
297
+ self.register_2_to_1(
298
+ sipi.complex_from_magnitude_phase,
299
+ _("Combine with phase"),
300
+ paramclass=sipb.AngleUnitParam,
301
+ icon_name="complex_from_magnitude_phase.svg",
302
+ comment=_("Create a complex-valued image from magnitude and phase"),
303
+ obj2_name=_("phase"),
304
+ )
305
+ self.register_1_to_1(sipi.real, _("Real part"), icon_name="re.svg")
306
+ self.register_1_to_1(sipi.imag, _("Imaginary part"), icon_name="im.svg")
307
+ self.register_2_to_1(
308
+ sipi.complex_from_real_imag,
309
+ _("Combine with imaginary part"),
310
+ icon_name="complex_from_real_imag.svg",
311
+ comment=_("Create a complex-valued image from real and imaginary parts"),
312
+ obj2_name=_("imaginary part"),
313
+ )
314
+ self.register_1_to_1(
315
+ sipi.astype,
316
+ _("Convert data type"),
317
+ paramclass=sigima.params.DataTypeIParam,
318
+ icon_name="convert_dtype.svg",
319
+ )
320
+ self.register_1_to_1(sipi.exp, _("Exponential"), icon_name="exp.svg")
321
+ self.register_1_to_1(
322
+ sipi.log10, _("Logarithm (base 10)"), icon_name="log10.svg"
323
+ )
324
+ self.register_1_to_1(
325
+ sipi.log10_z_plus_n,
326
+ "Log10(z+n)",
327
+ paramclass=sigima.params.Log10ZPlusNParam,
328
+ )
329
+ self.register_2_to_1(
330
+ sipi.flatfield,
331
+ _("Flat-field correction"),
332
+ sipi.FlatFieldParam,
333
+ obj2_name=_("flat field image"),
334
+ )
335
+ # Flip or rotation
336
+ self.register_1_to_1(
337
+ self._wrap_geometric_transform(sipi.fliph, "fliph"),
338
+ _("Flip horizontally"),
339
+ icon_name="flip_horizontally.svg",
340
+ )
341
+ self.register_1_to_1(
342
+ self._wrap_geometric_transform(sipi.transpose, "transpose"),
343
+ _("Flip diagonally"),
344
+ icon_name="swap_x_y.svg",
345
+ )
346
+ self.register_1_to_1(
347
+ self._wrap_geometric_transform(sipi.flipv, "flipv"),
348
+ _("Flip vertically"),
349
+ icon_name="flip_vertically.svg",
350
+ )
351
+ self.register_1_to_1(
352
+ self._wrap_geometric_transform(sipi.rotate270, "rotate270"),
353
+ _("Rotate %s right") % "90°",
354
+ icon_name="rotate_right.svg",
355
+ )
356
+ self.register_1_to_1(
357
+ self._wrap_geometric_transform(sipi.rotate90, "rotate90"),
358
+ _("Rotate %s left") % "90°",
359
+ icon_name="rotate_left.svg",
360
+ )
361
+ self.register_1_to_1(sipi.rotate, _("Rotate by"), sipi.RotateParam)
362
+ # Intensity profiles
363
+ self.register_1_to_1(
364
+ sipi.line_profile,
365
+ _("Line profile"),
366
+ sipi.LineProfileParam,
367
+ icon_name="profile.svg",
368
+ edit=False,
369
+ )
370
+ self.register_1_to_1(
371
+ sipi.segment_profile,
372
+ _("Segment profile"),
373
+ sipi.SegmentProfileParam,
374
+ icon_name="profile_segment.svg",
375
+ edit=False,
376
+ )
377
+ self.register_1_to_1(
378
+ sipi.average_profile,
379
+ _("Average profile"),
380
+ sipi.AverageProfileParam,
381
+ icon_name="profile_average.svg",
382
+ edit=False,
383
+ )
384
+ self.register_1_to_1(
385
+ sipi.radial_profile,
386
+ _("Radial profile"),
387
+ sipi.RadialProfileParam,
388
+ icon_name="profile_radial.svg",
389
+ )
390
+ self.register_2_to_1(
391
+ sipi.convolution,
392
+ _("Convolution"),
393
+ icon_name="convolution.svg",
394
+ obj2_name=_("kernel to convolve with"),
395
+ )
396
+ self.register_2_to_1(
397
+ sipi.deconvolution,
398
+ _("Deconvolution"),
399
+ icon_name="deconvolution.svg",
400
+ obj2_name=_("kernel to deconvolve with"),
401
+ )
402
+
403
+ def register_processing(self) -> None:
404
+ """Register processing functions."""
405
+ # Axis transformation
406
+ self.register_1_to_1(
407
+ sipi.set_uniform_coords,
408
+ _("Set uniform coordinates"),
409
+ sipi.UniformCoordsParam,
410
+ )
411
+ self.register_1_to_1(
412
+ sipi.calibration,
413
+ _("Polynomial calibration"),
414
+ sipi.XYZCalibrateParam,
415
+ comment=_(
416
+ "Apply polynomial calibration to the X, Y or Z axis:\n"
417
+ " • x' = a0 + a1*x + a2*x^2 + ...\n"
418
+ " • y' = a0 + a1*y + a2*y^2 + ...\n"
419
+ " • z' = a0 + a1*z + a2*z^2 + ..."
420
+ ),
421
+ )
422
+ self.register_1_to_1(
423
+ sipi.transpose,
424
+ _("Swap X/Y axes"),
425
+ icon_name="swap_x_y.svg",
426
+ )
427
+ # Level adjustment
428
+ self.register_1_to_1(
429
+ sipi.normalize,
430
+ _("Normalize"),
431
+ paramclass=sipb.NormalizeParam,
432
+ icon_name="normalize.svg",
433
+ )
434
+ self.register_1_to_1(sipi.clip, _("Clipping"), sipb.ClipParam, "clip.svg")
435
+ self.register_1_to_1(
436
+ sipi.offset_correction,
437
+ _("Offset correction"),
438
+ ROI2DParam,
439
+ comment=_("Evaluate and subtract the offset value from the data"),
440
+ icon_name="offset_correction.svg",
441
+ )
442
+ # Noise addition
443
+ self.register_1_to_1(
444
+ sipi.add_gaussian_noise, _("Add Gaussian noise"), NormalDistributionParam
445
+ )
446
+ self.register_1_to_1(
447
+ sipi.add_poisson_noise, _("Add Poisson noise"), PoissonDistributionParam
448
+ )
449
+ self.register_1_to_1(
450
+ sipi.add_uniform_noise, _("Add uniform noise"), UniformDistributionParam
451
+ )
452
+ # Noise reduction
453
+ self.register_1_to_1(
454
+ sipi.gaussian_filter,
455
+ _("Gaussian filter"),
456
+ sipb.GaussianParam,
457
+ )
458
+ self.register_1_to_1(
459
+ sipi.moving_average,
460
+ _("Moving average"),
461
+ sipb.MovingAverageParam,
462
+ )
463
+ self.register_1_to_1(
464
+ sipi.moving_median,
465
+ _("Moving median"),
466
+ sipb.MovingMedianParam,
467
+ )
468
+ self.register_1_to_1(sipi.wiener, _("Wiener filter"))
469
+ self.register_1_to_1(
470
+ sipi.erase,
471
+ _("Erase area") + "...",
472
+ ROI2DParam,
473
+ comment=_("Erase area in the image as defined by a region of interest"),
474
+ icon_name="erase.svg",
475
+ )
476
+ # Fourier analysis
477
+ self.register_1_to_1(
478
+ sipi.zero_padding,
479
+ _("Zero padding"),
480
+ sipi.ZeroPadding2DParam,
481
+ comment=_(
482
+ "Zero padding is used to increase the frequency resolution of the FFT"
483
+ ),
484
+ )
485
+ self.register_1_to_1(
486
+ sipi.fft,
487
+ _("FFT"),
488
+ sipb.FFTParam,
489
+ comment=_(
490
+ "Fast Fourier Transform (FFT) is an estimation of the "
491
+ "Discrete Fourier Transform (DFT). "
492
+ "Results are complex numbers, but only the real part is plotted."
493
+ ),
494
+ edit=False,
495
+ )
496
+ self.register_1_to_1(
497
+ sipi.ifft,
498
+ _("Inverse FFT"),
499
+ sipb.FFTParam,
500
+ comment=_(
501
+ "Inverse Fast Fourier Transform (IFFT) is an estimation of the "
502
+ "Inverse Discrete Fourier Transform (IDFT). "
503
+ "Results are complex numbers, but only the real part is plotted."
504
+ ),
505
+ edit=False,
506
+ )
507
+ self.register_1_to_1(
508
+ sipi.magnitude_spectrum,
509
+ _("Magnitude spectrum"),
510
+ paramclass=sigima.params.SpectrumParam,
511
+ comment=_(
512
+ "Magnitude spectrum is the absolute value of the FFT result. "
513
+ "It is a measure of the amplitude of the frequency components."
514
+ ),
515
+ )
516
+ self.register_1_to_1(
517
+ sipi.phase_spectrum,
518
+ _("Phase spectrum"),
519
+ comment=_(
520
+ "Phase spectrum is the angle of the FFT result. "
521
+ "It is a measure of the phase of the frequency components."
522
+ ),
523
+ )
524
+ self.register_1_to_1(
525
+ sipi.psd,
526
+ _("Power spectral density"),
527
+ paramclass=sigima.params.SpectrumParam,
528
+ comment=_(
529
+ "Power spectral density (PSD) is the square of the magnitude spectrum. "
530
+ "It is a measure of the power of the frequency components."
531
+ ),
532
+ )
533
+ # Frequency filters
534
+ self.register_1_to_1(
535
+ sipi.butterworth,
536
+ _("Butterworth"),
537
+ sipi.ButterworthParam,
538
+ )
539
+ self.register_1_to_1(
540
+ sipi.gaussian_freq_filter,
541
+ _("Gaussian bandpass"),
542
+ sipi.GaussianFreqFilterParam,
543
+ )
544
+ # Thresholding
545
+ self.register_1_to_1(
546
+ sipi.threshold,
547
+ _("Parametric thresholding"),
548
+ sipi.ThresholdParam,
549
+ comment=_(
550
+ "Parametric thresholding allows to select a thresholding method "
551
+ "and a threshold value."
552
+ ),
553
+ )
554
+ self.register_1_to_1(sipi.threshold_isodata, _("ISODATA thresholding"))
555
+ self.register_1_to_1(sipi.threshold_li, _("Li thresholding"))
556
+ self.register_1_to_1(sipi.threshold_mean, _("Mean thresholding"))
557
+ self.register_1_to_1(sipi.threshold_minimum, _("Minimum thresholding"))
558
+ self.register_1_to_1(sipi.threshold_otsu, _("Otsu thresholding"))
559
+ self.register_1_to_1(sipi.threshold_triangle, _("Triangle thresholding"))
560
+ self.register_1_to_1(sipi.threshold_yen, _("Yen thresholding"))
561
+ # Exposure
562
+ self.register_1_to_1(
563
+ sipi.adjust_gamma,
564
+ _("Gamma correction"),
565
+ sipi.AdjustGammaParam,
566
+ )
567
+ self.register_1_to_1(
568
+ sipi.adjust_log,
569
+ _("Logarithmic correction"),
570
+ sipi.AdjustLogParam,
571
+ )
572
+ self.register_1_to_1(
573
+ sipi.adjust_sigmoid,
574
+ _("Sigmoid correction"),
575
+ sipi.AdjustSigmoidParam,
576
+ )
577
+ self.register_1_to_1(
578
+ sipi.equalize_hist,
579
+ _("Histogram equalization"),
580
+ sipi.EqualizeHistParam,
581
+ )
582
+ self.register_1_to_1(
583
+ sipi.equalize_adapthist,
584
+ _("Adaptive histogram equalization"),
585
+ sipi.EqualizeAdaptHistParam,
586
+ )
587
+ self.register_1_to_1(
588
+ sipi.rescale_intensity,
589
+ _("Intensity rescaling"),
590
+ sipi.RescaleIntensityParam,
591
+ )
592
+ # Restoration
593
+ self.register_1_to_1(
594
+ sipi.denoise_tv,
595
+ _("Total variation denoising"),
596
+ sipi.DenoiseTVParam,
597
+ )
598
+ self.register_1_to_1(
599
+ sipi.denoise_bilateral,
600
+ _("Bilateral filter denoising"),
601
+ sipi.DenoiseBilateralParam,
602
+ )
603
+ self.register_1_to_1(
604
+ sipi.denoise_wavelet,
605
+ _("Wavelet denoising"),
606
+ sipi.DenoiseWaveletParam,
607
+ )
608
+ self.register_1_to_1(
609
+ sipi.denoise_tophat,
610
+ _("White Top-Hat denoising"),
611
+ sipi.MorphologyParam,
612
+ )
613
+ # Morphology
614
+ self.register_1_to_1(
615
+ sipi.white_tophat,
616
+ _("White Top-Hat (disk)"),
617
+ sipi.MorphologyParam,
618
+ )
619
+ self.register_1_to_1(
620
+ sipi.black_tophat,
621
+ _("Black Top-Hat (disk)"),
622
+ sipi.MorphologyParam,
623
+ )
624
+ self.register_1_to_1(
625
+ sipi.erosion,
626
+ _("Erosion (disk)"),
627
+ sipi.MorphologyParam,
628
+ )
629
+ self.register_1_to_1(
630
+ sipi.dilation,
631
+ _("Dilation (disk)"),
632
+ sipi.MorphologyParam,
633
+ )
634
+ self.register_1_to_1(
635
+ sipi.opening,
636
+ _("Opening (disk)"),
637
+ sipi.MorphologyParam,
638
+ )
639
+ self.register_1_to_1(
640
+ sipi.closing,
641
+ _("Closing (disk)"),
642
+ sipi.MorphologyParam,
643
+ )
644
+ # Edge detection
645
+ self.register_1_to_1(sipi.canny, _("Canny filter"), sipi.CannyParam)
646
+ self.register_1_to_1(sipi.farid, _("Farid filter"))
647
+ self.register_1_to_1(sipi.farid_h, _("Farid filter (horizontal)"))
648
+ self.register_1_to_1(sipi.farid_v, _("Farid filter (vertical)"))
649
+ self.register_1_to_1(sipi.laplace, _("Laplace filter"))
650
+ self.register_1_to_1(sipi.prewitt, _("Prewitt filter"))
651
+ self.register_1_to_1(sipi.prewitt_h, _("Prewitt filter (horizontal)"))
652
+ self.register_1_to_1(sipi.prewitt_v, _("Prewitt filter (vertical)"))
653
+ self.register_1_to_1(sipi.roberts, _("Roberts filter"))
654
+ self.register_1_to_1(sipi.scharr, _("Scharr filter"))
655
+ self.register_1_to_1(sipi.scharr_h, _("Scharr filter (horizontal)"))
656
+ self.register_1_to_1(sipi.scharr_v, _("Scharr filter (vertical)"))
657
+ self.register_1_to_1(sipi.sobel, _("Sobel filter"))
658
+ self.register_1_to_1(sipi.sobel_h, _("Sobel filter (horizontal)"))
659
+ self.register_1_to_1(sipi.sobel_v, _("Sobel filter (vertical)"))
660
+
661
+ # Other processing
662
+ self.register_1_to_n(sipi.extract_roi, "ROI", icon_name="roi.svg")
663
+ self.register_1_to_1(
664
+ sipi.resize,
665
+ _("Resize"),
666
+ sipi.ResizeParam,
667
+ icon_name="resize.svg",
668
+ )
669
+ self.register_1_to_1(
670
+ sipi.binning,
671
+ _("Pixel binning"),
672
+ sipi.BinningParam,
673
+ icon_name="binning.svg",
674
+ )
675
+ self.register_1_to_1(
676
+ sipi.resampling,
677
+ _("Resampling"),
678
+ sipi.Resampling2DParam,
679
+ icon_name="resampling2d.svg",
680
+ )
681
+
682
+ def register_analysis(self) -> None:
683
+ """Register analysis functions."""
684
+ self.register_1_to_0(sipi.stats, _("Statistics"), icon_name="stats.svg")
685
+ self.register_1_to_1(
686
+ sipi.horizontal_projection,
687
+ _("Horizontal projection"),
688
+ # icon_name="horizontal_projection.svg",
689
+ comment=_(
690
+ "Compute the sum of pixel intensities along each column "
691
+ "(projection on the x-axis)"
692
+ ),
693
+ )
694
+ self.register_1_to_1(
695
+ sipi.vertical_projection,
696
+ # icon_name="vertical_projection.svg",
697
+ _("Vertical projection"),
698
+ comment=_(
699
+ "Compute the sum of pixel intensities along each row "
700
+ "(projection on the y-axis)"
701
+ ),
702
+ )
703
+
704
+ self.register_1_to_1(
705
+ sipi.histogram,
706
+ _("Histogram"),
707
+ paramclass=sipb.HistogramParam,
708
+ icon_name="histogram.svg",
709
+ )
710
+ self.register_1_to_0(
711
+ sipi.centroid,
712
+ _("Centroid"),
713
+ comment=_("Compute image centroid"),
714
+ )
715
+ self.register_1_to_0(
716
+ sipi.enclosing_circle,
717
+ _("Minimum enclosing circle center"),
718
+ comment=_("Compute smallest enclosing circle center"),
719
+ )
720
+ self.register_1_to_0(
721
+ sipi.contour_shape,
722
+ _("Contour detection"),
723
+ sipi.ContourShapeParam,
724
+ comment=_("Compute contour shape fit"),
725
+ )
726
+ self.register_1_to_0(
727
+ sipi.peak_detection,
728
+ _("Peak detection"),
729
+ sipi.Peak2DDetectionParam,
730
+ comment=_("Detect peaks in the image"),
731
+ )
732
+ self.register_1_to_0(
733
+ sipi.hough_circle_peaks,
734
+ _("Circle Hough transform"),
735
+ sipi.HoughCircleParam,
736
+ comment=_("Detect circular shapes using circle Hough transform"),
737
+ )
738
+ # Blob detection
739
+ self.register_1_to_0(
740
+ sipi.blob_dog,
741
+ _("Blob detection (DOG)"),
742
+ sipi.BlobDOGParam,
743
+ comment=_("Detect blobs using Difference of Gaussian (DOG) method"),
744
+ )
745
+ self.register_1_to_0(
746
+ sipi.blob_doh,
747
+ _("Blob detection (DOH)"),
748
+ sipi.BlobDOHParam,
749
+ comment=_("Detect blobs using Difference of Gaussian (DOH) method"),
750
+ )
751
+ self.register_1_to_0(
752
+ sipi.blob_log,
753
+ _("Blob detection (LOG)"),
754
+ sipi.BlobLOGParam,
755
+ comment=_("Detect blobs using Laplacian of Gaussian (LOG) method"),
756
+ )
757
+ self.register_1_to_0(
758
+ sipi.blob_opencv,
759
+ _("Blob detection (OpenCV)"),
760
+ sipi.BlobOpenCVParam,
761
+ comment=_("Detect blobs using OpenCV SimpleBlobDetector"),
762
+ )
763
+
764
+ def create_roi_grid(self) -> None:
765
+ """Create a grid of regions of interest"""
766
+ obj0 = self.panel.objview.get_sel_objects(include_groups=True)[0]
767
+ if any(obj.roi is not None for obj in self.panel.objview.get_sel_objects()):
768
+ if (
769
+ QW.QMessageBox.question(
770
+ self.mainwindow,
771
+ _("Warning"),
772
+ _(
773
+ "Creating a ROI grid will overwrite any existing ROI.<br><br>"
774
+ "Do you want to continue?"
775
+ ),
776
+ QW.QMessageBox.Yes | QW.QMessageBox.No,
777
+ QW.QMessageBox.No,
778
+ )
779
+ == QW.QMessageBox.No
780
+ ):
781
+ return
782
+ editor = ImageGridROIEditor(parent=self.parent(), obj=obj0)
783
+ if exec_dialog(editor):
784
+ for obj in self.panel.objview.get_sel_objects():
785
+ obj.roi = editor.get_roi()
786
+ self.SIG_ADD_SHAPE.emit(get_uuid(obj0))
787
+ self.panel.selection_changed(update_items=True)
788
+ self.panel.refresh_plot(
789
+ "selected",
790
+ update_items=True,
791
+ only_visible=False,
792
+ only_existing=True,
793
+ )
794
+ # Now, we ask the user if we shall extract the freshly defined ROI:
795
+ if (
796
+ QW.QMessageBox.question(
797
+ self.mainwindow,
798
+ _("Extract ROI"),
799
+ _("Do you want to extract images from the defined ROI?"),
800
+ QW.QMessageBox.Yes | QW.QMessageBox.No,
801
+ QW.QMessageBox.No,
802
+ )
803
+ == QW.QMessageBox.Yes
804
+ ):
805
+ self.compute_roi_extraction(editor.get_roi())
806
+
807
+ @qt_try_except()
808
+ def compute_resize(self, param: sigima.params.ResizeParam | None = None) -> None:
809
+ """Resize image with :py:func:`sigima.proc.image.resize`"""
810
+ obj0 = self.panel.objview.get_sel_objects(include_groups=True)[0]
811
+ for obj in self.panel.objview.get_sel_objects():
812
+ if obj.data.shape != obj0.data.shape:
813
+ QW.QMessageBox.warning(
814
+ self.mainwindow,
815
+ APP_NAME,
816
+ _("Warning:")
817
+ + "\n"
818
+ + _("Selected images do not have the same size"),
819
+ )
820
+ edit, param = self.init_param(param, sipi.ResizeParam, _("Resize"))
821
+ if edit:
822
+ original_size = obj0.data.shape
823
+ dlg = ResizeDialog(
824
+ self.plotwidget,
825
+ new_size=original_size,
826
+ old_size=original_size,
827
+ text=_("Destination size:"),
828
+ )
829
+ if not exec_dialog(dlg):
830
+ return
831
+ param.zoom = dlg.get_zoom()
832
+ self.run_feature("resize", param, title=_("Resize"), edit=edit)
833
+
834
+ @qt_try_except()
835
+ def compute_binning(self, param: sigima.params.BinningParam | None = None) -> None:
836
+ """Binning image with :py:func:`sigima.proc.image.binning`"""
837
+ edit = param is None
838
+ obj0 = self.panel.objview.get_sel_objects(include_groups=True)[0]
839
+ input_dtype_str = str(obj0.data.dtype)
840
+ title = _("Binning")
841
+ edit, param = self.init_param(param, sipi.BinningParam, title)
842
+ if edit:
843
+ param.dtype_str = input_dtype_str
844
+ if param.dtype_str is None:
845
+ param.dtype_str = input_dtype_str
846
+ self.run_feature("binning", param, title=title, edit=edit)
847
+
848
+ @qt_try_except()
849
+ def compute_line_profile(
850
+ self, param: sigima.params.LineProfileParam | None = None
851
+ ) -> None:
852
+ """Compute profile along a vertical or horizontal line
853
+ with :py:func:`sigima.proc.image.line_profile`"""
854
+ title = _("Profile")
855
+ add_initial_shape = self.has_param_defaults(sigima.params.LineProfileParam)
856
+ edit, param = self.init_param(param, sipi.LineProfileParam, title)
857
+ if edit:
858
+ options = self.panel.plothandler.get_plot_options()
859
+ dlg = ProfileExtractionDialog(
860
+ "line", param, options, self.mainwindow, add_initial_shape
861
+ )
862
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
863
+ dlg.set_obj(obj)
864
+ if not exec_dialog(dlg):
865
+ return
866
+ self.run_feature("line_profile", param, title=title, edit=False)
867
+
868
+ @qt_try_except()
869
+ def compute_segment_profile(
870
+ self, param: sigima.params.SegmentProfileParam | None = None
871
+ ):
872
+ """Compute profile along a segment
873
+ with :py:func:`sigima.proc.image.segment_profile`"""
874
+ title = _("Profile")
875
+ add_initial_shape = self.has_param_defaults(sigima.params.SegmentProfileParam)
876
+ edit, param = self.init_param(param, sipi.SegmentProfileParam, title)
877
+ if edit:
878
+ options = self.panel.plothandler.get_plot_options()
879
+ dlg = ProfileExtractionDialog(
880
+ "segment", param, options, self.mainwindow, add_initial_shape
881
+ )
882
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
883
+ dlg.set_obj(obj)
884
+ if not exec_dialog(dlg):
885
+ return
886
+ self.run_feature("segment_profile", param, title=title, edit=False)
887
+
888
+ @qt_try_except()
889
+ def compute_average_profile(
890
+ self, param: sigima.params.AverageProfileParam | None = None
891
+ ) -> None:
892
+ """Compute average profile
893
+ with :py:func:`sigima.proc.image.average_profile`"""
894
+ title = _("Average profile")
895
+ add_initial_shape = self.has_param_defaults(sigima.params.AverageProfileParam)
896
+ edit, param = self.init_param(param, sipi.AverageProfileParam, title)
897
+ if edit:
898
+ options = self.panel.plothandler.get_plot_options()
899
+ dlg = ProfileExtractionDialog(
900
+ "rectangle",
901
+ param,
902
+ options,
903
+ self.mainwindow,
904
+ add_initial_shape,
905
+ )
906
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
907
+ dlg.set_obj(obj)
908
+ if not exec_dialog(dlg):
909
+ return
910
+ self.run_feature("average_profile", param, title=title, edit=False)
911
+
912
+ @qt_try_except()
913
+ def compute_radial_profile(
914
+ self, param: sigima.params.RadialProfileParam | None = None
915
+ ) -> None:
916
+ """Compute radial profile
917
+ with :py:func:`sigima.proc.image.radial_profile`"""
918
+ title = _("Radial profile")
919
+ edit, param = self.init_param(param, sipi.RadialProfileParam, title)
920
+ if edit:
921
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
922
+ param.update_from_obj(obj)
923
+ self.run_feature("radial_profile", param, title=title, edit=edit)
924
+
925
+ @qt_try_except()
926
+ def distribute_on_grid(self, param: sigima.params.GridParam | None = None) -> None:
927
+ """Distribute images on a grid"""
928
+ title = _("Distribute on grid")
929
+ edit, param = self.init_param(param, sipi.GridParam, title)
930
+ if edit and not param.edit(parent=self.mainwindow):
931
+ return
932
+ objs = self.panel.objview.get_sel_objects(include_groups=True)
933
+ g_row, g_col, x0, y0, x0_0, y0_0 = 0, 0, 0.0, 0.0, 0.0, 0.0
934
+ dx0, dy0 = 0.0, 0.0
935
+ with create_progress_bar(self.panel, title, max_=len(objs)) as progress:
936
+ for i_row, obj in enumerate(objs):
937
+ progress.setValue(i_row + 1)
938
+ QW.QApplication.processEvents()
939
+ if progress.wasCanceled():
940
+ break
941
+ if i_row == 0:
942
+ if obj.is_uniform_coords:
943
+ x0_0, y0_0 = x0, y0 = obj.x0, obj.y0
944
+ else:
945
+ x0_0, y0_0 = x0, y0 = obj.xcoords[0], obj.ycoords[0]
946
+ else:
947
+ if obj.is_uniform_coords:
948
+ dx0, dy0 = x0 - obj.x0, y0 - obj.y0
949
+ obj.x0 += dx0
950
+ obj.y0 += dy0
951
+ else:
952
+ dx0, dy0 = x0 - obj.xcoords[0], y0 - obj.ycoords[0]
953
+ obj.xcoords += dx0
954
+ obj.ycoords += dy0
955
+ apply_geometry_transform(obj, "translate", dx=dx0, dy=dy0)
956
+ sipi.transformer.transform_roi(obj, "translate", dx=dx0, dy=dy0)
957
+
958
+ # Get image width and height
959
+ if obj.is_uniform_coords:
960
+ img_width, img_height = obj.width, obj.height
961
+ else:
962
+ img_width = obj.xcoords[-1] - obj.xcoords[0]
963
+ img_height = obj.ycoords[-1] - obj.ycoords[0]
964
+
965
+ if param.direction == "row":
966
+ # Distributing images over rows
967
+ sign = np.sign(param.rows)
968
+ g_row = (g_row + sign) % param.rows
969
+ y0 += (img_height + param.rowspac) * sign
970
+ if g_row == 0:
971
+ g_col += 1
972
+ x0 += img_width + param.colspac
973
+ y0 = y0_0
974
+ else:
975
+ # Distributing images over columns
976
+ sign = np.sign(param.cols)
977
+ g_col = (g_col + sign) % param.cols
978
+ x0 += (img_width + param.colspac) * sign
979
+ if g_col == 0:
980
+ g_row += 1
981
+ x0 = x0_0
982
+ y0 += img_height + param.rowspac
983
+ self.panel.refresh_plot("selected", True, False)
984
+
985
+ @qt_try_except()
986
+ def reset_positions(self) -> None:
987
+ """Reset image positions"""
988
+ x0_0, y0_0 = 0.0, 0.0
989
+ dx0, dy0 = 0.0, 0.0
990
+ objs = self.panel.objview.get_sel_objects(include_groups=True)
991
+ for i_row, obj in enumerate(objs):
992
+ if i_row == 0:
993
+ if obj.is_uniform_coords:
994
+ x0_0, y0_0 = obj.x0, obj.y0
995
+ else:
996
+ x0_0, y0_0 = obj.xcoords[0], obj.ycoords[0]
997
+ else:
998
+ if obj.is_uniform_coords:
999
+ dx0, dy0 = x0_0 - obj.x0, y0_0 - obj.y0
1000
+ obj.x0 += dx0
1001
+ obj.y0 += dy0
1002
+ else:
1003
+ dx0, dy0 = x0_0 - obj.xcoords[0], y0_0 - obj.ycoords[0]
1004
+ obj.xcoords += dx0
1005
+ obj.ycoords += dy0
1006
+ apply_geometry_transform(obj, "translate", dx=dx0, dy=dy0)
1007
+ sipi.transformer.transform_roi(obj, "translate", dx=dx0, dy=dy0)
1008
+ self.panel.refresh_plot("selected", True, False)
1009
+
1010
+ # ------Image Processing
1011
+ @qt_try_except()
1012
+ def compute_offset_correction(self, param: ROI2DParam | None = None) -> None:
1013
+ """Compute offset correction
1014
+ with :py:func:`sigima.proc.image.offset_correction`"""
1015
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
1016
+ if param is None:
1017
+ dlg = imagebackground.ImageBackgroundDialog(obj, parent=self.mainwindow)
1018
+ if exec_dialog(dlg):
1019
+ x0, y0, x1, y1 = dlg.get_rect_coords()
1020
+ param = ROI2DParam.create(
1021
+ geometry="rectangle",
1022
+ x0=int(x0),
1023
+ y0=int(y0),
1024
+ dx=int(x1 - x0),
1025
+ dy=int(y1 - y0),
1026
+ )
1027
+ else:
1028
+ return
1029
+ self.run_feature("offset_correction", param)
1030
+
1031
+ @qt_try_except()
1032
+ def compute_erase(self, roi: ImageROI | None = None) -> None:
1033
+ """Erase area in the image as defined by a region of interest
1034
+
1035
+ Args:
1036
+ roi: Region of interest to erase
1037
+ """
1038
+ if roi is None or roi.is_empty():
1039
+ roi = self.edit_roi_graphically(mode="define")
1040
+ if roi is None or roi.is_empty():
1041
+ return
1042
+ obj = self.panel.objview.get_sel_objects(include_groups=True)[0]
1043
+ params = roi.to_params(obj)
1044
+
1045
+ # TODO: This `compute_1_to_1` call is not ideal, as it passes a list of
1046
+ # parameter sets (`params` is a list of `DataSet` objects) instead of a single
1047
+ # parameter set as expected by the method. Currently, the method implementation
1048
+ # is compatible with this call, and it simply passes the second argument through
1049
+ # to the `extract_rois` function. However, this should be rectified in the
1050
+ # future to ensure that the method signature and its usage are consistent.
1051
+ # The question is: should we pass a list of `DataSet` objects or directly a
1052
+ # `ImageROI` object?
1053
+ # (same as `_extract_multiple_roi_in_single_object`)
1054
+ self.compute_1_to_1(sipi.erase, params, title=_("Erase area"), edit=False)
1055
+
1056
+ @qt_try_except()
1057
+ def compute_all_threshold(self) -> None:
1058
+ """Compute all threshold algorithms
1059
+ using the following functions:
1060
+
1061
+ - :py:func:`sigima.proc.image.threshold_isodata`
1062
+ - :py:func:`sigima.proc.image.threshold_li`
1063
+ - :py:func:`sigima.proc.image.threshold_mean`
1064
+ - :py:func:`sigima.proc.image.threshold_minimum`
1065
+ - :py:func:`sigima.proc.image.threshold_otsu`
1066
+ - :py:func:`sigima.proc.image.threshold_triangle`
1067
+ - :py:func:`sigima.proc.image.threshold_yen`
1068
+ """
1069
+ self.compute_multiple_1_to_1(
1070
+ [
1071
+ sipi.threshold_isodata,
1072
+ sipi.threshold_li,
1073
+ sipi.threshold_mean,
1074
+ sipi.threshold_minimum,
1075
+ sipi.threshold_otsu,
1076
+ sipi.threshold_triangle,
1077
+ sipi.threshold_yen,
1078
+ ],
1079
+ None,
1080
+ "Threshold",
1081
+ edit=False,
1082
+ )
1083
+
1084
+ @qt_try_except()
1085
+ def compute_all_denoise(self, params: list | None = None) -> None:
1086
+ """Compute all denoising filters
1087
+ using the following functions:
1088
+
1089
+ - :py:func:`sigima.proc.image.denoise_tv`
1090
+ - :py:func:`sigima.proc.image.denoise_bilateral`
1091
+ - :py:func:`sigima.proc.image.denoise_wavelet`
1092
+ - :py:func:`sigima.proc.image.denoise_tophat`
1093
+ """
1094
+ if params is not None:
1095
+ assert len(params) == 4, "Wrong number of parameters (4 expected)"
1096
+ funcs = [
1097
+ sipi.denoise_tv,
1098
+ sipi.denoise_bilateral,
1099
+ sipi.denoise_wavelet,
1100
+ sipi.denoise_tophat,
1101
+ ]
1102
+ edit = params is None
1103
+ if edit:
1104
+ params = []
1105
+ for paramclass, title in (
1106
+ (sipi.DenoiseTVParam, _("Total variation denoising")),
1107
+ (sipi.DenoiseBilateralParam, _("Bilateral filter denoising")),
1108
+ (sipi.DenoiseWaveletParam, _("Wavelet denoising")),
1109
+ (sipi.MorphologyParam, _("Denoise / Top-Hat")),
1110
+ ):
1111
+ param = paramclass(title)
1112
+ self.update_param_defaults(param)
1113
+ params.append(param)
1114
+ self.compute_multiple_1_to_1(funcs, params, "Denoise", edit=edit)
1115
+
1116
+ @qt_try_except()
1117
+ def compute_all_morphology(
1118
+ self, param: sigima.params.MorphologyParam | None = None
1119
+ ) -> None:
1120
+ """Compute all morphology filters
1121
+ using the following functions:
1122
+
1123
+ - :py:func:`sigima.proc.image.white_tophat`
1124
+ - :py:func:`sigima.proc.image.black_tophat`
1125
+ - :py:func:`sigima.proc.image.erosion`
1126
+ - :py:func:`sigima.proc.image.dilation`
1127
+ - :py:func:`sigima.proc.image.opening`
1128
+ - :py:func:`sigima.proc.image.closing`
1129
+ """
1130
+ if param is None:
1131
+ param = sipi.MorphologyParam()
1132
+ if not param.edit(parent=self.mainwindow):
1133
+ return
1134
+ funcs = [
1135
+ sipi.white_tophat,
1136
+ sipi.black_tophat,
1137
+ sipi.erosion,
1138
+ sipi.dilation,
1139
+ sipi.opening,
1140
+ sipi.closing,
1141
+ ]
1142
+ self.compute_multiple_1_to_1(funcs, [param] * len(funcs), "Morph", edit=False)
1143
+
1144
+ @qt_try_except()
1145
+ def compute_all_edges(self) -> None:
1146
+ """Compute all edge detection algorithms.
1147
+
1148
+ This function calls the following edge detection algorithms:
1149
+
1150
+ - :py:func:`sigima.proc.image.canny`
1151
+ - :py:func:`sigima.proc.image.farid`
1152
+ - :py:func:`sigima.proc.image.farid_h`
1153
+ - :py:func:`sigima.proc.image.farid_v`
1154
+ - :py:func:`sigima.proc.image.laplace`
1155
+ - :py:func:`sigima.proc.image.prewitt`
1156
+ - :py:func:`sigima.proc.image.prewitt_h`
1157
+ - :py:func:`sigima.proc.image.prewitt_v`
1158
+ - :py:func:`sigima.proc.image.roberts`
1159
+ - :py:func:`sigima.proc.image.scharr`
1160
+ - :py:func:`sigima.proc.image.scharr_h`
1161
+ - :py:func:`sigima.proc.image.scharr_v`
1162
+ - :py:func:`sigima.proc.image.sobel`
1163
+ - :py:func:`sigima.proc.image.sobel_h`
1164
+ - :py:func:`sigima.proc.image.sobel_v`
1165
+ """
1166
+ funcs = [
1167
+ sipi.canny,
1168
+ sipi.farid,
1169
+ sipi.farid_h,
1170
+ sipi.farid_v,
1171
+ sipi.laplace,
1172
+ sipi.prewitt,
1173
+ sipi.prewitt_h,
1174
+ sipi.prewitt_v,
1175
+ sipi.roberts,
1176
+ sipi.scharr,
1177
+ sipi.scharr_h,
1178
+ sipi.scharr_v,
1179
+ sipi.sobel,
1180
+ sipi.sobel_h,
1181
+ sipi.sobel_v,
1182
+ ]
1183
+ self.compute_multiple_1_to_1(funcs, None, "Edges")
1184
+
1185
+ @qt_try_except()
1186
+ def _extract_multiple_roi_in_single_object(self, params: list[ROI2DParam]) -> None:
1187
+ """Extract multiple Regions Of Interest (ROIs) from data in a single object"""
1188
+ # TODO: This `compute_1_to_1` call is not ideal, as it passes a list of
1189
+ # parameter sets (`params` is a list of `DataSet` objects) instead of a single
1190
+ # parameter set as expected by the method. Currently, the method implementation
1191
+ # is compatible with this call, and it simply passes the second argument through
1192
+ # to the `extract_rois` function. However, this should be rectified in the
1193
+ # future to ensure that the method signature and its usage are consistent.
1194
+ # The question is: should we pass a list of `DataSet` objects or directly a
1195
+ # `ImageROI` object?
1196
+ # (same as `compute_erase`)
1197
+ self.compute_1_to_1(sipi.extract_rois, params, title=_("Extract ROI"))
1198
+
1199
+ # ------Image Analysis
1200
+ @qt_try_except()
1201
+ def compute_peak_detection(
1202
+ self, param: sigima.params.Peak2DDetectionParam | None = None
1203
+ ) -> dict[str, GeometryResult]:
1204
+ """Compute 2D peak detection
1205
+ with :py:func:`sigima.proc.image.peak_detection`"""
1206
+ edit, param = self.init_param(
1207
+ param, sipi.Peak2DDetectionParam, _("Peak detection")
1208
+ )
1209
+ if edit:
1210
+ data = self.panel.objview.get_sel_objects(include_groups=True)[0].data
1211
+ param.size = max(min(data.shape) // 40, 50)
1212
+
1213
+ # Run peak detection (ROI creation is handled automatically by base class)
1214
+ return self.run_feature("peak_detection", param, edit=edit)