melage 0.0.65__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 (501) hide show
  1. melage/__init__.py +16 -0
  2. melage/cli.py +4 -0
  3. melage/graphics/GLGraphicsItem.py +286 -0
  4. melage/graphics/GLViewWidget.py +595 -0
  5. melage/graphics/Transform3D.py +55 -0
  6. melage/graphics/__init__.py +8 -0
  7. melage/graphics/functions.py +101 -0
  8. melage/graphics/items/GLAxisItem.py +149 -0
  9. melage/graphics/items/GLGridItem.py +178 -0
  10. melage/graphics/items/GLPolygonItem.py +77 -0
  11. melage/graphics/items/GLScatterPlotItem.py +135 -0
  12. melage/graphics/items/GLVolumeItem.py +280 -0
  13. melage/graphics/items/GLVolumeItem_b.py +237 -0
  14. melage/graphics/items/__init__.py +0 -0
  15. melage/graphics/shaders.py +202 -0
  16. melage/main.py +270 -0
  17. melage/requirements22.txt +25 -0
  18. melage/requirements_old.txt +28 -0
  19. melage/resource/0circle.png +0 -0
  20. melage/resource/0circle_faded.png +0 -0
  21. melage/resource/3d.png +0 -0
  22. melage/resource/3d.psd +0 -0
  23. melage/resource/3dFaded.png +0 -0
  24. melage/resource/Eraser.png +0 -0
  25. melage/resource/EraserFaded.png +0 -0
  26. melage/resource/EraserX.png +0 -0
  27. melage/resource/EraserXFaded.png +0 -0
  28. melage/resource/Eraser_icon.svg +79 -0
  29. melage/resource/Hand.png +0 -0
  30. melage/resource/HandIcons_0.png +0 -0
  31. melage/resource/Hand_IX.png +0 -0
  32. melage/resource/Hand_IXFaded.png +0 -0
  33. melage/resource/Handsqueezed.png +0 -0
  34. melage/resource/Handwriting (copy).png +0 -0
  35. melage/resource/Handwriting.png +0 -0
  36. melage/resource/HandwritingMinus.png +0 -0
  37. melage/resource/HandwritingMinusX.png +0 -0
  38. melage/resource/HandwritingPlus.png +0 -0
  39. melage/resource/HandwritingPlusX.png +0 -0
  40. melage/resource/Move_icon.svg +8 -0
  41. melage/resource/PngItem_2422924.png +0 -0
  42. melage/resource/about.png +0 -0
  43. melage/resource/about_logo.png +0 -0
  44. melage/resource/about_logo0.png +0 -0
  45. melage/resource/action_check.png +0 -0
  46. melage/resource/action_check_OFF.png +0 -0
  47. melage/resource/arrow).png +0 -0
  48. melage/resource/arrow.png +0 -0
  49. melage/resource/arrowFaded.png +0 -0
  50. melage/resource/arrow_org.png +0 -0
  51. melage/resource/arrow_org.png.png +0 -0
  52. melage/resource/arrows.png +0 -0
  53. melage/resource/authors.mp4 +0 -0
  54. melage/resource/box.png +0 -0
  55. melage/resource/check-image-icon-0.jpg +0 -0
  56. melage/resource/circle.png +0 -0
  57. melage/resource/circle_faded.png +0 -0
  58. melage/resource/circle_or.png +0 -0
  59. melage/resource/close.png +0 -0
  60. melage/resource/close_bg.png +0 -0
  61. melage/resource/color/Simple.txt +18 -0
  62. melage/resource/color/Tissue.txt +24 -0
  63. melage/resource/color/Tissue12.txt +27 -0
  64. melage/resource/color/albert_LUT.txt +102 -0
  65. melage/resource/color/mcrib_LUT.txt +102 -0
  66. melage/resource/color/pediatric1.txt +29 -0
  67. melage/resource/color/pediatric1_old.txt +27 -0
  68. melage/resource/color/pediatric2.txt +87 -0
  69. melage/resource/color/pediatric3.txt +29 -0
  70. melage/resource/color/pediatrics (copy).csv +103 -0
  71. melage/resource/color/tissue_seg.txt +4 -0
  72. melage/resource/contour.png +0 -0
  73. melage/resource/contour.svg +2 -0
  74. melage/resource/contourFaded.png +0 -0
  75. melage/resource/contourX.png +0 -0
  76. melage/resource/contourXFaded.png +0 -0
  77. melage/resource/dti.png +0 -0
  78. melage/resource/dti0.png +0 -0
  79. melage/resource/dti222.png +0 -0
  80. melage/resource/dti_or.png +0 -0
  81. melage/resource/eco.png +0 -0
  82. melage/resource/eco22.png +0 -0
  83. melage/resource/eco_old.png +0 -0
  84. melage/resource/eco_or.png +0 -0
  85. melage/resource/eco_or2.png +0 -0
  86. melage/resource/eco_seg.png +0 -0
  87. melage/resource/eco_seg_old.png +0 -0
  88. melage/resource/export.png +0 -0
  89. melage/resource/hand-grab-icon-10.jpg +0 -0
  90. melage/resource/hand-grab-icon-25.jpg +0 -0
  91. melage/resource/info.png +0 -0
  92. melage/resource/line.png +0 -0
  93. melage/resource/linefaded.png +0 -0
  94. melage/resource/load.png +0 -0
  95. melage/resource/main.ico +0 -0
  96. melage/resource/manual_images/3D_rightc.png +0 -0
  97. melage/resource/manual_images/3D_rightc_goto.png +0 -0
  98. melage/resource/manual_images/3D_rightc_paint.png +0 -0
  99. melage/resource/manual_images/3D_rightc_paint_draw1.png +0 -0
  100. melage/resource/manual_images/3D_rightc_paint_draw2.png +0 -0
  101. melage/resource/manual_images/3D_rightc_paint_render.png +0 -0
  102. melage/resource/manual_images/3D_rightc_paint_render2.png +0 -0
  103. melage/resource/manual_images/3D_rightc_paint_render3.png +0 -0
  104. melage/resource/manual_images/3D_rightc_paint_render4.png +0 -0
  105. melage/resource/manual_images/3D_rightc_paint_render5.png +0 -0
  106. melage/resource/manual_images/3D_rightc_paint_render6.png +0 -0
  107. melage/resource/manual_images/3D_rightc_seg.png +0 -0
  108. melage/resource/manual_images/exit_toolbar.png +0 -0
  109. melage/resource/manual_images/load_image_file.png +0 -0
  110. melage/resource/manual_images/load_image_file_openp.png +0 -0
  111. melage/resource/manual_images/main_page.png +0 -0
  112. melage/resource/manual_images/menu_file.png +0 -0
  113. melage/resource/manual_images/menu_file_export.png +0 -0
  114. melage/resource/manual_images/menu_file_import.png +0 -0
  115. melage/resource/manual_images/menu_file_settings.png +0 -0
  116. melage/resource/manual_images/menu_file_ss.png +0 -0
  117. melage/resource/manual_images/open_save_load.png +0 -0
  118. melage/resource/manual_images/panning_toolbar.png +0 -0
  119. melage/resource/manual_images/segmentation_toolbar.png +0 -0
  120. melage/resource/manual_images/tab_mri.png +0 -0
  121. melage/resource/manual_images/tab_us.png +0 -0
  122. melage/resource/manual_images/tabs.png +0 -0
  123. melage/resource/manual_images/toolbar_tools.png +0 -0
  124. melage/resource/manual_images/tools_basic.png +0 -0
  125. melage/resource/manual_images/tools_bet.png +0 -0
  126. melage/resource/manual_images/tools_cs.png +0 -0
  127. melage/resource/manual_images/tools_deepbet.png +0 -0
  128. melage/resource/manual_images/tools_imageinfo.png +0 -0
  129. melage/resource/manual_images/tools_maskO.png +0 -0
  130. melage/resource/manual_images/tools_masking.png +0 -0
  131. melage/resource/manual_images/tools_n4b.png +0 -0
  132. melage/resource/manual_images/tools_resize.png +0 -0
  133. melage/resource/manual_images/tools_ruler.png +0 -0
  134. melage/resource/manual_images/tools_seg.png +0 -0
  135. melage/resource/manual_images/tools_threshold.png +0 -0
  136. melage/resource/manual_images/tools_tools.png +0 -0
  137. melage/resource/manual_images/widget_color.png +0 -0
  138. melage/resource/manual_images/widget_color_add.png +0 -0
  139. melage/resource/manual_images/widget_color_add2.png +0 -0
  140. melage/resource/manual_images/widget_color_additional.png +0 -0
  141. melage/resource/manual_images/widget_images.png +0 -0
  142. melage/resource/manual_images/widget_images2.png +0 -0
  143. melage/resource/manual_images/widget_images3.png +0 -0
  144. melage/resource/manual_images/widget_marker.png +0 -0
  145. melage/resource/manual_images/widget_mri.png +0 -0
  146. melage/resource/manual_images/widget_mri2.png +0 -0
  147. melage/resource/manual_images/widget_segintensity.png +0 -0
  148. melage/resource/manual_images/widget_tab_mutualview.png +0 -0
  149. melage/resource/manual_images/widget_tab_mutualview2.png +0 -0
  150. melage/resource/manual_images/widget_table.png +0 -0
  151. melage/resource/manual_images/widget_table2.png +0 -0
  152. melage/resource/manual_images/widget_us.png +0 -0
  153. melage/resource/melage_top.ico +0 -0
  154. melage/resource/melage_top.png +0 -0
  155. melage/resource/melage_top0.png +0 -0
  156. melage/resource/melage_top1.png +0 -0
  157. melage/resource/melage_top4.png +0 -0
  158. melage/resource/mri (copy).png +0 -0
  159. melage/resource/mri.png +0 -0
  160. melage/resource/mri0.png +0 -0
  161. melage/resource/mri000.png +0 -0
  162. melage/resource/mri22.png +0 -0
  163. melage/resource/mri_big.png +0 -0
  164. melage/resource/mri_old.png +0 -0
  165. melage/resource/mri_seg.png +0 -0
  166. melage/resource/mri_seg_old.png +0 -0
  167. melage/resource/new.png +0 -0
  168. melage/resource/open.png +0 -0
  169. melage/resource/open2.png +0 -0
  170. melage/resource/pan.png +0 -0
  171. melage/resource/pencil.png +0 -0
  172. melage/resource/pencilFaded.png +0 -0
  173. melage/resource/points.png +0 -0
  174. melage/resource/pointsFaded.png +0 -0
  175. melage/resource/rotate.png +0 -0
  176. melage/resource/ruler.png +0 -0
  177. melage/resource/rulerFaded.png +0 -0
  178. melage/resource/s.png +0 -0
  179. melage/resource/s.psd +0 -0
  180. melage/resource/save.png +0 -0
  181. melage/resource/saveas.png +0 -0
  182. melage/resource/seg_mri.png +0 -0
  183. melage/resource/seg_mri2.png +0 -0
  184. melage/resource/settings.png +0 -0
  185. melage/resource/synch.png +0 -0
  186. melage/resource/synchFaded.png +0 -0
  187. melage/resource/theme/rc/.keep +1 -0
  188. melage/resource/theme/rc/arrow_down.png +0 -0
  189. melage/resource/theme/rc/arrow_down@2x.png +0 -0
  190. melage/resource/theme/rc/arrow_down_disabled.png +0 -0
  191. melage/resource/theme/rc/arrow_down_disabled@2x.png +0 -0
  192. melage/resource/theme/rc/arrow_down_focus.png +0 -0
  193. melage/resource/theme/rc/arrow_down_focus@2x.png +0 -0
  194. melage/resource/theme/rc/arrow_down_pressed.png +0 -0
  195. melage/resource/theme/rc/arrow_down_pressed@2x.png +0 -0
  196. melage/resource/theme/rc/arrow_left.png +0 -0
  197. melage/resource/theme/rc/arrow_left@2x.png +0 -0
  198. melage/resource/theme/rc/arrow_left_disabled.png +0 -0
  199. melage/resource/theme/rc/arrow_left_disabled@2x.png +0 -0
  200. melage/resource/theme/rc/arrow_left_focus.png +0 -0
  201. melage/resource/theme/rc/arrow_left_focus@2x.png +0 -0
  202. melage/resource/theme/rc/arrow_left_pressed.png +0 -0
  203. melage/resource/theme/rc/arrow_left_pressed@2x.png +0 -0
  204. melage/resource/theme/rc/arrow_right.png +0 -0
  205. melage/resource/theme/rc/arrow_right@2x.png +0 -0
  206. melage/resource/theme/rc/arrow_right_disabled.png +0 -0
  207. melage/resource/theme/rc/arrow_right_disabled@2x.png +0 -0
  208. melage/resource/theme/rc/arrow_right_focus.png +0 -0
  209. melage/resource/theme/rc/arrow_right_focus@2x.png +0 -0
  210. melage/resource/theme/rc/arrow_right_pressed.png +0 -0
  211. melage/resource/theme/rc/arrow_right_pressed@2x.png +0 -0
  212. melage/resource/theme/rc/arrow_up.png +0 -0
  213. melage/resource/theme/rc/arrow_up@2x.png +0 -0
  214. melage/resource/theme/rc/arrow_up_disabled.png +0 -0
  215. melage/resource/theme/rc/arrow_up_disabled@2x.png +0 -0
  216. melage/resource/theme/rc/arrow_up_focus.png +0 -0
  217. melage/resource/theme/rc/arrow_up_focus@2x.png +0 -0
  218. melage/resource/theme/rc/arrow_up_pressed.png +0 -0
  219. melage/resource/theme/rc/arrow_up_pressed@2x.png +0 -0
  220. melage/resource/theme/rc/base_icon.png +0 -0
  221. melage/resource/theme/rc/base_icon@2x.png +0 -0
  222. melage/resource/theme/rc/base_icon_disabled.png +0 -0
  223. melage/resource/theme/rc/base_icon_disabled@2x.png +0 -0
  224. melage/resource/theme/rc/base_icon_focus.png +0 -0
  225. melage/resource/theme/rc/base_icon_focus@2x.png +0 -0
  226. melage/resource/theme/rc/base_icon_pressed.png +0 -0
  227. melage/resource/theme/rc/base_icon_pressed@2x.png +0 -0
  228. melage/resource/theme/rc/branch_closed.png +0 -0
  229. melage/resource/theme/rc/branch_closed@2x.png +0 -0
  230. melage/resource/theme/rc/branch_closed_disabled.png +0 -0
  231. melage/resource/theme/rc/branch_closed_disabled@2x.png +0 -0
  232. melage/resource/theme/rc/branch_closed_focus.png +0 -0
  233. melage/resource/theme/rc/branch_closed_focus@2x.png +0 -0
  234. melage/resource/theme/rc/branch_closed_pressed.png +0 -0
  235. melage/resource/theme/rc/branch_closed_pressed@2x.png +0 -0
  236. melage/resource/theme/rc/branch_end.png +0 -0
  237. melage/resource/theme/rc/branch_end@2x.png +0 -0
  238. melage/resource/theme/rc/branch_end_disabled.png +0 -0
  239. melage/resource/theme/rc/branch_end_disabled@2x.png +0 -0
  240. melage/resource/theme/rc/branch_end_focus.png +0 -0
  241. melage/resource/theme/rc/branch_end_focus@2x.png +0 -0
  242. melage/resource/theme/rc/branch_end_pressed.png +0 -0
  243. melage/resource/theme/rc/branch_end_pressed@2x.png +0 -0
  244. melage/resource/theme/rc/branch_line.png +0 -0
  245. melage/resource/theme/rc/branch_line@2x.png +0 -0
  246. melage/resource/theme/rc/branch_line_disabled.png +0 -0
  247. melage/resource/theme/rc/branch_line_disabled@2x.png +0 -0
  248. melage/resource/theme/rc/branch_line_focus.png +0 -0
  249. melage/resource/theme/rc/branch_line_focus@2x.png +0 -0
  250. melage/resource/theme/rc/branch_line_pressed.png +0 -0
  251. melage/resource/theme/rc/branch_line_pressed@2x.png +0 -0
  252. melage/resource/theme/rc/branch_more.png +0 -0
  253. melage/resource/theme/rc/branch_more@2x.png +0 -0
  254. melage/resource/theme/rc/branch_more_disabled.png +0 -0
  255. melage/resource/theme/rc/branch_more_disabled@2x.png +0 -0
  256. melage/resource/theme/rc/branch_more_focus.png +0 -0
  257. melage/resource/theme/rc/branch_more_focus@2x.png +0 -0
  258. melage/resource/theme/rc/branch_more_pressed.png +0 -0
  259. melage/resource/theme/rc/branch_more_pressed@2x.png +0 -0
  260. melage/resource/theme/rc/branch_open.png +0 -0
  261. melage/resource/theme/rc/branch_open@2x.png +0 -0
  262. melage/resource/theme/rc/branch_open_disabled.png +0 -0
  263. melage/resource/theme/rc/branch_open_disabled@2x.png +0 -0
  264. melage/resource/theme/rc/branch_open_focus.png +0 -0
  265. melage/resource/theme/rc/branch_open_focus@2x.png +0 -0
  266. melage/resource/theme/rc/branch_open_pressed.png +0 -0
  267. melage/resource/theme/rc/branch_open_pressed@2x.png +0 -0
  268. melage/resource/theme/rc/checkbox_checked.png +0 -0
  269. melage/resource/theme/rc/checkbox_checked0.png +0 -0
  270. melage/resource/theme/rc/checkbox_checked@2x.png +0 -0
  271. melage/resource/theme/rc/checkbox_checked@2x0.png +0 -0
  272. melage/resource/theme/rc/checkbox_checked@2x000.png.png +0 -0
  273. melage/resource/theme/rc/checkbox_checked_disabled.png +0 -0
  274. melage/resource/theme/rc/checkbox_checked_disabled0.png +0 -0
  275. melage/resource/theme/rc/checkbox_checked_disabled@2x.png +0 -0
  276. melage/resource/theme/rc/checkbox_checked_disabled@2x0.png +0 -0
  277. melage/resource/theme/rc/checkbox_checked_focus.png +0 -0
  278. melage/resource/theme/rc/checkbox_checked_focus0.png +0 -0
  279. melage/resource/theme/rc/checkbox_checked_focus@2x.png +0 -0
  280. melage/resource/theme/rc/checkbox_checked_focus@2x0.png +0 -0
  281. melage/resource/theme/rc/checkbox_checked_pressed.png +0 -0
  282. melage/resource/theme/rc/checkbox_checked_pressed0.png +0 -0
  283. melage/resource/theme/rc/checkbox_checked_pressed@2x.png +0 -0
  284. melage/resource/theme/rc/checkbox_checked_pressed@2x0.png +0 -0
  285. melage/resource/theme/rc/checkbox_indeterminate.png +0 -0
  286. melage/resource/theme/rc/checkbox_indeterminate@2x.png +0 -0
  287. melage/resource/theme/rc/checkbox_indeterminate_disabled.png +0 -0
  288. melage/resource/theme/rc/checkbox_indeterminate_disabled@2x.png +0 -0
  289. melage/resource/theme/rc/checkbox_indeterminate_focus.png +0 -0
  290. melage/resource/theme/rc/checkbox_indeterminate_focus@2x.png +0 -0
  291. melage/resource/theme/rc/checkbox_indeterminate_pressed.png +0 -0
  292. melage/resource/theme/rc/checkbox_indeterminate_pressed@2x.png +0 -0
  293. melage/resource/theme/rc/checkbox_unchecked.png +0 -0
  294. melage/resource/theme/rc/checkbox_unchecked0.png +0 -0
  295. melage/resource/theme/rc/checkbox_unchecked00.png +0 -0
  296. melage/resource/theme/rc/checkbox_unchecked@2x.png +0 -0
  297. melage/resource/theme/rc/checkbox_unchecked@2x0.png +0 -0
  298. melage/resource/theme/rc/checkbox_unchecked@2x00.png +0 -0
  299. melage/resource/theme/rc/checkbox_unchecked_disabled.png +0 -0
  300. melage/resource/theme/rc/checkbox_unchecked_disabled0.png +0 -0
  301. melage/resource/theme/rc/checkbox_unchecked_disabled00.png +0 -0
  302. melage/resource/theme/rc/checkbox_unchecked_disabled@2x.png +0 -0
  303. melage/resource/theme/rc/checkbox_unchecked_disabled@2x0.png +0 -0
  304. melage/resource/theme/rc/checkbox_unchecked_disabled@2x00.png +0 -0
  305. melage/resource/theme/rc/checkbox_unchecked_focus.png +0 -0
  306. melage/resource/theme/rc/checkbox_unchecked_focus0.png +0 -0
  307. melage/resource/theme/rc/checkbox_unchecked_focus00.png +0 -0
  308. melage/resource/theme/rc/checkbox_unchecked_focus@2x.png +0 -0
  309. melage/resource/theme/rc/checkbox_unchecked_focus@2x0.png +0 -0
  310. melage/resource/theme/rc/checkbox_unchecked_focus@2x00.png +0 -0
  311. melage/resource/theme/rc/checkbox_unchecked_pressed.png +0 -0
  312. melage/resource/theme/rc/checkbox_unchecked_pressed0.png +0 -0
  313. melage/resource/theme/rc/checkbox_unchecked_pressed00.png +0 -0
  314. melage/resource/theme/rc/checkbox_unchecked_pressed@2x.png +0 -0
  315. melage/resource/theme/rc/checkbox_unchecked_pressed@2x0.png +0 -0
  316. melage/resource/theme/rc/checkbox_unchecked_pressed@2x00.png +0 -0
  317. melage/resource/theme/rc/line_horizontal.png +0 -0
  318. melage/resource/theme/rc/line_horizontal@2x.png +0 -0
  319. melage/resource/theme/rc/line_horizontal_disabled.png +0 -0
  320. melage/resource/theme/rc/line_horizontal_disabled@2x.png +0 -0
  321. melage/resource/theme/rc/line_horizontal_focus.png +0 -0
  322. melage/resource/theme/rc/line_horizontal_focus@2x.png +0 -0
  323. melage/resource/theme/rc/line_horizontal_pressed.png +0 -0
  324. melage/resource/theme/rc/line_horizontal_pressed@2x.png +0 -0
  325. melage/resource/theme/rc/line_vertical.png +0 -0
  326. melage/resource/theme/rc/line_vertical@2x.png +0 -0
  327. melage/resource/theme/rc/line_vertical_disabled.png +0 -0
  328. melage/resource/theme/rc/line_vertical_disabled@2x.png +0 -0
  329. melage/resource/theme/rc/line_vertical_focus.png +0 -0
  330. melage/resource/theme/rc/line_vertical_focus@2x.png +0 -0
  331. melage/resource/theme/rc/line_vertical_pressed.png +0 -0
  332. melage/resource/theme/rc/line_vertical_pressed@2x.png +0 -0
  333. melage/resource/theme/rc/radio_checked.png +0 -0
  334. melage/resource/theme/rc/radio_checked@2x.png +0 -0
  335. melage/resource/theme/rc/radio_checked_disabled.png +0 -0
  336. melage/resource/theme/rc/radio_checked_disabled@2x.png +0 -0
  337. melage/resource/theme/rc/radio_checked_focus.png +0 -0
  338. melage/resource/theme/rc/radio_checked_focus@2x.png +0 -0
  339. melage/resource/theme/rc/radio_checked_pressed.png +0 -0
  340. melage/resource/theme/rc/radio_checked_pressed@2x.png +0 -0
  341. melage/resource/theme/rc/radio_unchecked.png +0 -0
  342. melage/resource/theme/rc/radio_unchecked@2x.png +0 -0
  343. melage/resource/theme/rc/radio_unchecked_disabled.png +0 -0
  344. melage/resource/theme/rc/radio_unchecked_disabled@2x.png +0 -0
  345. melage/resource/theme/rc/radio_unchecked_focus.png +0 -0
  346. melage/resource/theme/rc/radio_unchecked_focus@2x.png +0 -0
  347. melage/resource/theme/rc/radio_unchecked_pressed.png +0 -0
  348. melage/resource/theme/rc/radio_unchecked_pressed@2x.png +0 -0
  349. melage/resource/theme/rc/toolbar_move_horizontal.png +0 -0
  350. melage/resource/theme/rc/toolbar_move_horizontal@2x.png +0 -0
  351. melage/resource/theme/rc/toolbar_move_horizontal_disabled.png +0 -0
  352. melage/resource/theme/rc/toolbar_move_horizontal_disabled@2x.png +0 -0
  353. melage/resource/theme/rc/toolbar_move_horizontal_focus.png +0 -0
  354. melage/resource/theme/rc/toolbar_move_horizontal_focus@2x.png +0 -0
  355. melage/resource/theme/rc/toolbar_move_horizontal_pressed.png +0 -0
  356. melage/resource/theme/rc/toolbar_move_horizontal_pressed@2x.png +0 -0
  357. melage/resource/theme/rc/toolbar_move_vertical.png +0 -0
  358. melage/resource/theme/rc/toolbar_move_vertical@2x.png +0 -0
  359. melage/resource/theme/rc/toolbar_move_vertical_disabled.png +0 -0
  360. melage/resource/theme/rc/toolbar_move_vertical_disabled@2x.png +0 -0
  361. melage/resource/theme/rc/toolbar_move_vertical_focus.png +0 -0
  362. melage/resource/theme/rc/toolbar_move_vertical_focus@2x.png +0 -0
  363. melage/resource/theme/rc/toolbar_move_vertical_pressed.png +0 -0
  364. melage/resource/theme/rc/toolbar_move_vertical_pressed@2x.png +0 -0
  365. melage/resource/theme/rc/toolbar_separator_horizontal.png +0 -0
  366. melage/resource/theme/rc/toolbar_separator_horizontal@2x.png +0 -0
  367. melage/resource/theme/rc/toolbar_separator_horizontal_disabled.png +0 -0
  368. melage/resource/theme/rc/toolbar_separator_horizontal_disabled@2x.png +0 -0
  369. melage/resource/theme/rc/toolbar_separator_horizontal_focus.png +0 -0
  370. melage/resource/theme/rc/toolbar_separator_horizontal_focus@2x.png +0 -0
  371. melage/resource/theme/rc/toolbar_separator_horizontal_pressed.png +0 -0
  372. melage/resource/theme/rc/toolbar_separator_horizontal_pressed@2x.png +0 -0
  373. melage/resource/theme/rc/toolbar_separator_vertical.png +0 -0
  374. melage/resource/theme/rc/toolbar_separator_vertical@2x.png +0 -0
  375. melage/resource/theme/rc/toolbar_separator_vertical_disabled.png +0 -0
  376. melage/resource/theme/rc/toolbar_separator_vertical_disabled@2x.png +0 -0
  377. melage/resource/theme/rc/toolbar_separator_vertical_focus.png +0 -0
  378. melage/resource/theme/rc/toolbar_separator_vertical_focus@2x.png +0 -0
  379. melage/resource/theme/rc/toolbar_separator_vertical_pressed.png +0 -0
  380. melage/resource/theme/rc/toolbar_separator_vertical_pressed@2x.png +0 -0
  381. melage/resource/theme/rc/transparent.png +0 -0
  382. melage/resource/theme/rc/transparent@2x.png +0 -0
  383. melage/resource/theme/rc/transparent_disabled.png +0 -0
  384. melage/resource/theme/rc/transparent_disabled@2x.png +0 -0
  385. melage/resource/theme/rc/transparent_focus.png +0 -0
  386. melage/resource/theme/rc/transparent_focus@2x.png +0 -0
  387. melage/resource/theme/rc/transparent_pressed.png +0 -0
  388. melage/resource/theme/rc/transparent_pressed@2x.png +0 -0
  389. melage/resource/theme/rc/window_close.png +0 -0
  390. melage/resource/theme/rc/window_close@2x.png +0 -0
  391. melage/resource/theme/rc/window_close_disabled.png +0 -0
  392. melage/resource/theme/rc/window_close_disabled@2x.png +0 -0
  393. melage/resource/theme/rc/window_close_focus.png +0 -0
  394. melage/resource/theme/rc/window_close_focus@2x.png +0 -0
  395. melage/resource/theme/rc/window_close_pressed.png +0 -0
  396. melage/resource/theme/rc/window_close_pressed@2x.png +0 -0
  397. melage/resource/theme/rc/window_grip.png +0 -0
  398. melage/resource/theme/rc/window_grip@2x.png +0 -0
  399. melage/resource/theme/rc/window_grip_disabled.png +0 -0
  400. melage/resource/theme/rc/window_grip_disabled@2x.png +0 -0
  401. melage/resource/theme/rc/window_grip_focus.png +0 -0
  402. melage/resource/theme/rc/window_grip_focus@2x.png +0 -0
  403. melage/resource/theme/rc/window_grip_pressed.png +0 -0
  404. melage/resource/theme/rc/window_grip_pressed@2x.png +0 -0
  405. melage/resource/theme/rc/window_minimize.png +0 -0
  406. melage/resource/theme/rc/window_minimize@2x.png +0 -0
  407. melage/resource/theme/rc/window_minimize_disabled.png +0 -0
  408. melage/resource/theme/rc/window_minimize_disabled@2x.png +0 -0
  409. melage/resource/theme/rc/window_minimize_focus.png +0 -0
  410. melage/resource/theme/rc/window_minimize_focus@2x.png +0 -0
  411. melage/resource/theme/rc/window_minimize_pressed.png +0 -0
  412. melage/resource/theme/rc/window_minimize_pressed@2x.png +0 -0
  413. melage/resource/theme/rc/window_undock.png +0 -0
  414. melage/resource/theme/rc/window_undock@2x.png +0 -0
  415. melage/resource/theme/rc/window_undock_disabled.png +0 -0
  416. melage/resource/theme/rc/window_undock_disabled@2x.png +0 -0
  417. melage/resource/theme/rc/window_undock_focus.png +0 -0
  418. melage/resource/theme/rc/window_undock_focus@2x.png +0 -0
  419. melage/resource/theme/rc/window_undock_pressed.png +0 -0
  420. melage/resource/theme/rc/window_undock_pressed@2x.png +0 -0
  421. melage/resource/theme/style.qss +2223 -0
  422. melage/resource/tract.png +0 -0
  423. melage/resource/view1.png +0 -0
  424. melage/resource/view1_eco.png +0 -0
  425. melage/resource/view1_mri.png +0 -0
  426. melage/resource/view1_seg.png +0 -0
  427. melage/resource/view2.png +0 -0
  428. melage/resource/view2_seg.png +0 -0
  429. melage/resource/w.png +0 -0
  430. melage/resource/zoom_in.png +0 -0
  431. melage/resource/zoom_inFaded.png +0 -0
  432. melage/resource/zoom_out.png +0 -0
  433. melage/resource/zoom_outFaded.png +0 -0
  434. melage/some_notes.txt +3 -0
  435. melage/utils/DispalyIm.py +2788 -0
  436. melage/utils/GMM.py +720 -0
  437. melage/utils/Shaders_120.py +257 -0
  438. melage/utils/Shaders_330.py +314 -0
  439. melage/utils/Shaders_bu.py +314 -0
  440. melage/utils/__init__0.py +7 -0
  441. melage/utils/brain_extraction_helper.py +234 -0
  442. melage/utils/custom_QScrollBar.py +61 -0
  443. melage/utils/glScientific.py +1554 -0
  444. melage/utils/glScientific_bc.py +1585 -0
  445. melage/utils/readData.py +1061 -0
  446. melage/utils/registration.py +512 -0
  447. melage/utils/source_folder.py +18 -0
  448. melage/utils/utils.py +3808 -0
  449. melage/version.txt +1 -0
  450. melage/widgets/ApplyMask.py +212 -0
  451. melage/widgets/ChangeSystem.py +152 -0
  452. melage/widgets/DeepLModels/InfantSegment/Unet.py +464 -0
  453. melage/widgets/DeepLModels/NPP/dataset/mri_dataset_affine.py +149 -0
  454. melage/widgets/DeepLModels/NPP/models/checkpoints/npp_v1.pth.py +0 -0
  455. melage/widgets/DeepLModels/NPP/models/losses.py +146 -0
  456. melage/widgets/DeepLModels/NPP/models/model.py +272 -0
  457. melage/widgets/DeepLModels/NPP/models/utils.py +303 -0
  458. melage/widgets/DeepLModels/NPP/npp.py +116 -0
  459. melage/widgets/DeepLModels/NPP/requirements.txt +8 -0
  460. melage/widgets/DeepLModels/NPP/train/train.py +116 -0
  461. melage/widgets/DeepLModels/Unet3DAtt.py +657 -0
  462. melage/widgets/DeepLModels/Unet3D_basic.py +648 -0
  463. melage/widgets/DeepLModels/new_unet.py +652 -0
  464. melage/widgets/DeepLModels/new_unet_old.py +639 -0
  465. melage/widgets/DeepLModels/new_unet_old2.py +658 -0
  466. melage/widgets/HistImage.py +153 -0
  467. melage/widgets/ImageThresholding.py +222 -0
  468. melage/widgets/MaskOperations.py +147 -0
  469. melage/widgets/N4Dialog.py +241 -0
  470. melage/widgets/Segmentation/FCM.py +1553 -0
  471. melage/widgets/Segmentation/__init__.py +588 -0
  472. melage/widgets/Segmentation/utils.py +417 -0
  473. melage/widgets/SemiAutoSeg.py +666 -0
  474. melage/widgets/Synthstrip.py +141 -0
  475. melage/widgets/__init__0.py +5 -0
  476. melage/widgets/about.py +246 -0
  477. melage/widgets/activation.py +437 -0
  478. melage/widgets/activator.py +147 -0
  479. melage/widgets/be_dl.py +409 -0
  480. melage/widgets/be_dl_unet3d.py +441 -0
  481. melage/widgets/brain_extraction.py +855 -0
  482. melage/widgets/brain_extraction_dl.py +887 -0
  483. melage/widgets/brain_extraction_dl_bu.py +869 -0
  484. melage/widgets/colorwidget.py +100 -0
  485. melage/widgets/dockWidgets.py +2005 -0
  486. melage/widgets/enhanceImWidget.py +109 -0
  487. melage/widgets/fileDialog_widget.py +275 -0
  488. melage/widgets/iminfo.py +346 -0
  489. melage/widgets/mainwindow_widget.py +6775 -0
  490. melage/widgets/melageAbout.py +123 -0
  491. melage/widgets/openglWidgets.py +556 -0
  492. melage/widgets/registrationWidget.py +342 -0
  493. melage/widgets/repeat_widget.py +74 -0
  494. melage/widgets/screenshot_widget.py +138 -0
  495. melage/widgets/settings_widget.py +77 -0
  496. melage/widgets/tranformationWidget.py +275 -0
  497. melage-0.0.65.dist-info/METADATA +742 -0
  498. melage-0.0.65.dist-info/RECORD +501 -0
  499. melage-0.0.65.dist-info/WHEEL +5 -0
  500. melage-0.0.65.dist-info/entry_points.txt +2 -0
  501. melage-0.0.65.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2788 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ __AUTHOR__ = 'Bahram Jafrasteh'
5
+ import os
6
+ from PyQt5 import QtWidgets, QtCore, QtGui
7
+ from melage.utils.utils import cursorPaint, zonePoint, cursorOpenHand, \
8
+ try_disconnect, cursorErase,\
9
+ ConvertPToPolygons, ConvertPointsToPolygons, findIndexWhiteVoxels, generate_extrapoint_on_line, PermuteProperAxis, magic_selection,permute_axis
10
+ #import OpenGL.GL as gl
11
+ from OpenGL.GL import *
12
+ import sys
13
+ import numpy as np
14
+ from skimage.segmentation import flood
15
+ from PyQt5.QtCore import pyqtSignal, QPoint, QSize, Qt, QEvent
16
+ from PyQt5.QtGui import (QColor, QMatrix4x4, QOpenGLShader, QKeyEvent,
17
+ QOpenGLShaderProgram, QPainter, QWheelEvent)
18
+ from PyQt5.QtWidgets import QOpenGLWidget, QWidget, QMenu, QAction
19
+ import cv2
20
+ import math
21
+
22
+ from collections import defaultdict
23
+ from sys import platform
24
+ if platform=='darwin':
25
+ from melage.utils.Shaders_120 import vsrc, fsrc, fsrcPaint, vsrcPaint
26
+ else:
27
+ from melage.utils.Shaders_330 import vsrc, fsrc, fsrcPaint, vsrcPaint
28
+ from melage.utils.GMM import GaussianMixture
29
+ from melage.utils.utils import LargestCC
30
+ from sklearn.mixture import GaussianMixture
31
+
32
+ """
33
+ import pyfftw
34
+ fft = pyfftw.interfaces.numpy_fft.fft2
35
+ ifft = pyfftw.interfaces.numpy_fft.ifft2
36
+ fftshift = pyfftw.interfaces.numpy_fft.fftshift
37
+ ifftshift = pyfftw.interfaces.numpy_fft.ifftshift
38
+ """
39
+ fft = np.fft.fft2
40
+ ifft = np.fft.ifft2
41
+ fftshift = np.fft.fftshift
42
+ ifftshift = np.fft.ifftshift
43
+
44
+ class GLWidget(QOpenGLWidget):
45
+ """
46
+ The main class to visualize image using MELAGE
47
+ """
48
+ ##### the list of signals used in the class #####
49
+ zRotationChanged = pyqtSignal(int) #change rotation angle
50
+ clicked = pyqtSignal() #
51
+ segChanged = pyqtSignal(object, object, object, object) # segmentation changed
52
+ LineChanged = pyqtSignal(object) # line changed
53
+ goto = pyqtSignal(object, object) # if go to signal is activated
54
+ zoomchanged = pyqtSignal(object, object) # zooming is activated
55
+ rulerInfo = pyqtSignal(object, object) # ruler is activated
56
+ sliceNChanged = pyqtSignal(object) # changing slice number
57
+ NewPoints = pyqtSignal(object, object) # adding new points
58
+ mousePress = pyqtSignal(object) # if mouse pressed
59
+ interpolate = pyqtSignal(object) # if interpolation is required
60
+
61
+ def __init__(self, colorsCombinations, parent=None, currentWidnowName = 'sagittal',
62
+ imdata=None, type= 'eco',id=0
63
+ ):
64
+ super(GLWidget, self).__init__(parent)
65
+ #print(self.format().version())
66
+ #fmt =QtGui.QSurfaceFormat()
67
+ #fmt.setVersion(1, 1)
68
+ #fmt.setProfile(QtGui.QSurfaceFormat.CoreProfile)
69
+ #self.setFormat(fmt)
70
+ self.id = id # unique id of the class
71
+ self.colorsCombinations = colorsCombinations # coloring scheme
72
+ self.color_name = []
73
+ self.imType = type # image type
74
+ self.affine = None # image affine
75
+ self.tract = None # tractography
76
+ self.colorObject = [1,0,0,1] # RGBA
77
+ self._magic_slice = None
78
+ self.colorInd = 9876 # index of colr
79
+ self.colorInds = [9876] # indices of colors
80
+ self.intensitySeg = 1.0 # intensity of the segmentation
81
+ self.N_rulerPoints = 0 # number of rulers used
82
+ self.showAxis = False # show axis
83
+ self.n_colors = 1 # number of colors used
84
+
85
+ self._n_colors_previous = np.inf
86
+ self.smooth = True # smoothing visualization
87
+ self._threshold_image = defaultdict(list) # thresholding image
88
+ self._allowed_goto = False
89
+ self._selected_seg_color = 1 # segmentation color
90
+
91
+ self.currentWidnowName = currentWidnowName.lower() # name of the window
92
+ if self.currentWidnowName == 'coronal':
93
+ self.colorv = [1,0,1,1]
94
+ self.colorh = [0,0,1,1]
95
+ elif self.currentWidnowName == 'sagittal':
96
+ self.colorv = [1,0,1,1]
97
+ self.colorh = [1,0,0,1]
98
+ elif self.currentWidnowName == 'axial':
99
+ self.colorh = [0,0,1,1]
100
+ self.colorv = [1,0,0,1]
101
+ self.setContextMenuPolicy(Qt.CustomContextMenu) # enable right click
102
+ if imdata is not None:
103
+ imdata = self.updateImage(imdata)
104
+ #self.setNewImage.emit(imdata.shape)
105
+
106
+ self.imSeg = None # segmentaiton image
107
+ self.imSlice = imdata # slice of the image
108
+ self.installEventFilter(self)
109
+ self.program = []
110
+ self.showSeg = True # visualize segmentation or not
111
+ self.width_line_tract = 3 # width line for tractography
112
+ self.polygon_info = [] # inforamtion of polygon
113
+ if imdata is not None:
114
+ self.imSeg = np.zeros_like(imdata) #
115
+ else:
116
+ self.imSeg = None
117
+
118
+
119
+ self.initialState()
120
+
121
+ def clear(self):
122
+ """
123
+ clear image slices
124
+ :return:
125
+ """
126
+ self.imSeg = None
127
+ self.imSlice = None
128
+
129
+ def _updateScreen(self, screen):
130
+ """
131
+ :param screen:
132
+ :return:
133
+ """
134
+ self._updatePixelRatio()
135
+ if screen is not None:
136
+ screen.physicalDotsPerInchChanged.connect(self._updatePixelRatio)
137
+ screen.logicalDotsPerInchChanged.connect(self._updatePixelRatio)
138
+ def _updatePixelRatio(self):
139
+ """
140
+ By Bahram Jafrasteh
141
+ :return:
142
+ """
143
+ event = QtGui.QResizeEvent(self.size(), self.size())
144
+ self.resizeEvent(event)
145
+
146
+ def resetInit(self):
147
+ # reset class parameters
148
+ self.enabledPan = False # Panning
149
+ self.enabledCircle = False # circle segmentation
150
+ self._NenabledCircle = 1 # circle segmentation
151
+ self._center_circle = [] # center of circles
152
+ self._radius_circle = 5 # radius of circles
153
+ self._tol_magic_tool = 5 #tolerance magic tool
154
+ self._tol_cricle_tool = 1 # tolerance circle tool
155
+ self.enabledRotate = False # Rotating
156
+ self.enabledPen = False # Polygon Drawing
157
+ self.enabledMagicTool = False #FreeHand Drawing
158
+ self.enabledErase = False # Erasing the points
159
+ self.enabledZoom = False # Zooming
160
+ self.enabledPointSelection = False # Point Selection
161
+ self.enabledGoTo = False # GOTO
162
+ self.enabledRuler = False # Ruller
163
+ self.enabledLine = False # Line
164
+ self.tract = None # Tractography
165
+ self.width_line_tract = 3 # width tractography
166
+ self._selected_seg_color = 1 # color of the segmetation
167
+ self.points = [] # points selected
168
+ self.penPoints = [] # pen points selected
169
+ self.erasePoints = [] # points that surrond subject to erase it
170
+ self.rulerPoints = defaultdict(list) # ruler points
171
+ self.linePoints = [] # line points
172
+ self.startLinePoints = [] # start line points
173
+ self.guidelines_h = [] # guide lines horizontal
174
+ self.guidelines_v = [] # guide lines vertical
175
+ self.selectedPoints = [] # selected points
176
+ self.colorObject = [1,0,0,1] # color object
177
+ self.colorInd = 9876 # color index
178
+ self.colorInds = [9876] # color indices
179
+ self.affine = np.eye(4)
180
+
181
+ def initialState(self):
182
+ """
183
+ Initial state parameters
184
+ :return:
185
+ """
186
+ self.BandPR1 = 0.0 # bandpass radius 1
187
+ self.BandPR2 = 0.0 # bandpass radius 2
188
+ self.contrast = 1.0 # image contrast
189
+ self.brightness = 0.0 # image brightness
190
+ self.activateSobel = 0 # activate sobel filter
191
+ self.thresholdSobel = 1 # activate sobel threshold
192
+ self.n_colors = 1 # number of colors
193
+ self.tract = None # tractography
194
+ self._selected_seg_color = 1 # selected segmentation color
195
+ self._n_colors_previous = np.inf # number of colors previous
196
+ self.width_line_tract = 3 # width line tractography
197
+ self.hamming = False # use hamming filter
198
+ self.widthPen = 0.0 # width pen
199
+ self.senderPoints = None # sender points
200
+ self.senderWindow = None # sender window
201
+ # update image and info
202
+ #self.updateInfo()
203
+ self.setFocusPolicy(Qt.StrongFocus)
204
+ self.clearColor = QColor(Qt.black)
205
+ self.xRot = 0 # rotation in x
206
+ self.yRot = 0 # rotation in y
207
+ self.zRot = 0 # rotation in z
208
+ self._threshold_image = defaultdict(list) # image thresholding
209
+
210
+ self.enabledPan = False # Panning
211
+ self.enabledCircle = False # circle
212
+ self._NenabledCircle = 1
213
+ self._center_circle = []
214
+ #self._radius_circle = 5
215
+ """
216
+
217
+ self.enabledRotate = False # Rotating
218
+ self.enabledPen = False # Polygon Drawing
219
+ self.enabledMagicTool = False #FreeHand Drawing
220
+ self.enabledErase = False # Erasing the points
221
+ self.enabledZoom = False # ZOOM DISABLED
222
+ self.enabledPointSelection = False # Point Selection
223
+ self.enabledGoTo = False
224
+ self.enabledRuler = False # Ruler
225
+ self.enabledLine = False
226
+ """
227
+ self.lastPos = QPoint()
228
+ self.points = []
229
+ self.penPoints = []
230
+ self.erasePoints = []
231
+ self.rulerPoints = defaultdict(list)
232
+ self.linePoints = []
233
+ self.guidelines_h = []
234
+ self.guidelines_v = []
235
+ self.startLinePoints = []
236
+ self.selectedPoints = []
237
+ #Initialize camera values
238
+ self.left = 0
239
+ self.bottom = 0
240
+ self.zoom_level_x = 1
241
+ self.zoom_level_y = 1
242
+
243
+ self.ZOOM_IN_FACTOR = 1.05
244
+ self.ZOOM_OUT_FACTOR = 1 / self.ZOOM_IN_FACTOR
245
+ self._kspaces = defaultdict(list)
246
+
247
+ self.setCursor(Qt.ArrowCursor)
248
+
249
+
250
+ def create_histogram(self, img, n_components= 5, plot=False):
251
+ # Colorize image based on Gaussian Mixture Models
252
+ """
253
+ :param img: image
254
+ :param n_components: integer (number of components)
255
+ :param plot:
256
+ :return:
257
+ """
258
+ from sklearn.preprocessing import OneHotEncoder
259
+
260
+ if n_components> len(self.colorsCombinations):
261
+ n_components = len(self.colorsCombinations)-1
262
+ #data = img.ravel()
263
+ #min_pixel = 1
264
+
265
+ # Fit GMM
266
+ x, y = np.where(img)
267
+ vals = img[tuple(zip(*(np.array((x, y)).T)))]
268
+ data_im = np.array([x, y, vals]).T
269
+ if self.segSlice.max()>0:
270
+ sgm = self.segSlice[tuple(zip(*(np.array((x, y)).T)))].reshape(-1, 1)
271
+ dm = OneHotEncoder(sparse=False).fit_transform(sgm)
272
+ data_im = np.hstack([dm, data_im])
273
+ gmm = GaussianMixture(n_components=n_components, covariance_type = 'diag')
274
+ try:
275
+ pred = gmm.fit_predict(X=(data_im-data_im.mean(0))/data_im.std(0))+1
276
+ except:
277
+ return
278
+ img2 = img.copy()
279
+ img2[tuple(zip(*(np.array((x, y)).T)))] = pred
280
+
281
+ list_keys = [key for key in self.colorsCombinations if self.colorsCombinations[key]!=[]]
282
+ key_comp = np.random.permutation(list_keys)[:n_components]
283
+ # Evaluate GMM
284
+ threshold_image = np.tile(np.expand_dims(img, -1), 3)*0.0
285
+ m_old =-1.0
286
+ for com in np.unique(pred):
287
+
288
+ color = self.colorsCombinations[key_comp[com-1]]
289
+ ind = img2 == com
290
+ try:
291
+ threshold_image[ind, 0] = color[0]*255.0
292
+ threshold_image[ind, 1] = color[1]*255.0
293
+ threshold_image[ind, 2] = color[2]*255.0
294
+ except:
295
+ print('GMM wrong color {}'.format(com))
296
+
297
+
298
+ self._threshold_image[self.sliceNum] = threshold_image.astype(np.float64)
299
+
300
+
301
+ def changeView(self, currentWidnowName, zRot):
302
+ """
303
+ Utilities to help change coronal to sagital vice versa
304
+ :param currentWidnowName: window name
305
+ :param zRot: rotation
306
+ :return:
307
+ """
308
+ self.currentWidnowName = currentWidnowName
309
+ self.initialState()
310
+ self.zRot = zRot
311
+ #self.updateInfo()
312
+ #self.update()
313
+
314
+ def updateCurrentImageInfo(self, shape):
315
+ """
316
+ Updating image info using shape information and according to current slice name
317
+ :param shape:
318
+ :return:
319
+ """
320
+ self.imXMax, self.imYMax, self.imZMax = shape
321
+
322
+ if self.currentWidnowName == 'sagittal':
323
+ self.labelX = 'C'
324
+ self.colorXID = Qt.red
325
+ self.colorX = [1, 0, 0]
326
+
327
+ self.labelY = 'A'
328
+ self.colorYID = Qt.magenta
329
+ self.colorY = [1, 0, 1]
330
+
331
+ self.activeDim = 2 # matrix for slicing
332
+ self.imWidth = self.imYMax
333
+ self.imHeight = self.imXMax
334
+ self.imDepth = self.imZMax
335
+ elif self.currentWidnowName == 'axial':
336
+ self.labelX = 'S'
337
+ self.colorXID = Qt.blue
338
+ self.colorX = [0, 0, 1]
339
+
340
+ self.labelY = 'C'
341
+ self.colorYID = Qt.red
342
+ self.colorY = [1, 0, 0]
343
+
344
+ self.activeDim = 0# matrix for slicing
345
+ self.imWidth = self.imZMax
346
+ self.imHeight = self.imYMax
347
+ self.imDepth = self.imXMax
348
+ elif self.currentWidnowName == 'coronal':
349
+ self.labelX = 'S'
350
+ self.colorXID = Qt.blue
351
+ self.colorX = [0, 0, 1]
352
+
353
+ self.labelY = 'A'
354
+ self.colorYID = Qt.magenta
355
+ self.colorY = [1, 0, 1]
356
+
357
+ self.activeDim = 1# matrix for slicing
358
+ self.imWidth = self.imZMax
359
+ self.imHeight = self.imXMax
360
+ self.imDepth = self.imYMax
361
+
362
+ self.imAr = self.imWidth / self.imHeight # image aspect ratio
363
+
364
+ self.maxAllowedDis = math.hypot(self.imWidth/2, self.imHeight/2)/1.0 # maximum allowed distance
365
+ ############################################################################
366
+ self.coord = [(0, 0), (0, 1), (1, 1), (1, 0)]
367
+ self.vertex = [(0, 0), (0, self.imHeight), (self.imWidth, self.imHeight), (self.imWidth, 0)]
368
+
369
+
370
+
371
+ #Initialize camera values
372
+ self.left = 0
373
+ self.bottom = 0
374
+ self.zoom_level_x = 1
375
+ self.zoom_level_y = 1
376
+ self.ZOOM_IN_FACTOR = 1.05
377
+ self.ZOOM_OUT_FACTOR = 1 / self.ZOOM_IN_FACTOR
378
+
379
+ def updateInfo(self, imSlice = None, segSlice = None, tract = None, sliceNum = None, shape = None,
380
+ initialState = False, imSpacing=None):
381
+ """
382
+ Updaing class according to given information
383
+ :param imSlice: image slice
384
+ :param segSlice: segmentation slice
385
+ :param tract: tractography
386
+ :param sliceNum: slice number
387
+ :param shape: shape
388
+ :param initialState: boolean
389
+ :param imSpacing: image spacing
390
+ :return:
391
+ """
392
+ self.sliceNum = sliceNum
393
+ self.imSlice = imSlice
394
+ self.segSlice = segSlice
395
+
396
+ self.imSpacing = imSpacing
397
+ self.tract = tract
398
+
399
+ #if imdata is not None:
400
+ #self.imdata = self.updateImage(imdata)
401
+ #self.setNewImage.emit(shape)
402
+ if initialState:
403
+ self.initialState()
404
+ self.updateCurrentImageInfo(shape)
405
+ self.UpdatePaintInfo()
406
+
407
+ self._kspaces = defaultdict(list)
408
+ if self.enabledMagicTool or self.enabledCircle:
409
+ self._magic_slice = None
410
+
411
+
412
+
413
+
414
+
415
+ self.makeObject()
416
+
417
+ def ShowContextMenu(self, pos):
418
+ """
419
+ Context Menu for the current class when panning allowed
420
+ :param pos:
421
+ :return:
422
+ """
423
+ menu = QMenu("Edit")
424
+ remove_action = QAction("Close polygon")
425
+ remove_action.triggered.connect(self.closePolygon)
426
+ escape_action = QAction("Cancel")
427
+ escape_action.triggered.connect(self.escapePolygon)
428
+
429
+ menu.addAction(remove_action)
430
+ menu.addAction(escape_action)
431
+ menu.exec_(self.mapToGlobal(pos))
432
+
433
+ def ShowContextMenu_ruler(self, pos):
434
+ """
435
+ Using RULER
436
+ :param pos:
437
+ :return:
438
+ """
439
+ menu = QMenu("Ruler")
440
+ remove_action = QAction("Remove")
441
+ self.key_min_ruler, length, pos_ruler = self._compute_distance_ruler(pos)
442
+ lenght = self.imSpacing[0]*length
443
+ distance_action = QAction("Segment Length {:.2f}mm".format(lenght))
444
+ try:
445
+ angle = self.rulerPoints[self.key_min_ruler]['angle']
446
+ except:
447
+ angle = 0
448
+ pos_ruler = [0, 0]
449
+
450
+ if type(angle)!=float:
451
+ return
452
+ angle_action = QAction("Angle {:.2f} degrees".format(angle))
453
+ xy_action = QAction("Center X {:.1f}, Y {:.1f}".format(pos_ruler[0], pos_ruler[1]))
454
+ remove_action.triggered.connect(self.removeRuler)
455
+ send_action = QAction("Send to Table")
456
+ #send_action.triggered.connect(partial(self.sendRulerValue, [lenght, angle], 1))
457
+ menu.addAction(xy_action)
458
+ menu.addAction(distance_action)
459
+ menu.addAction(angle_action)
460
+ menu.addAction(remove_action)
461
+ menu.addAction(send_action)
462
+
463
+ action = menu.exec_(self.mapToGlobal(pos))
464
+ if action == send_action:
465
+ #['ImType', 'Area', 'Perimeter', 'Slice', 'WindowName', 'pos_ruler']
466
+ if length==0:
467
+ return
468
+ vals = []
469
+ vals.append('')
470
+ if self.imType == 'eco':
471
+ vals.append('0')
472
+ else:
473
+ vals.append('1')
474
+ vals.append("{:.2f} mm".format(length))
475
+ vals.append("{:.2f}\N{DEGREE SIGN}".format(angle))
476
+ vals.append(str(self.sliceNum))
477
+ vals.append(self.currentWidnowName)
478
+ vals.append("{:.2f},{:.2f}".format(pos_ruler[0], pos_ruler[1]))
479
+ vals.append('')
480
+ self.rulerInfo.emit(vals, 0)
481
+
482
+ def ShowContextMenu_gen(self, pos):
483
+ """
484
+ Context MENU in case of generating contour from lines
485
+ :param pos:
486
+ :return:
487
+ """
488
+ menu = QMenu("ContourGen")
489
+ empty_action = QAction("Empty")
490
+ empty_action.triggered.connect(self.removeRuler)
491
+ gen_action = QAction("GenerateAction")
492
+ #send_action.triggered.connect(partial(self.sendRulerValue, [lenght, angle], 1))
493
+ menu.addAction(gen_action)
494
+ menu.addAction(empty_action)
495
+
496
+ action = menu.exec_(self.mapToGlobal(pos))
497
+ if action == gen_action:
498
+ self.LineChanged.emit([[], [], False, True])
499
+ elif action==empty_action:
500
+ self.linePoints = []
501
+ self.startLinePoints = []
502
+ self.LineChanged.emit([[], self.colorInd, True, False])
503
+
504
+
505
+
506
+ def ShowContextMenu_contour(self, pos):
507
+ """
508
+ Context Menu for contouring including center of contouring, perimeter, interpolation, etc.
509
+ :param pos:
510
+ :return:
511
+ """
512
+ from melage.utils.utils import point_in_contour
513
+ menu = QMenu("Ruler")
514
+ remove_action = QAction("Cancel")
515
+ x, y = self.to_real_world(pos.x(), pos.y())
516
+ color = self.segSlice[int(y), int(x)]
517
+ try:
518
+ if color in self.colorInds or 9876 in self.colorInds:
519
+ area, perimeter, centerXY, WI_index = point_in_contour(self.segSlice.copy(), (x,y), color)
520
+ area = (self.imSpacing[0]**2)*area
521
+ perimeter = (self.imSpacing[0])*perimeter
522
+ else:
523
+ area, perimeter, centerXY, WI_index = 0, 0, [0, 0], None
524
+ except:
525
+ area = 0.0
526
+ perimeter = 0.0
527
+ centerXY = [x, y]
528
+ self.penPoints = []
529
+ area_action = QAction("Surface {:.2f} mm\u00b2".format(area))
530
+ perimeter_action = QAction("Perimeter {:.2f} mm".format(perimeter))
531
+
532
+ #xyz = [x, y, self.sliceNum, 1]
533
+ if self.currentWidnowName == 'sagittal':
534
+ #xyz = [xyz[1], xyz[0], xyz[2], 1]
535
+ xyz = [self.sliceNum, centerXY[1], centerXY[0], 1]
536
+ elif self.currentWidnowName == 'coronal':
537
+ #xyz = [xyz[1], xyz[2], xyz[0], 1]
538
+ xyz = [centerXY[1], self.sliceNum, centerXY[0], 1]
539
+ elif self.currentWidnowName == 'axial':
540
+ #xyz = [xyz[2], xyz[1], xyz[0], 1]
541
+ xyz = [centerXY[1], centerXY[0], self.sliceNum, 1]
542
+ loc = self.affine @ np.array(xyz)
543
+ xy_action = QAction("Loc ({:.1f}, {:.1f},{:.1f})".format(loc[0], loc[1], loc[2]))
544
+ if color>0:
545
+ name_area = QAction(f"{self.color_name[color-1]}")
546
+ else:
547
+ name_area = QAction(f"Unknown")
548
+ send_action = QAction("Send to Table")
549
+ interploateadd_action = QAction("Add to interploation")
550
+ apply_interpolation = QAction("Apply interploation")
551
+ #send_action.triggered.connect(partial(self.sendRulerValue, [area], 0))
552
+ remove_action.triggered.connect(self.emptyPenPoints)
553
+ menu.addAction(name_area)
554
+ menu.addAction(xy_action)
555
+ menu.addAction(area_action)
556
+ menu.addAction(perimeter_action)
557
+ menu.addAction(send_action)
558
+ menu.addSeparator()
559
+ menu.addAction(interploateadd_action)
560
+ menu.addAction(apply_interpolation)
561
+ menu.addSeparator()
562
+ menu.addAction(remove_action)
563
+ action = menu.exec_(self.mapToGlobal(pos))
564
+ if action == send_action:
565
+ #['ImType', 'Area', 'Perimeter', 'Slice', 'WindowName', 'CenterXY']
566
+ if area==0:
567
+ return
568
+ vals = []
569
+ vals.append('{}'.format(color))
570
+
571
+ if self.imType == 'eco':
572
+ vals.append('Top')
573
+ else:
574
+ vals.append('Bottom')
575
+ vals.append("{:.2f} mm\u00b2".format(area))
576
+ vals.append("{:.2f} mm".format(perimeter))
577
+ vals.append(str(self.sliceNum))
578
+ vals.append(self.currentWidnowName)
579
+
580
+ xyz = [x, y, self.sliceNum]
581
+ if self.currentWidnowName == 'sagittal':
582
+ xyz = [xyz[1], xyz[0], xyz[2]]
583
+ elif self.currentWidnowName == 'coronal':
584
+ xyz = [xyz[1], xyz[2], xyz[0]]
585
+ elif self.currentWidnowName == 'axial':
586
+ xyz = [xyz[2], xyz[1], xyz[0]]
587
+
588
+ vals.append("{:.2f},{:.2f}".format(centerXY[0], centerXY[1]))
589
+ vals.append('')
590
+ self.rulerInfo.emit(vals, 0)
591
+ elif action == interploateadd_action:
592
+ if WI_index is not None:
593
+ self.interpolate.emit([self.sliceNum, self.currentWidnowName, False, WI_index])
594
+ elif action == apply_interpolation:
595
+ if WI_index is not None:
596
+ self.interpolate.emit([self.sliceNum, self.currentWidnowName, True, WI_index])
597
+ return
598
+ def emptyPenPoints(self):
599
+ """
600
+ eliminating pen points
601
+ :return:
602
+ """
603
+ self.penPoints = []
604
+
605
+
606
+ def sendRulerValue(self, values, column):
607
+ # send value obtained from ruler
608
+ if values[0] != 0:
609
+ self.rulerInfo.emit(values, column)
610
+
611
+
612
+
613
+ def _compute_distance_ruler(self, pos):
614
+ '''
615
+ GET information from RULER
616
+ :param pos:
617
+ :return:
618
+ '''
619
+ key_min = None
620
+ length = 0
621
+ x, y = self.lastPressRuler
622
+ #x, y = pos
623
+ try:
624
+ min_dist = np.inf
625
+ for key in self.rulerPoints.keys():
626
+ if key is None:
627
+ continue
628
+ if len(self.rulerPoints[key]['center'])!=3:
629
+ continue
630
+ a, b, c = self.rulerPoints[key]['center']
631
+ x1, y1, _ = self.rulerPoints[key]['points'][0]
632
+ x2, y2, _ = self.rulerPoints[key]['points'][-1]
633
+ dist_center = (x-(x1+x2)/2.0)**2. + (y-(y1+y2)/2.)**2.
634
+ dist_line = abs(a * y + b * x + c) / np.sqrt(a + b ** 2)
635
+ dist = dist_line + 0.1*np.sqrt(dist_center)
636
+ if dist < min_dist:
637
+ min_dist = dist
638
+ key_min = key
639
+ length = np.sqrt((y2-y1)**2+(x2-x1)**2)
640
+ except Exception as e:
641
+ pass
642
+ return key_min, length, [pos.x(), pos.y()]
643
+
644
+ def removeRuler(self):
645
+ """
646
+ Remover one of the lines from ruler
647
+ :return:
648
+ """
649
+ try:
650
+ self.rulerPoints.pop(self.key_min_ruler)
651
+ self.update()
652
+ except Exception as e:
653
+ pass
654
+
655
+ def escapePolygon(self):
656
+ """
657
+ When panning menu is activated
658
+ :return:
659
+ """
660
+ self.points = []
661
+
662
+ def closePolygon(self):
663
+ if len(self.points) > 1:
664
+ if len(self.points) > 2:
665
+ try:
666
+ polygonC = ConvertPToPolygons(self.points)
667
+ for poly in polygonC:
668
+ whiteVoxels = np.empty((0,3))
669
+ if len(poly.exterior.xy[0]) > 1:
670
+ #print(list(poly.exterior.coords))
671
+ whiteVoxel, edges = findIndexWhiteVoxels(poly, self.currentWidnowName)
672
+ whiteVoxels = np.vstack((whiteVoxels, whiteVoxel))
673
+
674
+ #if whiteInd is not None:
675
+ #self.imSeg[tuple(zip(*whiteInd))] = self.colorInd
676
+ if whiteVoxels.shape[0] != 0:
677
+ self.segChanged.emit(whiteVoxels.astype("int"), self.currentWidnowName, self.colorInd, self.sliceNum)
678
+ except Exception as e:
679
+ print('something')
680
+ self.points = []
681
+
682
+
683
+ def setZRotation(self, angle):
684
+ angle = self.normalizeAngle(angle)
685
+ if angle != self.zRot:
686
+ self.zRot = angle
687
+ self.zRotationChanged.emit(angle)
688
+ self.update()
689
+
690
+ def normalizeAngle(self, angle):
691
+ while angle < 0:
692
+ angle += 360 * 16
693
+ while angle > 360 * 16:
694
+ angle -= 360 * 16
695
+ return angle
696
+
697
+ def minimumSizeHint(self):
698
+ return QSize(50, 50)
699
+
700
+ #def sizeHint(self):
701
+ # return QSize(self.imWidth, self.imHeight)
702
+
703
+ #def rotateBy(self, xAngle, yAngle, zAngle):
704
+ # self.xRot = 0.0
705
+ # self.yRot = 0.0
706
+ # self.zRot += -(zAngle)
707
+ #self.update()
708
+
709
+ def _endIMage(self, x, y):
710
+ """
711
+ check if we are at the end of the image
712
+ :param x: input x direction
713
+ :param y: input y direction
714
+ :return:
715
+ """
716
+ xmax = self.imWidth
717
+ ymax = self.imHeight
718
+ endIm = False
719
+ if x<0:
720
+ endIm = True
721
+ x = 0
722
+ elif x>xmax:
723
+ endIm = True
724
+ x = xmax
725
+ if y <0:
726
+ endIm = True
727
+ y = 0
728
+ elif y > ymax:
729
+ endIm = True
730
+ y = ymax
731
+ return endIm, x, y
732
+
733
+ def _endWindow(self, xw, yw):
734
+ """
735
+ check if we are at the end of the the curretn winow
736
+ :param xw: x direction
737
+ :param yw: y direction
738
+ :return:
739
+ """
740
+ xwmax = 0.95*self.width()
741
+ ywmax = 0.95*self.height()
742
+ xwmin = 0.00*self.width()
743
+ ywmin = 0.00*self.height()
744
+ state = 0
745
+ if xw <xwmin or xw>xwmax:
746
+ state = 1
747
+ if yw <ywmin or yw > ywmax:
748
+ if state != 0:
749
+ state = 3
750
+ else:
751
+ state = 2
752
+ return state
753
+
754
+
755
+ def pan(self, dx, dy):
756
+ """
757
+ Panning throug image using dx and dy
758
+ :param dx:
759
+ :param dy:
760
+ :return:
761
+ """
762
+ left_p = self.left
763
+ right_p = self.right
764
+ bottom_p = self.bottom
765
+ top_p = self.top
766
+ dx, dy = dx*100/self.width(), dy*100/self.height()
767
+ self.left -= dx #
768
+ self.right -= dx #
769
+ self.bottom -= dy
770
+ self.top -= dy #
771
+
772
+ halfPoint = self.to_real_world(self.width()/2, self.height()/2)
773
+
774
+ # displacement from the image center
775
+ displacement = math.hypot(halfPoint[0]-self.imWidth/2, halfPoint[1]-self.imHeight/2)
776
+
777
+ if displacement > self.maxAllowedDis: # panning should not be done outside of the image
778
+ self.left = left_p
779
+ self.right = right_p
780
+ self.bottom = bottom_p
781
+ self.top = top_p
782
+
783
+ self.update()
784
+
785
+
786
+ def fromRealWorld(self, mouseXWorld, mouseYWorld):
787
+ """
788
+ convert to real world from mouse position
789
+ :param mouseXWorld: mouse x position
790
+ :param mouseYWorld: mouse y position
791
+ :return:
792
+ """
793
+ def convertR(xr, yr): # convert back x and y from rotation
794
+ x_p = (xr-cx)*ca - (yr - cy)*sa+cx
795
+ y_p = (xr-cx)*sa + (yr - cy)*ca+cy
796
+ return x_p, y_p
797
+ # rotation angle
798
+ angle = self.zRot/16
799
+ if angle !=0:
800
+ cx, cy = self.imWidth / 2, self.imHeight / 2
801
+ angle_r = math.radians(angle)
802
+ sa = math.sin(angle_r)
803
+ ca = math.cos(angle_r)
804
+ mouseXWorld, mouseYWorld = convertR(mouseXWorld, mouseYWorld)
805
+ xp = (mouseXWorld - self.left)/self.zoomed_width
806
+ yp = (mouseYWorld - self.bottom) / self.zoomed_height
807
+ return xp*self.width(), yp*self.height()
808
+
809
+
810
+ def to_real_world(self, x, y):
811
+ """
812
+ convert to real world
813
+ :param x:
814
+ :param y:
815
+ :return:
816
+ """
817
+ def convertR(xr, yr): # convert back x and y from rotation
818
+ x_p = (xr-cx)*ca - (yr - cy)*sa+cx
819
+ y_p = (xr-cx)*sa + (yr - cy)*ca+cy
820
+ return x_p, y_p
821
+
822
+ # rotation center
823
+ angle = self.zRot/16
824
+
825
+ # scale back the window to image data
826
+ mouseXWorld = (x / self.width()) * self.zoomed_width + self.left
827
+ mouseYWorld = (y/self.height())*self.zoomed_height + self.bottom
828
+ if angle !=0:
829
+ cx, cy = self.imWidth / 2, self.imHeight / 2
830
+ angle_r = math.radians(angle)
831
+ sa = -1*math.sin(angle_r)
832
+ ca = math.cos(angle_r)
833
+ mouseXWorld, mouseYWorld = convertR(mouseXWorld, mouseYWorld)
834
+
835
+
836
+ return mouseXWorld, mouseYWorld
837
+
838
+
839
+ def createProgram(self, id, vsrc, fsrc):
840
+ """
841
+ Create shader program using id and other shader information
842
+ :param id:
843
+ :param vsrc:
844
+ :param fsrc:
845
+ :return:
846
+ """
847
+ glEnable(GL_DEPTH_TEST)
848
+ glEnable(GL_CULL_FACE)
849
+
850
+ vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
851
+ vshader.compileSourceCode(vsrc)
852
+ if not vshader.isCompiled():
853
+ print(vshader.log())
854
+
855
+ fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
856
+ fshader.compileSourceCode(fsrc)
857
+
858
+ if not fshader.isCompiled():
859
+ print(fshader.log())
860
+
861
+ self.program.append(QOpenGLShaderProgram())
862
+ self.program[id].addShader(vshader)
863
+ self.program[id].addShader(fshader)
864
+
865
+ #self.program.bindAttributeLocation('in_Vertex',
866
+ # self.PROGRAM_VERTEX_ATTRIBUTE)
867
+ #self.program.bindAttributeLocation('vertTexCoord',
868
+ # self.PROGRAM_TEXCOORD_ATTRIBUTE)
869
+ self.program[id].link()
870
+ self.program[id].bind()
871
+ #################### SHADER ID #################
872
+ self.program[id].vertPosAttrId = self.program[id].attributeLocation("in_Vertex")
873
+ self.program[id].vertTexCoordAttrId = self.program[id].attributeLocation("vertTexCoord")
874
+
875
+ self.program[id].vertModelViewAttrId = self.program[id].uniformLocation("g_matModelView")
876
+ self.program[id].texSamplerId = self.program[id].uniformLocation("tex")
877
+
878
+ self.program[id].texSamplerId2 = self.program[id].uniformLocation("u_input")
879
+
880
+ self.program[id].u_transformSizeID = self.program[id].uniformLocation("u_transformSize")
881
+ self.program[id].u_subtransformSizeID = self.program[id].uniformLocation("u_subtransformSize")
882
+
883
+ self.program[id].R = self.program[id].uniformLocation("R")
884
+ self.program[id].G = self.program[id].uniformLocation("G")
885
+ self.program[id].B = self.program[id].uniformLocation("B")
886
+
887
+
888
+
889
+ glUniform1i(
890
+ self.program[id].texSamplerId, # texture sampler uniform ID */
891
+ 0) # value of sampler */
892
+
893
+ glUniform1i(
894
+ self.program[id].texSamplerId2, # texture sampler uniform ID */
895
+ 1) # value of sampler */
896
+
897
+
898
+ self.program[id].release()
899
+ self.program[id].removeAllShaders()
900
+ return True
901
+
902
+
903
+ def wheelEvent(self, event: QWheelEvent):
904
+ """
905
+ Whele event
906
+ :param event:
907
+ :return:
908
+ """
909
+
910
+ if self.enabledZoom:
911
+ x = event.x()
912
+ y = event.y()
913
+ xr, yr=self.to_real_world(x, y)
914
+ endim = self._endIMage(xr, yr)
915
+ if endim[0]:
916
+ x, y = self.fromRealWorld(endim[1], endim[2])
917
+ deltaAngY = event.angleDelta().y()
918
+ self.scaleF = self.ZOOM_IN_FACTOR if deltaAngY > 0 else self.ZOOM_OUT_FACTOR if deltaAngY < 0 else 1
919
+
920
+ if 0.0<self.zoom_level_y*self.scaleF<5 or 0.0<self.zoom_level_x*self.scaleF<5:
921
+ self.updateScale(x, y, self.scaleF, self.scaleF)
922
+ else:
923
+ # scroll images
924
+ deltaAngY = event.angleDelta().y()
925
+ NexSlice = -1 if deltaAngY > 0 else 1 if deltaAngY < 0 else 1
926
+ self.sliceNChanged.emit(self.sliceNum+NexSlice)
927
+
928
+
929
+ def updateScale(self, x, y, scaleFX, scaleFY):
930
+ """
931
+ update zoom level
932
+ :param x:
933
+ :param y:
934
+ :param scaleFX:
935
+ :param scaleFY:
936
+ :return:
937
+ """
938
+ self.zoom_level_x *= scaleFX
939
+ self.zoom_level_y *= scaleFY
940
+
941
+ # locate mouse and to real world
942
+ # mouseXWorld, mouseYWorld, mouseX, mouseY = self.to_real_world(x, y)
943
+ mouseX = x / self.width() # x location in the window
944
+ mouseY = y / self.height() # y location in the window
945
+ mouseX = 0.5; mouseY = 0.5
946
+ mouseXWorld = self.left + mouseX * self.zoomed_width
947
+ mouseYWorld = self.bottom + mouseY * self.zoomed_height
948
+
949
+ # update zoom width and heigth
950
+ self.zoomed_width *= scaleFX
951
+ self.zoomed_height *= scaleFY
952
+
953
+ self.left = mouseXWorld - mouseX * self.zoomed_width
954
+ self.right = mouseXWorld + (1 - mouseX) * self.zoomed_width
955
+ self.bottom = mouseYWorld - mouseY * self.zoomed_height
956
+ self.top = mouseYWorld + (1 - mouseY) * self.zoomed_height
957
+ #if self.enabledCircle:
958
+ #self.zoomchanged.emit(self._radius_circle/abs(self.to_real_world( 1, 0)[0] - self.to_real_world(0, 0)[0]), True)
959
+ self.zoomchanged.emit(None, True)
960
+ self.update()
961
+
962
+ def initializeGL(self):
963
+ """
964
+ Initialize GL
965
+ :return:
966
+ """
967
+ self.createProgram(0, fsrc=fsrc, vsrc=vsrc)
968
+ self.createProgram(1, fsrc=fsrcPaint, vsrc=vsrcPaint)
969
+
970
+ if self.imSlice is None:
971
+ return
972
+
973
+ self.UpdatePaintInfo()
974
+ self.makeObject()
975
+
976
+
977
+ def UpdatePaintInfo(self):
978
+ """
979
+ Update infroamtion of the iamge
980
+ :return:
981
+ """
982
+ self.windowAR = self.width() / (self.height()+0.000001) #windows aspect ratio
983
+ imWidth = self.imWidth
984
+ imHeight = self.imHeight
985
+
986
+ ratioX = imWidth / self.width()
987
+ ratioY = imHeight / (self.height()+0.000001)
988
+ if self.width() > self.height():
989
+ if imWidth <= imHeight:
990
+
991
+ self.zoomed_height = imHeight
992
+ # self.zoomed_height /= self.imAr
993
+ self.bottom = 0
994
+ self.top = self.bottom + self.zoomed_height
995
+
996
+ # rescale to the image width
997
+ self.zoomed_width = ratioY * self.width()
998
+
999
+ # image left position
1000
+ self.left = imWidth / 2 - (self.zoomed_height * self.windowAR - self.zoomed_width / 2)
1001
+
1002
+ # image right position
1003
+ self.right = self.left + self.zoomed_width
1004
+ else:
1005
+ if ratioX < 1 and ratioY > 1:
1006
+ self.zoomed_height = imHeight
1007
+ self.bottom = 0 #
1008
+ self.top = self.bottom + self.zoomed_height
1009
+
1010
+ self.zoomed_width = ratioY * self.width()
1011
+ self.left = imWidth / 2 - (self.zoomed_height * self.windowAR - self.zoomed_width / 2)
1012
+ self.right = self.left + self.zoomed_width
1013
+
1014
+
1015
+ else:
1016
+ self.zoomed_width = imWidth
1017
+ self.left = 0 # -imWidth * (self.windowAR - 1) / 2
1018
+ self.right = self.left + self.zoomed_width
1019
+
1020
+ self.zoomed_height = ratioX * self.height()
1021
+ self.bottom = imHeight / 2 - (self.zoomed_width / self.windowAR - self.zoomed_height / 2)
1022
+ self.top = self.bottom + self.zoomed_height
1023
+
1024
+
1025
+ elif self.width() <= self.height():
1026
+
1027
+ if imWidth <= imHeight:
1028
+ if ratioX < 1 and ratioY > 1 or ratioX / ratioY < 1:
1029
+ self.zoomed_height = imHeight
1030
+ self.bottom = 0
1031
+ self.top = self.bottom + self.zoomed_height
1032
+
1033
+ self.zoomed_width = ratioY * self.width()
1034
+
1035
+ self.left = imWidth / 2 - (self.zoomed_height * self.windowAR - self.zoomed_width / 2)
1036
+ self.right = self.left + self.zoomed_width
1037
+
1038
+
1039
+ else:
1040
+ self.zoomed_width = imWidth
1041
+ self.left = 0
1042
+ self.right = self.left + self.zoomed_width
1043
+
1044
+ self.zoomed_height = ratioX * self.height()
1045
+ self.bottom = imHeight / 2 - (self.zoomed_width / self.windowAR - self.zoomed_height / 2)
1046
+ self.top = self.bottom + self.zoomed_height
1047
+
1048
+
1049
+ else:
1050
+
1051
+ self.zoomed_width = imWidth
1052
+ self.left = 0
1053
+ self.right = self.left + self.zoomed_width
1054
+
1055
+ self.zoomed_height = ratioX * self.height()
1056
+ #self.zoomed_height = 2*imWidth/self.imAr
1057
+
1058
+ self.bottom = imHeight / 2 - (self.zoomed_width / self.windowAR - self.zoomed_height / 2)
1059
+ self.top = self.bottom + self.zoomed_height
1060
+ #if self.currentWidnowName != 'axial' and self.imType == 't1':
1061
+ # self.updateScale( self.width()//2, self.height()//2, 1, 0.5)
1062
+ self.halfH = self.height() // 2
1063
+ glEnable(GL_NORMALIZE) # light normalization
1064
+
1065
+ def drawImage(self):
1066
+
1067
+ # pre requisites and drawImage
1068
+ self.drawImagePre()
1069
+
1070
+
1071
+ # draw additional
1072
+ self.drawImPolygon() # draw imFreeHand
1073
+
1074
+
1075
+ if len(self.erasePoints) > 1:
1076
+ self.DrawerasePolygon()
1077
+
1078
+ if len(self.rulerPoints.keys()) >= 1:
1079
+ self.DrawRulerLines()
1080
+
1081
+ if len(self.linePoints) > 3:
1082
+ self.DrawLines(self.linePoints)
1083
+
1084
+ if self.enabledCircle:
1085
+ self.DrawCricles(self._center_circle)
1086
+
1087
+ if self.tract is not None: # for tract file
1088
+ self.Draw_tract(self.tract, self.width_line_tract)
1089
+
1090
+ if len(self.guidelines_h) > 0:
1091
+ self.DrawLines(self.guidelines_h, self.colorh)
1092
+ if len(self.guidelines_v)>0:
1093
+ self.DrawLines(self.guidelines_v, self.colorv)
1094
+
1095
+ # end iamges
1096
+ self.drawImageEnd()
1097
+
1098
+
1099
+ def Draw_tract(self, tract, width_line=3):
1100
+ """
1101
+ Draw tractography files
1102
+ :param tract:
1103
+ :param width_line:
1104
+ :return:
1105
+ """
1106
+ from melage.utils.utils import divide_track_to_prinicipals
1107
+ uq = np.unique(tract[:,-1])
1108
+ r = 0
1109
+ for u in uq:
1110
+ trct = tract[tract[:, -1] == u, :-1]
1111
+
1112
+ if trct.shape[0]==trct[0,-1]:
1113
+ trcts = divide_track_to_prinicipals(trct[:,:3])
1114
+ colorv = [trct[0,3], trct[0,4], trct[0,5], 1]
1115
+ self.DrawLines(tuple(trcts), colorv = colorv, width_line=width_line)
1116
+ r +=1
1117
+ print(r)
1118
+
1119
+ def drawImagePre(self):
1120
+ """
1121
+ Draw Image pre-requisites
1122
+ :return:
1123
+ """
1124
+ #glPushMatrix()
1125
+ #glPushAttrib(
1126
+ # GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_ENABLE_BIT
1127
+ # | GL_DEPTH_BUFFER_BIT)
1128
+
1129
+
1130
+ ################## static GL data ##################
1131
+ # use the program object
1132
+ glUseProgram(self.program[0].programId())
1133
+ #self.program[0].link()
1134
+ #self.program[0].bind()
1135
+ # set rotation view translation matrix
1136
+ # model-view-projection matrix
1137
+ #glUniformMatrix4fv(self.vertModelViewAttrId, 1, GL_FALSE, mvpMatrix)
1138
+ # texture
1139
+ m = QMatrix4x4()
1140
+
1141
+ m.ortho( self.left, self.right, self.top, self.bottom, -1, 1 )
1142
+
1143
+ m.translate(self.imWidth/2, self.imHeight/2, 0.0)
1144
+
1145
+ m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)
1146
+
1147
+ m.translate(-self.imWidth/2, -self.imHeight/2, 0.0)
1148
+
1149
+ self.mvpMatrix = m.copyDataTo()
1150
+
1151
+
1152
+ glUniformMatrix4fv(self.program[0].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
1153
+
1154
+ glUniform1fv(self.program[0].u_transformSizeID, 1,self.imWidth)
1155
+ glUniform1fv(self.program[0].u_subtransformSizeID, 1, self.imHeight)
1156
+
1157
+ #self.program[0].setUniformValue('g_matModelView', m)
1158
+
1159
+ # thresholding
1160
+ threshold_loc = glGetUniformLocation(self.program[0].programId(), "threshold")
1161
+ glUniform1f(threshold_loc, 0)
1162
+
1163
+ # contrast
1164
+ contrast_loc = glGetUniformLocation(self.program[0].programId(), "contrastMult")
1165
+ glUniform1f(contrast_loc, 1)
1166
+
1167
+ # brightness
1168
+ brightnessAdd_loc = glGetUniformLocation(self.program[0].programId(), "brightnessAdd")
1169
+ glUniform1f(brightnessAdd_loc, self.brightness)
1170
+
1171
+ # soble filter
1172
+ activateSobel_loc = glGetUniformLocation(self.program[0].programId(), "sobel")
1173
+ glUniform1i(activateSobel_loc, self.activateSobel)
1174
+ activateSobel_loc = glGetUniformLocation(self.program[0].programId(), "sobel_threshold")
1175
+ glUniform1f(activateSobel_loc, self.thresholdSobel)
1176
+
1177
+ minRad_loc = glGetUniformLocation(self.program[0].programId(), "iResolution")
1178
+ glUniform2fv(minRad_loc, 1, [self.width(), self.height()])
1179
+ maxRad_loc = glGetUniformLocation(self.program[0].programId(), "maxRadius")
1180
+ glUniform1f(maxRad_loc, self.thresholdSobel)
1181
+
1182
+ ilum_loc = glGetUniformLocation(self.program[0].programId(), "Ilum")
1183
+ glUniform1f(maxRad_loc, 0.8)
1184
+
1185
+ mouse_pos_loc = glGetUniformLocation(self.program[0].programId(), "mousePos")
1186
+ glUniform2fv(mouse_pos_loc, 1, [self.lastPos.x(), self.height()-self.lastPos.y()] )
1187
+
1188
+ # deinterlace
1189
+ deinterlace = [self.imHeight, 0, 0]
1190
+ deinterlace_loc = glGetUniformLocation(self.program[0].programId(), "deinterlace")
1191
+ glUniform3fv(deinterlace_loc, 1, deinterlace)
1192
+
1193
+ self.program[0].enableAttributeArray(self.program[0].vertTexCoordAttrId)
1194
+ self.program[0].setAttributeArray(self.program[0].vertTexCoordAttrId, self.coord)
1195
+ self.program[0].enableAttributeArray(self.program[0].vertPosAttrId)
1196
+ self.program[0].setAttributeArray(self.program[0].vertPosAttrId, self.vertex)
1197
+
1198
+ glEnable(GL_TEXTURE_2D)
1199
+
1200
+ glActiveTexture(GL_TEXTURE0)
1201
+
1202
+ glBindTexture(GL_TEXTURE_2D, self.textureID)
1203
+ glEnableClientState(GL_VERTEX_ARRAY)
1204
+ glDrawArrays(GL_QUADS, 0, 4)
1205
+ glDisable(GL_TEXTURE_2D)
1206
+ glDisableClientState(GL_VERTEX_ARRAY)
1207
+ #glBindTexture(GL_TEXTURE_2D, 0)
1208
+ self.program[0].disableAttributeArray(self.program[0].vertTexCoordAttrId)
1209
+ self.program[0].disableAttributeArray(self.program[0].vertPosAttrId)
1210
+
1211
+
1212
+ def drawImageEnd(self):
1213
+ glUseProgram(0) # necessary to release the program
1214
+
1215
+
1216
+ def paintGL_start(self):
1217
+
1218
+ glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
1219
+ self.clearColor.blueF(), self.clearColor.alphaF())
1220
+
1221
+
1222
+ glClear(GL_COLOR_BUFFER_BIT)
1223
+ glClear(GL_DEPTH_BUFFER_BIT)
1224
+
1225
+ glDisable(GL_CULL_FACE) # disable backface culling
1226
+ glDisable(GL_LIGHTING) # disable lighting
1227
+ glDisable(GL_DEPTH_TEST) # disable depth test
1228
+
1229
+ glMatrixMode(GL_PROJECTION)
1230
+ glLoadIdentity()
1231
+
1232
+ def paintGL(self):
1233
+ """
1234
+ Painting GL
1235
+ :return:
1236
+ """
1237
+ if self.imSlice is None:
1238
+ return
1239
+
1240
+ #version_str = glGetString(GL_VERSION)
1241
+ #print(f"OpenGL Version: {version_str.decode('utf-8')}")
1242
+ self.paintGL_start()
1243
+ self.drawImage()
1244
+ self.paintGL_end()
1245
+
1246
+ def subpaintGL(self,points, windowname):
1247
+ """
1248
+ reserved function for mutual painting
1249
+ :param points:
1250
+ :param windowname:
1251
+ :return:
1252
+ """
1253
+ self.senderPoints = None
1254
+ self.senderWindow = None
1255
+ self.update()
1256
+
1257
+ def paintGL_end(self):
1258
+ glClear(GL_DEPTH_BUFFER_BIT)
1259
+
1260
+ #glFlush()
1261
+ #swa
1262
+ #glCallList(1)
1263
+ if self.showAxis:
1264
+ self.draw_axis()
1265
+ self.drawLabel()
1266
+
1267
+
1268
+ def draw_closed_polygon(self):
1269
+ """
1270
+ Draw polygon
1271
+ :return:
1272
+ """
1273
+ glPushMatrix()
1274
+ glPushAttrib(GL_CURRENT_BIT)
1275
+ glPolygonMode(GL_BACK, GL_LINE) # filling the polygon
1276
+
1277
+ glDisable(GL_LIGHTING)
1278
+ glNormal3f(0.5, 1.0, 1.0)
1279
+ glLineWidth(5.0)
1280
+ glBegin(GL_POLYGON) # These vertices form a closed polygon
1281
+ glColor3f(1.0, 1.0, 0.0) # Yellow
1282
+ glVertex2f(0.4* self.imHeight, 0.2 * self.imWidth)
1283
+ glVertex2f(0.6* self.imHeight, 0.2 * self.imWidth)
1284
+ glVertex2f(0.7* self.imHeight, 0.4 * self.imWidth)
1285
+
1286
+ glEnd()
1287
+ glPopAttrib()
1288
+ glPopMatrix()
1289
+
1290
+ def draw_axis(self):
1291
+ """
1292
+ Draw axis
1293
+ :return:
1294
+ """
1295
+ offset = 0.05
1296
+ x0, y0, x1, y1 = 0, 0.0, 1,1
1297
+ glPushAttrib(GL_CURRENT_BIT)
1298
+ glLineStipple(1, 0xF00F)
1299
+ glEnable(GL_LINE_STIPPLE)
1300
+ glLineWidth(1.0)
1301
+ #Set The Color To Blue One Time Only
1302
+ glBegin(GL_LINES)
1303
+ glColor3f(self.colorX[0], self.colorX[1], self.colorX[2])
1304
+ glVertex2f(x0+offset, y0)
1305
+ glVertex2f(x1, y0)
1306
+ glVertex2f(x0-offset, y0)
1307
+ glVertex2f(-x1, y0)
1308
+ glColor3f(self.colorY[0], self.colorY[1], self.colorY[2])
1309
+ glVertex2f(x0, y0-offset)
1310
+ glVertex2f(x0, -y1)
1311
+ glVertex2f(x0, y0+offset)
1312
+ glVertex2f(x0, y1)
1313
+ #glVertex2f(x1, -y1)
1314
+ #glVertex2f(x0, y1)
1315
+
1316
+
1317
+ glEnd()
1318
+ glPopAttrib()
1319
+ # glEndList()
1320
+ glDisable(GL_LINE_STIPPLE)
1321
+
1322
+ def drawLabel(self):
1323
+ """
1324
+
1325
+ :return:
1326
+ """
1327
+
1328
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
1329
+ painter = QPainter(self)
1330
+ painter.setRenderHint(QPainter.Antialiasing)
1331
+
1332
+ painter.save()
1333
+
1334
+ font = painter.font()
1335
+ font.setPointSize(font.pointSize()*5)
1336
+ #font.setPixelSize(font.pixelSize()*1)
1337
+ painter.setRenderHint(QPainter.Antialiasing)
1338
+ font.setBold(True)
1339
+
1340
+ #pen = QPen(self.colorXID, 30, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
1341
+ #painter.setPen(pen)
1342
+
1343
+ painter.setPen(self.colorXID)
1344
+ painter.drawText(int(self.width()/2.1), int(0.02*self.height()),self.width(),self.height(),Qt.AlignCenter,
1345
+ self.labelX)
1346
+ painter.drawText(-int(self.width()/2.1), int(0.02*self.height()),self.width(),self.height(),Qt.AlignCenter,
1347
+ self.labelX)
1348
+
1349
+
1350
+ painter.setPen(self.colorYID)
1351
+
1352
+
1353
+ painter.drawText(int(0.02*self.width()), -int(self.height()/2.1),self.width(),self.height(),Qt.AlignCenter,
1354
+ self.labelY)
1355
+ painter.drawText(int(0.02*self.width()), int(self.height()/2.1),self.width(),self.height(),Qt.AlignCenter,
1356
+ self.labelY)
1357
+
1358
+ painter.setPen(Qt.white)
1359
+ painter.drawText(-int(self.width()/2.3), -int(self.height()/2.1),self.width(),self.height(),Qt.AlignCenter,
1360
+ self.currentWidnowName)
1361
+
1362
+
1363
+ painter.restore()
1364
+
1365
+ def takescreenshot(self, area=None, width=0, height=0):
1366
+ """
1367
+ This function has been designed to make screenshot from GL
1368
+ :param area:
1369
+ :param width:
1370
+ :param height:
1371
+ :return:
1372
+ """
1373
+ if self.imSlice is None:
1374
+ return None
1375
+ if area=='whole':
1376
+ self.makeObject()
1377
+ else:
1378
+ self.paintGL()
1379
+ if width==0:
1380
+ width = self.width()
1381
+ if height==0:
1382
+ height = self.height()
1383
+ val = max(height, width)
1384
+ glPixelStorei(GL_PACK_ALIGNMENT, 1)
1385
+ z = glReadPixels(0, 0, val, val, GL_RGBA,
1386
+ GL_FLOAT)[::-1]
1387
+ if val == width:
1388
+ z = z[val-height:width, 0:width]
1389
+ else:
1390
+ z = z[0:height, 0:width]
1391
+
1392
+ return z
1393
+
1394
+ def resizeGL(self, width, height):
1395
+ if self.imSlice is None:
1396
+ return
1397
+ side = min(width, height)
1398
+ #Set viewport
1399
+ glViewport((width- side) // 2, (height-side) // 2, self.imWidth,
1400
+ self.imHeight)
1401
+
1402
+ def mousePressEvent(self, event):
1403
+ self.mousePress.emit(event)
1404
+ self.lastPos = event.pos()
1405
+ """
1406
+
1407
+ if self.activateSobel:
1408
+ #event_r = QtGui.QResizeEvent(self.size(), self.size())
1409
+ #self.resizeEvent(event_r)
1410
+ self.makeCurrent()
1411
+ self.paintGL()
1412
+ glPixelStorei(GL_PACK_ALIGNMENT, 1)
1413
+ px = 20
1414
+ z = glReadPixels(self.lastPos.x()-px, self.height()-self.lastPos.y()-px, px*2+1, px*2+1, GL_RGBA, GL_BYTE)[::-1][:,:,[0,1,2]]
1415
+ z = cv2.resize(z.astype(np.uint8), (int((px*2+1)/(self.width()/self.imWidth)), int((px*2+1)/(self.height()/self.imHeight))), interpolation=cv2.INTER_AREA)
1416
+ result = np.all(z == [0, 127, 0], axis=2)
1417
+
1418
+ xs, ys = np.where(result)
1419
+ x, y = self.to_real_world(event.pos().x(), event.pos().y())
1420
+ x, y = x - z.shape[0]//2, y - z.shape[1]//2
1421
+ xs, ys = np.round(y) +xs, np.round(x) + ys
1422
+ whiteVoxels = np.zeros((len(xs), 3))
1423
+ whiteVoxels[:, 1] = ys
1424
+ whiteVoxels[:, 0] = xs
1425
+ whiteVoxels[:, 2] = self.sliceNum
1426
+ self.segChanged.emit(whiteVoxels.astype("int"), self.currentWidnowName, self.colorInd)
1427
+ self.update()
1428
+ """
1429
+ if self.enabledMagicTool:
1430
+ xc, yc = event.pos().x(), event.pos().y()
1431
+
1432
+ realx, realy = self.to_real_world(xc, yc)
1433
+
1434
+ initial_point = (int(realx), int(realy))
1435
+ #input_point = np.expand_dims(np.array(initial_point),0)
1436
+ #self._sam_predictor.set_image(np.repeat(self.imSlice[:, :, np.newaxis], 3, axis=2))
1437
+ #input_label = np.array([1])
1438
+ #seg_new, _, _ = self._sam_predictor.predict(point_coords=input_point, point_labels=input_label,
1439
+ # multimask_output=False, )
1440
+ seg_new = magic_selection(self.imSlice, initial_point, connectivity=4, tol=self._tol_magic_tool)
1441
+ self._magic_slice = None
1442
+ if seg_new is not None and self.colorInd!= 9876:
1443
+ l1 = list(np.where(seg_new > 0))
1444
+ whiteInd = np.stack([l1[1], l1[0], len(l1[0]) * [self.sliceNum]]).T
1445
+ whiteInd, _ = permute_axis(whiteInd, whiteInd, self.currentWidnowName)
1446
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, self.colorInd, self.sliceNum)
1447
+ self.makeObject()
1448
+ self.update()
1449
+
1450
+
1451
+
1452
+ elif self.enabledCircle:
1453
+ if self._magic_slice is not None:
1454
+ slc = self._magic_slice.sum(2)
1455
+ slc = slc/slc.max()
1456
+ l1 = list(np.where((slc-self.segSlice) > 0))
1457
+ if len(l1[0])>0:
1458
+ whiteInd = np.stack([l1[1], l1[0], len(l1[0]) * [self.sliceNum]]).T
1459
+ whiteInd, _ = permute_axis(whiteInd, whiteInd, self.currentWidnowName)
1460
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, self.colorInd, self.sliceNum)
1461
+ self.makeObject()
1462
+ self.update()
1463
+
1464
+ if self._NenabledCircle>0 or 1>2:
1465
+ xc, yc = event.pos().x(), event.pos().y()
1466
+ num_segments = 50
1467
+ #if self.zoom_level_x<=1:
1468
+ radius = self._radius_circle/4#*self.imSpacing[0]#*self.zoom_level_x
1469
+ #else:
1470
+ # radius = self._radius_circle/self.zoom_level_x
1471
+ points = []
1472
+ realx, realy = self.to_real_world(xc, yc)
1473
+ for ii in range(num_segments):
1474
+ theta = 2.0 * np.pi * ii / num_segments # get the current angle
1475
+ x = radius * np.cos(theta) # calculate the x component
1476
+ y = radius * np.sin(theta) # calculate the y component
1477
+ #x1, y1 = self.to_real_world(xc+x,yc+y)
1478
+ x1, y1 = realx +x, realy + y
1479
+ points.append([x1,y1, self.sliceNum])
1480
+ self._center_circle = points
1481
+ self.update()
1482
+ self._NenabledCircle += 1
1483
+ elif self.enabledPointSelection:
1484
+
1485
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1486
+ endIm = self._endIMage(x, y)
1487
+ if endIm[0]:
1488
+ # update x and y
1489
+ x = endIm[1]
1490
+ y = endIm[2]
1491
+ self.selectedPoints.append([x,y, self.sliceNum])
1492
+ whiteInd, edges = findIndexWhiteVoxels(self.selectedPoints, self.currentWidnowName, is_pixel=True,bool_permute_axis=True)
1493
+ print(whiteInd[0])
1494
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, True, self.sliceNum)
1495
+ self.selectedPoints = []
1496
+ self.update()
1497
+
1498
+ elif self.enabledPen:
1499
+ x, y = self.to_real_world(event.pos().x(), event.pos().y())
1500
+ endIm = self._endIMage(x, y)
1501
+ if endIm[0]:
1502
+ # update x and y
1503
+ x = endIm[1]
1504
+ y = endIm[2]
1505
+ self.penPoints.append([x, y, self.sliceNum])
1506
+ elif self.enabledRotate:
1507
+ self.lastPressPos = self.lastPos
1508
+ self.pressZone = zonePoint(event.x(), event.y(), self.width() / 2, self.width() / 2)
1509
+ elif self.enabledErase:
1510
+ self.erasePoints = []
1511
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1512
+ endIm = self._endIMage(x, y)
1513
+ if endIm[0]:
1514
+ # update x and y
1515
+ x = endIm[1]
1516
+ y = endIm[2]
1517
+ self.erasePoints.append([x, y, self.sliceNum])
1518
+ elif self.enabledRuler:
1519
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1520
+ endIm = self._endIMage(x, y)
1521
+ if endIm[0]:
1522
+ # update x and y
1523
+ x = endIm[1]
1524
+ y = endIm[2]
1525
+
1526
+ self.lastPressRuler = [x,y]
1527
+
1528
+ if event.button()==Qt.LeftButton:
1529
+ self.rulerPoints[self.N_rulerPoints] = defaultdict(list)
1530
+ self.rulerPoints[self.N_rulerPoints]['points'] = []
1531
+ self.rulerPoints[self.N_rulerPoints]['perpendicular1'] = []
1532
+ self.rulerPoints[self.N_rulerPoints]['perpendicular2'] = []
1533
+ self.rulerPoints[self.N_rulerPoints]['center'] = []
1534
+ self.rulerPoints[self.N_rulerPoints]['points'].append([x, y, self.sliceNum])
1535
+
1536
+ elif self.enabledLine:
1537
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1538
+ endIm = self._endIMage(x, y)
1539
+ if endIm[0]:
1540
+ # update x and y
1541
+ x = endIm[1]
1542
+ y = endIm[2]
1543
+ self.startLinePoints.append([x, y, self.sliceNum])
1544
+
1545
+ if self.enabledGoTo:
1546
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1547
+ endIm = self._endIMage(x, y)
1548
+ if endIm[0]:
1549
+ # update x and y
1550
+ x = endIm[1]
1551
+ y = endIm[2]
1552
+ self.goto.emit([x, y, self.sliceNum], [self.currentWidnowName, self.imType])
1553
+ if event.button() == Qt.LeftButton:
1554
+ self._allowed_goto = True
1555
+
1556
+
1557
+ def updateCursor(self, event, x, y, dx, dy,endIm):
1558
+ """
1559
+ Update Cursor
1560
+ :param event:
1561
+ :param x:
1562
+ :param y:
1563
+ :param dx:
1564
+ :param dy:
1565
+ :param endIm:
1566
+ :return:
1567
+ """
1568
+ if self.zoom_level_x < 1 and self.zoom_level_y < 1 and not endIm:
1569
+ state = self._endWindow(event.x(), event.y())
1570
+ if state != 0:
1571
+ if state == 1: # xpanning
1572
+ self.pan(-dx, 0)
1573
+ xWind, yWind = self.fromRealWorld(x, y)
1574
+ xyG = self.mapToGlobal(QPoint(int(xWind), int(yWind))) # map from current window to global window
1575
+ self.cursor().setPos(xyG.x(), xyG.y())
1576
+ elif state == 2: # y panning
1577
+ self.pan(0, -dy)
1578
+ xWind, yWind = self.fromRealWorld(x, y)
1579
+ xyG = self.mapToGlobal(QPoint(int(xWind), int(yWind))) # map from current window to global window
1580
+ self.cursor().setPos(xyG.x(), xyG.y())
1581
+ elif state == 3: # x and y panning (corners)
1582
+ self.pan(-dx, -dy)
1583
+ xWind, yWind = self.fromRealWorld(x, y)
1584
+ xyG = self.mapToGlobal(QPoint(int(xWind), int(yWind))) # map from current window to global window
1585
+ self.cursor().setPos(xyG.x(), xyG.y())
1586
+
1587
+ def leaveEvent(self, event):
1588
+ if self.enabledMagicTool or self.enabledCircle:
1589
+ self._magic_slice = None
1590
+ self.makeObject()
1591
+ self.update()
1592
+ super().leaveEvent(event)
1593
+
1594
+
1595
+ def mouseMoveEvent(self, event):
1596
+ if self.enabledMagicTool:
1597
+ xc, yc = event.pos().x(), event.pos().y()
1598
+
1599
+ realx, realy = self.to_real_world(xc, yc)
1600
+ if realx<=0 or realy<=0:
1601
+ return
1602
+ initial_point = (int(realx), int(realy))
1603
+
1604
+
1605
+ seg_new = magic_selection(self.imSlice, initial_point, connectivity=4, tol=self._tol_magic_tool)
1606
+
1607
+ if seg_new is not None and self.colorInd!= 9876:
1608
+ color = self.colorsCombinations[self.colorInd]
1609
+ color_image = np.stack((seg_new * color[0]*255, seg_new * color[1]*255, seg_new * color[2]*255), axis=-1)
1610
+ self._magic_slice = color_image
1611
+ self.makeObject()
1612
+ self.update()
1613
+ else:
1614
+ self._magic_slice = None
1615
+ elif self.enabledCircle:
1616
+ # from melage.utils.utils import
1617
+ if self._NenabledCircle > 0 and self.colorInd != 9876:
1618
+
1619
+ self._center_circle = []
1620
+ xc, yc = event.pos().x(), event.pos().y()
1621
+ xc_mp, yc_mp = self.to_real_world(xc, yc)
1622
+
1623
+ num_segments = 50
1624
+ # if self.zoom_level_x<=1:
1625
+ radius = self._radius_circle / 4.0 # *self.imSpacing[0]#*self.zoom_level_x
1626
+ # else:
1627
+ # radius = self._radius_circle/self.zoom_level_x
1628
+ sum_diff = 100
1629
+ realx, realy = self.to_real_world(xc, yc)
1630
+ if hasattr(self, 'lastPos'):
1631
+ xc_l, yc_l = self.lastPos.x(), self.lastPos.y()
1632
+ realx_l, realy_l = self.to_real_world(xc_l, yc_l)
1633
+ sum_diff = abs(realx_l-realx)+abs(realy_l-realy)
1634
+ if sum_diff>0.1:
1635
+ #print(sum_diff)
1636
+ points = []
1637
+ for ii in range(num_segments):
1638
+ theta = 2.0 * np.pi * ii / num_segments # get the current angle
1639
+ x = radius * np.cos(theta) # calculate the x component
1640
+ y = radius * np.sin(theta) # calculate the y component
1641
+ # x1, y1 = self.to_real_world(xc+x,yc+y)
1642
+ x1, y1 = realx + x, realy + y
1643
+ points.append([x1, y1, self.sliceNum])
1644
+ if len(points) > 2:
1645
+ from melage.utils.utils import seperate_lcc
1646
+ polygonC = ConvertPointsToPolygons(points, width=0)
1647
+ whiteInd = None
1648
+ if polygonC.is_valid:
1649
+ whiteInd, edges = findIndexWhiteVoxels(polygonC, self.currentWidnowName,
1650
+ bool_permute_axis=False)
1651
+ else:
1652
+ # print('not valid polygon')
1653
+ # polygonC = polygonC.buffer(0)
1654
+ multipolys = ConvertPToPolygons(self.penPoints)
1655
+ whiteInd = np.empty((0, 3))
1656
+ for poly in multipolys:
1657
+ if poly.is_valid:
1658
+ if poly.area < 2:
1659
+ continue
1660
+ voxels, edges = findIndexWhiteVoxels(poly, self.currentWidnowName)
1661
+ if voxels is not None:
1662
+ whiteInd = np.vstack((whiteInd, voxels))
1663
+
1664
+ #center_intensity = self.imSlice[int(yc_mp), int(xc_mp)]
1665
+ ind_1 = (whiteInd[:, 1] < self.imSlice.shape[0])*(whiteInd[:, 1]>0)
1666
+ whiteInd = whiteInd[ind_1, :]
1667
+ ind_2 = (whiteInd[:, 0] < self.imSlice.shape[1]) * (whiteInd[:, 0] > 0)
1668
+ whiteInd = whiteInd[ind_2, :]
1669
+ if len(whiteInd)==0:
1670
+ return super().mouseMoveEvent(event)
1671
+ new_index_intensity = self.imSlice[whiteInd[:, 1], whiteInd[:, 0]]
1672
+
1673
+
1674
+
1675
+ # seg_c = self.segSlice[int(yc_mp), int(xc_mp)]
1676
+ segs = self.segSlice[whiteInd[:, 1], whiteInd[:, 0]]
1677
+
1678
+ # ind_seg = segs != seg_c
1679
+ min_whit = whiteInd.min(0)
1680
+ from scipy.ndimage import gaussian_filter
1681
+ a1 = whiteInd[:, [1, 0]] - min_whit[[1, 0]]
1682
+ im1 = np.zeros(a1.max(0) + 1)
1683
+ seg1 = np.zeros_like(im1)
1684
+ try:
1685
+ im1[tuple(
1686
+ zip(*a1))] = new_index_intensity # im1[a1[:, 1], a1[:, 0]] = new_index_intensity
1687
+ seg1[tuple(zip(*a1))] = segs
1688
+ except:
1689
+ pass
1690
+
1691
+ seed_point = (int(yc_mp) - min_whit[1], int(xc_mp) - min_whit[0])
1692
+
1693
+ im1 = cv2.equalizeHist(im1.astype(np.uint8))
1694
+ # im1 = cv2.GaussianBlur(im1, (0, 0), 1)
1695
+ try:
1696
+ segmented_area = flood(im1, seed_point, connectivity=1,
1697
+ tolerance=self._tol_cricle_tool*im1.std())
1698
+
1699
+ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
1700
+ sub_seg_new = cv2.morphologyEx((segmented_area>0).astype(np.float32), cv2.MORPH_CLOSE, kernel)
1701
+ im_l, im_f = LargestCC(sub_seg_new, 1)
1702
+ if im_f.shape[0]>1:
1703
+ index_sel = im_l[seed_point[1], seed_point[0]]
1704
+ sub_seg_new = (im_l == index_sel).astype('int')
1705
+ except:
1706
+ sub_seg_new = None
1707
+ if sub_seg_new is not None and self.colorInd != 9876:
1708
+ color = self.colorsCombinations[self.colorInd]
1709
+ seg_new = np.zeros_like(self.segSlice)
1710
+ seg_new[whiteInd[:, 1], whiteInd[:, 0]] = sub_seg_new[tuple(zip(*a1))]
1711
+ color_image = np.stack(
1712
+ (seg_new * color[0] * 255, seg_new * color[1] * 255, seg_new * color[2] * 255), axis=-1)
1713
+ self._magic_slice = color_image
1714
+ self.makeObject()
1715
+ self.update()
1716
+ else:
1717
+ self._magic_slice = None
1718
+
1719
+ def compute_line(x, m, b):
1720
+ return m*x+b
1721
+ #print(self.cursor().pos())
1722
+
1723
+ if self.enabledPen and len(self.penPoints)>0:
1724
+ dx = event.x() - self.lastPos.x()
1725
+ dy = event.y() - self.lastPos.y()
1726
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1727
+
1728
+ endIm = self._endIMage(x, y)
1729
+ if endIm[0]:
1730
+ # update x and y
1731
+ x = endIm[1]
1732
+ y = endIm[2]
1733
+ self.penPoints.append([x, y, self.sliceNum])
1734
+ self.updateCursor(event, x, y, dx, dy, endIm[0])
1735
+
1736
+ # update the window
1737
+ self.update()
1738
+
1739
+ #elif self.enabledCircle:
1740
+ # x, y = self.to_real_world(event.pos().x(), event.pos().y())
1741
+ # self._center_circle = [x,y]
1742
+ # self.update()
1743
+ elif self.enabledErase and len(self.erasePoints)>0:
1744
+ dx = event.x() - self.lastPos.x()
1745
+ dy = event.y() - self.lastPos.y()
1746
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1747
+
1748
+ endIm = self._endIMage(x, y)
1749
+ if endIm[0]:
1750
+ # update x and y
1751
+ x = endIm[1]
1752
+ y = endIm[2]
1753
+ self.erasePoints.append([x, y, self.sliceNum])
1754
+ self.updateCursor(event, x, y, dx, dy,endIm[0])
1755
+
1756
+ # update the window
1757
+ self.update()
1758
+ elif self.enabledRuler: # if RULER is activated
1759
+ dx = event.x() - self.lastPos.x()
1760
+ dy = event.y() - self.lastPos.y()
1761
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1762
+
1763
+
1764
+ endIm = self._endIMage(x, y)
1765
+ if endIm[0]:
1766
+ # update x and y
1767
+ x = endIm[1]
1768
+ y = endIm[2]
1769
+ if 'points' in self.rulerPoints[self.N_rulerPoints]:
1770
+ if len(self.rulerPoints[self.N_rulerPoints]['points'])>0:
1771
+ self.rulerPoints[self.N_rulerPoints]['points'].append([x, y, self.sliceNum])
1772
+ if len(self.rulerPoints[self.N_rulerPoints]['points'])>=2:
1773
+ self.rulerPoints[self.N_rulerPoints]['points'] = [self.rulerPoints[self.N_rulerPoints]['points'][0],
1774
+ self.rulerPoints[self.N_rulerPoints]['points'][-1]]
1775
+ x1, y1 = self.rulerPoints[self.N_rulerPoints]['points'][0][0], self.rulerPoints[self.N_rulerPoints]['points'][0][1]
1776
+ x2, y2 = self.rulerPoints[self.N_rulerPoints]['points'][-1][0], self.rulerPoints[self.N_rulerPoints]['points'][-1][1]
1777
+ self.rulerPoints[self.N_rulerPoints]['center'] = [(x1+x2)/2.0, (y1+y2)/2.0, self.sliceNum]
1778
+ if (y2-y1) != 0:
1779
+ m_prime = -(x2-x1)/(y2-y1)
1780
+ if m_prime != 0:
1781
+ # a, b, c, ax+by+c = 0
1782
+ m = -1.0/m_prime
1783
+ bline= y1 - m*x1
1784
+ self.rulerPoints[self.N_rulerPoints]['center'] = [1, -m,
1785
+ -bline]
1786
+ else:
1787
+ self.rulerPoints[self.N_rulerPoints]['center'] = [0,1,
1788
+ -x1]
1789
+
1790
+
1791
+ b = y1-m_prime*x1
1792
+ delta = 5
1793
+ if abs(m_prime)>1:
1794
+ delta /=abs(m_prime)
1795
+ ruler_perpendicular1 = [(x1-delta, compute_line(x1-delta, m_prime, b), self.sliceNum),
1796
+ (x1, compute_line(x1, m_prime, b), self.sliceNum),
1797
+ (x1+delta, compute_line(x1+delta, m_prime, b), self.sliceNum)]
1798
+ b = y2 - m_prime * x2
1799
+ ruler_perpendicular2 = [(x2-delta, compute_line(x2-delta, m_prime, b), self.sliceNum),
1800
+ (x2, compute_line(x2, m_prime, b), self.sliceNum),
1801
+ (x2+delta, compute_line(x2+delta, m_prime, b), self.sliceNum)]
1802
+
1803
+ else:
1804
+ delta = 5
1805
+ ruler_perpendicular1 = [(x1, y1 - delta, self.sliceNum), (x1, y1, self.sliceNum),
1806
+ (x1, y1 + delta, self.sliceNum)]
1807
+ ruler_perpendicular2 = [(x2, y2 - delta, self.sliceNum), (x2, y2, self.sliceNum),
1808
+ (x2, y2 + delta, self.sliceNum)]
1809
+ self.rulerPoints[self.N_rulerPoints]['center'] = [1, 0,-y1]
1810
+ self.rulerPoints[self.N_rulerPoints]['perpendicular1'] = ruler_perpendicular1
1811
+ self.rulerPoints[self.N_rulerPoints]['perpendicular2'] = ruler_perpendicular2
1812
+
1813
+ self.updateCursor(event, x, y, dx, dy,endIm[0])
1814
+
1815
+ # update the window
1816
+ self.update()
1817
+ elif self.enabledLine and len(self.startLinePoints)>0:
1818
+ dx = event.x() - self.lastPos.x()
1819
+ dy = event.y() - self.lastPos.y()
1820
+ x , y = self.to_real_world(event.pos().x(), event.pos().y())
1821
+ endIm = self._endIMage(x, y)
1822
+ if endIm[0]:
1823
+ # update x and y
1824
+ x = endIm[1]
1825
+ y = endIm[2]
1826
+
1827
+ if len(self.linePoints)>3:
1828
+ try:
1829
+ self.linePoints = generate_extrapoint_on_line(self.startLinePoints[0], [x, y, self.sliceNum], self.sliceNum)
1830
+ except:
1831
+ pass
1832
+ else:
1833
+ self.linePoints.append([x, y ,self.sliceNum])
1834
+
1835
+ self.updateCursor(event, x, y, dx, dy,endIm[0])
1836
+
1837
+ # update the window
1838
+ self.update()
1839
+
1840
+ elif event.buttons() & Qt.LeftButton:
1841
+
1842
+ dx = event.x() - self.lastPos.x()
1843
+ dy = event.y() - self.lastPos.y()
1844
+
1845
+ if self.enabledPan:
1846
+ self.pan(dx, dy)
1847
+ #self.rotateBy(8 * dy, 8 * dx, 0)
1848
+ elif event.buttons() & Qt.RightButton:
1849
+ self._allowed_goto = False
1850
+ dx = abs(event.x() - self.lastPos.x())
1851
+
1852
+ if self.enabledRotate:
1853
+ pZone =zonePoint(event.x(), event.y(), self.lastPressPos.x(), self.lastPressPos.y())
1854
+ if self.pressZone == 1:
1855
+ if pZone == 2 or pZone == 1:
1856
+ self.setZRotation(self.zRot - 1 * dx)
1857
+ elif pZone == 4 or pZone == 3:
1858
+ self.setZRotation(self.zRot + 1 * dx)
1859
+ elif self.pressZone == 2:
1860
+ if pZone == 1:
1861
+ self.setZRotation(self.zRot + 1 * dx)
1862
+ elif pZone == 3:
1863
+ self.setZRotation(self.zRot - 1 * dx)
1864
+ elif self.pressZone == 3:
1865
+ if pZone == 2:
1866
+ self.setZRotation(self.zRot + 1 * dx)
1867
+ elif pZone == 4:
1868
+ self.setZRotation(self.zRot - 1 * dx)
1869
+ elif self.pressZone == 4:
1870
+ if pZone == 1:
1871
+ self.setZRotation(self.zRot - 1 * dx)
1872
+ elif pZone == 3:
1873
+ self.setZRotation(self.zRot + 1 * dx)
1874
+
1875
+ self.lastPos = event.pos()
1876
+
1877
+ if self.enabledGoTo and self._allowed_goto:
1878
+ x, y = self.to_real_world(event.pos().x(), event.pos().y())
1879
+ endIm = self._endIMage(x, y)
1880
+ if endIm[0]:
1881
+ # update x and y
1882
+ x = endIm[1]
1883
+ y = endIm[2]
1884
+ self.goto.emit([x, y, self.sliceNum], [self.currentWidnowName, self.imType])
1885
+
1886
+
1887
+ def keyPressEvent(self, event: QKeyEvent):
1888
+
1889
+ if event.key() == Qt.Key_Control:
1890
+ self.enabledRotate = True
1891
+ self.enabledZoom = True
1892
+ try_disconnect(self)
1893
+
1894
+ def keyReleaseEvent(self, event: QtGui.QKeyEvent) -> None:
1895
+ #key options
1896
+ if event.key() == Qt.Key_0:
1897
+
1898
+ self.updateEvents()
1899
+ self.setCursor(Qt.ArrowCursor)
1900
+ try_disconnect(self)
1901
+ elif event.key() == Qt.Key_1: # ImFreeHand
1902
+
1903
+ self.updateEvents()
1904
+ self.enabledMagicTool = True
1905
+
1906
+ self.setCursor(cursorPaint())
1907
+ try_disconnect(self)
1908
+ try:
1909
+ self.customContextMenuRequested.connect(self.ShowContextMenu)
1910
+ except Exception as e:
1911
+ pass
1912
+ elif event.key() == Qt.Key_2: # Panning
1913
+
1914
+ self.updateEvents()
1915
+ self.enabledPan = True
1916
+ self.setCursor(cursorOpenHand())
1917
+ try_disconnect(self)
1918
+ elif event.key() == Qt.Key_3: # Erasing
1919
+ self.setCursor(cursorOpenHand())
1920
+ self.updateEvents()
1921
+ self.enabledErase = True
1922
+ self.setCursor(cursorErase())
1923
+ try_disconnect(self)
1924
+ elif event.key() == Qt.Key_4: # ImPaint
1925
+ self.updateEvents()
1926
+ self.enabledPen = True
1927
+ self.setCursor(cursorPaint())
1928
+ elif event.key() == Qt.Key_Control:
1929
+ try_disconnect(self)
1930
+ if self.enabledCircle:
1931
+ self.zoomchanged.emit(self._radius_circle/abs(self.to_real_world( 1, 0)[0] - self.to_real_world(0, 0)[0]), True)
1932
+ self.enabledZoom = False
1933
+ try:
1934
+ self.customContextMenuRequested.connect(self.ShowContextMenu)
1935
+ except Exception as e:
1936
+ pass
1937
+
1938
+ def mouseReleaseEvent(self, event):
1939
+ """
1940
+ Mouse release events
1941
+ :param event:
1942
+ :return:
1943
+ """
1944
+ self._allowed_goto = False
1945
+ self.clicked.emit()
1946
+ if self.enabledErase:
1947
+ # erase mode
1948
+ if len(self.erasePoints)>2:
1949
+ self.erasePoints.append(self.erasePoints[0])
1950
+ polErase = ConvertPToPolygons(self.erasePoints) # convert to polygons
1951
+ self.erasePolygon(polErase)
1952
+ elif self.enabledCircle:
1953
+ #from melage.utils.utils import
1954
+ if self._NenabledCircle>0:
1955
+
1956
+ self._center_circle=[]
1957
+ xc, yc = event.pos().x(), event.pos().y()
1958
+ xc_mp, yc_mp = self.to_real_world(xc, yc)
1959
+ num_segments = 50
1960
+ #if self.zoom_level_x<=1:
1961
+ radius = self._radius_circle/4.0#*self.imSpacing[0]#*self.zoom_level_x
1962
+ #else:
1963
+ # radius = self._radius_circle/self.zoom_level_x
1964
+ realx, realy = self.to_real_world(xc, yc)
1965
+
1966
+ points = []
1967
+ for ii in range(num_segments):
1968
+ theta = 2.0 * np.pi * ii / num_segments # get the current angle
1969
+ x = radius * np.cos(theta) # calculate the x component
1970
+ y = radius * np.sin(theta) # calculate the y component
1971
+ #x1, y1 = self.to_real_world(xc+x,yc+y)
1972
+ x1, y1 = realx + x, realy + y
1973
+ points.append([x1,y1, self.sliceNum])
1974
+ if len(points) > 2:
1975
+ from melage.utils.utils import seperate_lcc
1976
+ polygonC = ConvertPointsToPolygons(points, width = 0)
1977
+ try:
1978
+ whiteInd = None
1979
+ if polygonC.is_valid:
1980
+ whiteInd, edges = findIndexWhiteVoxels(polygonC, self.currentWidnowName, bool_permute_axis=False)
1981
+ else:
1982
+ #print('not valid polygon')
1983
+ #polygonC = polygonC.buffer(0)
1984
+ multipolys = ConvertPToPolygons(self.penPoints)
1985
+ whiteInd = np.empty((0, 3))
1986
+ for poly in multipolys:
1987
+ if poly.is_valid:
1988
+ if poly.area<2:
1989
+ continue
1990
+ voxels, edges = findIndexWhiteVoxels(poly, self.currentWidnowName)
1991
+ if voxels is not None:
1992
+ whiteInd = np.vstack((whiteInd, voxels))
1993
+ #if whiteInd is not None:
1994
+ # self.imSeg[tuple(zip(*whiteInd))] = self.colorInd
1995
+ #if whiteInd is not None:
1996
+ # self.segSlice[tuple(zip(*whiteInd))] = self.colorInd
1997
+ center_intensity = self.imSlice[int(yc_mp), int(xc_mp)]
1998
+
1999
+
2000
+ whiteInd = whiteInd[whiteInd[:, 1] < self.imSlice.shape[0], :]
2001
+ whiteInd = whiteInd[whiteInd[:, 0] < self.imSlice.shape[1], :]
2002
+ new_index_intensity = self.imSlice[whiteInd[:, 1], whiteInd[:, 0]]
2003
+
2004
+ std_strategy = False
2005
+ if std_strategy:
2006
+ std_d = new_index_intensity.std()
2007
+ index_proximity = (new_index_intensity > (center_intensity - 20*std_d)) * (new_index_intensity < (
2008
+ center_intensity + 20*std_d))
2009
+ whiteInd = whiteInd[index_proximity, :]
2010
+ try:
2011
+ whiteInd = seperate_lcc(whiteInd, [int(yc_mp), int(xc_mp)])
2012
+ except:
2013
+ pass
2014
+ else:
2015
+ from skimage.segmentation import flood
2016
+ #seg_c = self.segSlice[int(yc_mp), int(xc_mp)]
2017
+ segs =self.segSlice[whiteInd[:, 1], whiteInd[:, 0]]
2018
+ #ind_seg = segs != seg_c
2019
+ min_whit = whiteInd.min(0)
2020
+ from scipy.ndimage import gaussian_filter
2021
+ a1 = whiteInd[:,[1,0]] - min_whit[[1,0]]
2022
+ im1 = np.zeros(a1.max(0) + 1)
2023
+ seg1 = np.zeros_like(im1)
2024
+ try:
2025
+ im1[tuple(zip(*a1))] = new_index_intensity# im1[a1[:, 1], a1[:, 0]] = new_index_intensity
2026
+ seg1[tuple(zip(*a1))] = segs
2027
+ except:
2028
+ pass
2029
+ debug = False
2030
+ if debug:
2031
+ ims = np.zeros_like(self.imSlice)
2032
+ ims[tuple(zip(*whiteInd[:, [1, 0]]))] = 0
2033
+ ims[int(yc_mp), int(xc_mp)] = 1000
2034
+ a1 = whiteInd[:, [1, 0]] - min_whit[[1, 0]]
2035
+ im1 = np.zeros(a1.max(0) + 1)
2036
+ im1[tuple(zip(*a1))] = new_index_intensity
2037
+
2038
+ ims = self.imSlice.copy()
2039
+ ims[tuple(zip(*whiteInd[:, [1, 0]]))] = 0
2040
+ ims[int(yc_mp), int(xc_mp)] = 1000
2041
+ #whit = np.argwhere(ind_sel)[:, [1, 0]] + min_whit[[1, 0]]
2042
+ #ims[tuple(zip(*whit))] = 0
2043
+
2044
+ #import matplotlib.pyplot as plt
2045
+ #plt.imshow(ims)
2046
+ #plt.show()
2047
+
2048
+ seed_point = (int(yc_mp) - min_whit[1], int(xc_mp) - min_whit[0])
2049
+
2050
+ im1 = cv2.equalizeHist(im1.astype(np.uint8))
2051
+ #im1 = cv2.GaussianBlur(im1, (0, 0), 1)
2052
+
2053
+ ind_sel = flood(im1, seed_point, connectivity=1,
2054
+ tolerance=self._tol_cricle_tool*im1.std())*im1
2055
+ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
2056
+ ind_sel = cv2.morphologyEx((ind_sel > 0).astype(np.float32), cv2.MORPH_CLOSE,
2057
+ kernel)
2058
+
2059
+ im_l, im_f = LargestCC(ind_sel, 1)
2060
+ if im_f.shape[0] > 1:
2061
+ index_sel = im_l[seed_point[1], seed_point[0]]
2062
+ ind_sel = (im_l == index_sel).astype('int')
2063
+
2064
+ whit = np.argwhere(ind_sel)[:, [0, 1]] + min_whit[[1, 0]]
2065
+ whiteInd = np.c_[whit[:,[1,0]], np.full(whit.shape[0], whiteInd[0, 2])]
2066
+ #try:
2067
+ # whiteInd = seperate_lcc(whiteInd, [int(yc_mp), int(xc_mp)])
2068
+ #except:
2069
+ # pass
2070
+ """
2071
+ ind_seg = seg1 == seg_c
2072
+ criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
2073
+ _, labels, centers = cv2.kmeans(np.float32(im1.reshape((-1, 2))), 2, None, criteria, 2,
2074
+ cv2.KMEANS_RANDOM_CENTERS)
2075
+ segmented_image = centers[labels.flatten()]
2076
+ segmented_image = segmented_image.reshape(im1.shape)
2077
+ seg_c = segmented_image[int(yc_mp)-min_whit[0], int(xc_mp)-min_whit[1]]
2078
+ (segmented_image-seg_c)
2079
+
2080
+ from melage.utils.utils import Threshold_MultiOtsu
2081
+ num_class = 3
2082
+
2083
+ ths = Threshold_MultiOtsu(im1, num_class)
2084
+
2085
+ for i in range(3):
2086
+ if i == 0:
2087
+ im1[im1 <= ths[i]] = i
2088
+ elif i == len(ths):
2089
+ im1[im1 >= ths[i - 1]] = i
2090
+ print(i)
2091
+ else:
2092
+ im1[(im1 <= ths[i]) * (im1 > ths[i - 1])] = i
2093
+
2094
+
2095
+ prob = np.exp(-(im1 - im1[ind_seg].mean()) ** 2)
2096
+ prob = gaussian_filter(prob, sigma=2)
2097
+ ind_sel = prob > prob.mean()
2098
+ whit = np.argwhere(ind_sel)[:,[1,0]]+min_whit[[1,0]]
2099
+ whiteInd = np.c_[whit, np.full(whit.shape[0], whiteInd[0, 2])]
2100
+
2101
+
2102
+ prob = np.exp(-(new_index_intensity - new_index_intensity[~ind_seg].mean()) ** 2)
2103
+ ind_sel = prob>0.5
2104
+ whiteInd = whiteInd[ind_sel, :]
2105
+
2106
+
2107
+ segs[ind_seg] = 0
2108
+ segs[~ind_seg]=1
2109
+ #X = np.concatenate([np.argwhere(ind_seg), self.imSlice[ind_seg].reshape(-1, 1)], 1)
2110
+ X = np.concatenate([whiteInd[:, [1, 0]]], 1)
2111
+ theta, residuals, rank, s = np.linalg.lstsq(
2112
+ X,segs, rcond=None)
2113
+ pred = np.dot(X, theta)
2114
+ X_new = np.concatenate([whiteInd[:, [1, 0]]], 1)
2115
+ pred_new = np.dot(X_new, theta).squeeze()
2116
+ tol = 0.5
2117
+ ind_sel = (pred_new<=1+tol)*(pred_new>=1-tol)
2118
+ whiteInd = whiteInd[ind_sel,:]
2119
+ """
2120
+ whiteInd, _ = permute_axis(whiteInd, edges, self.currentWidnowName)
2121
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, self.colorInd, self.sliceNum)
2122
+ self.polygon_info.append([polygonC.centroid.x, polygonC.centroid.y, polygonC.area])
2123
+ self.update()
2124
+ except Exception as e:
2125
+ print(e)
2126
+
2127
+
2128
+
2129
+ elif self.enabledRuler: # if ruler option is activated
2130
+ if 'points' in self.rulerPoints[self.N_rulerPoints]:
2131
+ if len(self.rulerPoints[self.N_rulerPoints]['points'])>1:
2132
+ #self.rulerPoints.append(self.rulerPoints[0])
2133
+ #compute_distance = compute_distance_between_two_points(self.rulerPoints)
2134
+ endl = self.rulerPoints[self.N_rulerPoints]['points'][1]
2135
+ startl = self.rulerPoints[self.N_rulerPoints]['points'][0]
2136
+ angl = math.atan2(startl[1] - endl[1], startl[0] - endl[0]) * 180 / np.pi
2137
+ if angl<0:
2138
+ angl = abs(angl)
2139
+ else:
2140
+ angl = 180-angl
2141
+
2142
+ self.rulerPoints[self.N_rulerPoints]['angle'] = angl
2143
+ #print(angl)
2144
+ self.N_rulerPoints += 1
2145
+ elif self.enabledLine: # draw line activated
2146
+ if len(self.linePoints)<=3:
2147
+ self.linePoints = []
2148
+ self.startLinePoints = []
2149
+ pass
2150
+ try:
2151
+ whiteInd = np.round(generate_extrapoint_on_line(self.startLinePoints[0], self.linePoints[-1], self.sliceNum))
2152
+ whiteInd = PermuteProperAxis(whiteInd, self.currentWidnowName)
2153
+ self.LineChanged.emit([[whiteInd[0,:],whiteInd[-1,:]], self.colorInd, False, False])
2154
+ #whiteInd= np.vstack([xs, ys, [self.sliceNum]*len(xs)]).T
2155
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, 1500, self.sliceNum)
2156
+ self.update()
2157
+
2158
+ except Exception as e:
2159
+ pass
2160
+
2161
+ self.linePoints = []
2162
+ self.startLinePoints = []
2163
+
2164
+ elif self.enabledPen: #drawing countour
2165
+ if len(self.penPoints) > 2:
2166
+ polygonC = ConvertPointsToPolygons(self.penPoints, width = self.widthPen)
2167
+ try:
2168
+ whiteInd = None
2169
+ if polygonC.is_valid:
2170
+ whiteInd, edges = findIndexWhiteVoxels(polygonC, self.currentWidnowName)
2171
+
2172
+ else:
2173
+ #print('not valid polygon')
2174
+ #polygonC = polygonC.buffer(0)
2175
+ multipolys = ConvertPToPolygons(self.penPoints)
2176
+ whiteInd = np.empty((0, 3))
2177
+ for poly in multipolys:
2178
+ if poly.is_valid:
2179
+ if poly.area<2:
2180
+ continue
2181
+ voxels, edges = findIndexWhiteVoxels(poly, self.currentWidnowName)
2182
+ if voxels is not None:
2183
+ whiteInd = np.vstack((whiteInd, voxels))
2184
+ #if whiteInd is not None:
2185
+ # self.imSeg[tuple(zip(*whiteInd))] = self.colorInd
2186
+ #if whiteInd is not None:
2187
+ # self.segSlice[tuple(zip(*whiteInd))] = self.colorInd
2188
+ self.segChanged.emit(whiteInd.astype("int"), self.currentWidnowName, self.colorInd, self.sliceNum)
2189
+ self.polygon_info.append([polygonC.centroid.x, polygonC.centroid.y, polygonC.area])
2190
+ self.update()
2191
+ except Exception as e:
2192
+ print(e)
2193
+ self.penPoints = []
2194
+ #elif self.enabledGoTo:
2195
+
2196
+
2197
+
2198
+ def erasePolygon(self,polsErase):
2199
+ """
2200
+ Erase polygons by doing intersection
2201
+ :param polsErase:
2202
+ :return:
2203
+ """
2204
+
2205
+ #changedKeys = []
2206
+ #points = []
2207
+ whiteVoxels = np.empty((0, 3))
2208
+ for polErase in polsErase:
2209
+ #points += fillInsidePol(polErase)
2210
+ if len(polErase.exterior.xy[0]) > 1:
2211
+ # print(list(poly.exterior.coords))
2212
+ try:
2213
+ whiteVoxel, edges = findIndexWhiteVoxels(polErase, self.currentWidnowName)
2214
+ whiteVoxels = np.vstack((whiteVoxels, whiteVoxel))
2215
+ except Exception as e:
2216
+ print(e)
2217
+
2218
+ # if whiteInd is not None:
2219
+ # self.imSeg[tuple(zip(*whiteInd))] = self.colorInd
2220
+ if whiteVoxels.shape[0] != 0:
2221
+ self.segChanged.emit(whiteVoxels.astype("int"), self.currentWidnowName, 0, self.sliceNum)
2222
+ # if len(points)>1:
2223
+ # points = np.array(points).astype("int")[:,0:2][:,[1,0]]
2224
+ #segSlice = self.imSeg
2225
+ #segSlice[tuple(zip(*points))] = 0
2226
+ #self.updateSeg(segSlice)
2227
+ #self.segSlice[tuple(zip(*points))] = 0
2228
+
2229
+
2230
+ self.erasePoints = []
2231
+
2232
+
2233
+ self.update()
2234
+
2235
+ def currentSegSlice(self):
2236
+ """
2237
+ Get current slice image
2238
+ :return:
2239
+ """
2240
+ if self.activeDim == 0:
2241
+ segSlice = self.imSeg[self.sliceNum, :,: ]
2242
+ elif self.activeDim == 1:
2243
+ segSlice = self.imSeg[:, self.sliceNum, :]
2244
+ elif self.activeDim == 2:
2245
+ segSlice = self.imSeg[:, :, self.sliceNum]
2246
+ return segSlice.astype(np.uint8)
2247
+
2248
+ def updateSeg(self, segSlice):
2249
+ """
2250
+ Update segmented image
2251
+ :param segSlice:
2252
+ :return:
2253
+ """
2254
+ if self.activeDim == 0:
2255
+ self.imSeg[self.sliceNum, :,: ] = segSlice
2256
+ elif self.activeDim == 1:
2257
+ self.imSeg[:, self.sliceNum, :] = segSlice
2258
+ elif self.activeDim == 2:
2259
+ self.imSeg[:, :, self.sliceNum] = segSlice
2260
+
2261
+
2262
+ """
2263
+
2264
+ def contourShow(self, contours):
2265
+ def toAxis(contour):
2266
+ return np.squeeze(contour)
2267
+
2268
+ for contour in contours:
2269
+ contour = np.squeeze(contour)
2270
+ glBegin(GL_LINE_STRIP)
2271
+ # These vertices form a closed polygon
2272
+ print(contour)
2273
+ # glColor3f(*self.colorObject) # Red
2274
+ # listPts = list(polyg.exterior.coords)
2275
+ if len(contour.shape)<=1:
2276
+ return
2277
+ for (x, y) in contour:
2278
+ glVertex2f(x, y)
2279
+ glVertex2f(contour[0][0],contour[0][1])
2280
+ glEnd()
2281
+ """
2282
+
2283
+ def drawImPolygon(self): # draw polygons
2284
+ def changeAccordingToWN(points, windowname):
2285
+ if windowname == 'coronal':
2286
+ if self.currentWidnowName == 'sagittal':
2287
+ points = np.array(points)[:,[2,0,1]]
2288
+ return points
2289
+ if self.currentWidnowName == 'axial':
2290
+ points = np.array(points)[:, [2, 0, 1]]
2291
+ return points
2292
+
2293
+
2294
+
2295
+
2296
+ #glPushMatrix()
2297
+ glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT)
2298
+ glMatrixMode(GL_MODELVIEW)
2299
+
2300
+
2301
+ glUseProgram(self.program[1].programId())
2302
+ glUniformMatrix4fv(self.program[1].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
2303
+
2304
+
2305
+
2306
+ self.program[1].enableAttributeArray(self.program[1].vertTexCoordAttrId)
2307
+ self.program[1].setAttributeArray(self.program[1].vertTexCoordAttrId, self.coord)
2308
+ self.program[1].enableAttributeArray(self.program[1].vertPosAttrId)
2309
+ self.program[1].setAttributeArray(self.program[1].vertPosAttrId, self.vertex)
2310
+
2311
+ # set brightness/contrast
2312
+
2313
+ # self.deinterlace = [100, 0, 0]
2314
+ color_location = glGetUniformLocation(self.program[1].programId(), "my_color")
2315
+ glUniform4fv(color_location, 1, self.colorObject)
2316
+
2317
+
2318
+ glDisable(GL_LIGHTING)
2319
+ glNormal3f(0.5, 1.0, 1.0)
2320
+ glLineWidth(1.0)
2321
+ glClearColor(1.0, 0.0, 0.0, 1.0)
2322
+
2323
+ # antialias
2324
+ glEnable(GL_LINE_SMOOTH)
2325
+ glEnable(GL_BLEND)
2326
+
2327
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
2328
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
2329
+
2330
+ glUniform4fv(color_location, 1, self.colorObject)
2331
+ if len(self.points)>1:
2332
+ self.NewPoints.emit(self.points, self.currentWidnowName)
2333
+
2334
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2335
+ #glColor3f(1.0, 0.0, 0.0)
2336
+ for (x, y, z) in self.points:
2337
+
2338
+ glVertex2f(x, y)
2339
+ glEnd()
2340
+
2341
+ if len(self.selectedPoints)>0:
2342
+
2343
+ glPointSize(20)
2344
+ glBegin( GL_POINTS ) # These vertices form a point
2345
+ #glColor3f(1.0, 0.0, 0.0)
2346
+ for (x, y, z) in self.selectedPoints:
2347
+
2348
+ glVertex2f(x, y)
2349
+ glEnd()
2350
+
2351
+
2352
+ if len(self.penPoints)>2:
2353
+ #self.NewPoints.emit(self.penPoints, None)
2354
+ polygonC = ConvertPointsToPolygons(self.penPoints, width=self.widthPen)
2355
+ penPoints = list(polygonC.exterior.coords)
2356
+ glLineWidth(1)
2357
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2358
+ #glColor3f(1.0, 0.0, 0.0)
2359
+ for (x, y, z) in penPoints:
2360
+ glVertex2f(x, y)
2361
+ glEnd()
2362
+ glLineWidth(1.0)
2363
+
2364
+ if self.senderPoints is not None: # arrange points arrray according to the windows information
2365
+ points = changeAccordingToWN(self.senderPoints, self.senderWindow)
2366
+ glLineWidth(1)
2367
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2368
+ #glColor3f(1.0, 0.0, 0.0)
2369
+ for (x, y, z) in points:
2370
+ glVertex2f(x, y)
2371
+ glEnd()
2372
+ glLineWidth(1.0)
2373
+
2374
+ glUseProgram(0)
2375
+ glPopAttrib()
2376
+ # glPopMatrix()
2377
+ glMatrixMode(GL_MODELVIEW)
2378
+
2379
+
2380
+ def DrawerasePolygon(self):
2381
+ """
2382
+ Draw polygon to erase area
2383
+ :return:
2384
+ """
2385
+ glPushMatrix()
2386
+ glPushAttrib(GL_CURRENT_BIT)
2387
+
2388
+ glUseProgram(self.program[1].programId())
2389
+ glUniformMatrix4fv(self.program[1].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
2390
+
2391
+ self.program[1].enableAttributeArray(self.program[1].vertTexCoordAttrId)
2392
+ self.program[1].setAttributeArray(self.program[1].vertTexCoordAttrId, self.coord)
2393
+ self.program[1].enableAttributeArray(self.program[1].vertPosAttrId)
2394
+ self.program[1].setAttributeArray(self.program[1].vertPosAttrId, self.vertex)
2395
+
2396
+
2397
+
2398
+ # color location
2399
+ color_location = glGetUniformLocation(self.program[1].programId(), "my_color")
2400
+ glUniform4fv(color_location, 1, [1,0,0, 1])
2401
+
2402
+ glDisable(GL_LIGHTING)
2403
+ glNormal3f(0.5, 1.0, 1.0)
2404
+ glLineWidth(1.0)
2405
+
2406
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2407
+ glColor3f(1.0, 1.0, 0.0) # Red
2408
+ for (x, y, z) in self.erasePoints:
2409
+ glVertex2f(x, y)
2410
+ glEnd()
2411
+
2412
+ glUseProgram(0)
2413
+
2414
+ glPopAttrib()
2415
+ glPopMatrix()
2416
+
2417
+
2418
+ def DrawLines(self, points, colorv=[1,0,0, 1], width_line= 1):
2419
+ """
2420
+ Draw lines on the screen
2421
+ :param points:
2422
+ :param colorv:
2423
+ :param width_line:
2424
+ :return:
2425
+ """
2426
+ glPushMatrix()
2427
+ glPushAttrib(GL_CURRENT_BIT)
2428
+
2429
+ glUseProgram(self.program[1].programId())
2430
+ glUniformMatrix4fv(self.program[1].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
2431
+
2432
+ self.program[1].enableAttributeArray(self.program[1].vertTexCoordAttrId)
2433
+ self.program[1].setAttributeArray(self.program[1].vertTexCoordAttrId, self.coord)
2434
+ self.program[1].enableAttributeArray(self.program[1].vertPosAttrId)
2435
+ self.program[1].setAttributeArray(self.program[1].vertPosAttrId, self.vertex)
2436
+
2437
+
2438
+
2439
+ # color location
2440
+ color_location = glGetUniformLocation(self.program[1].programId(), "my_color")
2441
+ glUniform4fv(color_location, 1, colorv)
2442
+ #glLineStipple(1, 0x00FF) # [1] dashed lines
2443
+ glEnable(GL_LINE_STIPPLE)
2444
+ #glDisable(GL_LIGHTING)
2445
+ glEnable(GL_LIGHTING)
2446
+ glNormal3f(0.5, 1.0, 1.0)
2447
+ glLineWidth(width_line)
2448
+
2449
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2450
+ glColor3f(1.0, 1.0, 0.0) # Red
2451
+ for (x, y, z) in points:
2452
+ glVertex3f(x, y, z)
2453
+ glEnd()
2454
+
2455
+ glUseProgram(0)
2456
+ glDisable(GL_LINE_STIPPLE)
2457
+ glPopAttrib()
2458
+ glPopMatrix()
2459
+
2460
+
2461
+ def DrawCricles(self, points):
2462
+ """
2463
+ Draw circle to segment image
2464
+ :param points:
2465
+ :return:
2466
+ """
2467
+
2468
+
2469
+ glPushMatrix()
2470
+ glPushAttrib(GL_CURRENT_BIT)
2471
+
2472
+ glUseProgram(self.program[1].programId())
2473
+ glUniformMatrix4fv(self.program[1].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
2474
+
2475
+ self.program[1].enableAttributeArray(self.program[1].vertTexCoordAttrId)
2476
+ self.program[1].setAttributeArray(self.program[1].vertTexCoordAttrId, self.coord)
2477
+ self.program[1].enableAttributeArray(self.program[1].vertPosAttrId)
2478
+ self.program[1].setAttributeArray(self.program[1].vertPosAttrId, self.vertex)
2479
+
2480
+
2481
+
2482
+ # color location
2483
+ color_location = glGetUniformLocation(self.program[1].programId(), "my_color")
2484
+ glUniform4fv(color_location, 1, [1,1,0, 1])
2485
+ #glLineStipple(1, 0x00FF) # [1] dashed lines
2486
+ glEnable(GL_LINE_STIPPLE)
2487
+ #glDisable(GL_LIGHTING)
2488
+ glEnable(GL_LIGHTING)
2489
+ glNormal3f(0.5, 1.0, 1.0)
2490
+ glLineWidth(2.0)
2491
+
2492
+ glBegin( GL_LINE_LOOP ) # These vertices form a closed polygon
2493
+ glColor3f(1.0, 1.0, 0.0) # Red
2494
+ for (x, y, z) in points:
2495
+ glVertex3f(x, y, z)
2496
+ glEnd()
2497
+
2498
+ glUseProgram(0)
2499
+ glDisable(GL_LINE_STIPPLE)
2500
+ glPopAttrib()
2501
+ glPopMatrix()
2502
+
2503
+
2504
+
2505
+ def DrawRulerLines(self):
2506
+ """
2507
+ Draw ruler lines
2508
+ :return:
2509
+ """
2510
+ for ke in self.rulerPoints.keys():
2511
+ if not hasattr(self.rulerPoints[ke], 'keys'):
2512
+ continue
2513
+ for key in self.rulerPoints[ke].keys():
2514
+ if key == 'center' or key == 'angle':
2515
+ continue
2516
+ points = self.rulerPoints[ke][key]
2517
+
2518
+ glPushMatrix()
2519
+ glPushAttrib(GL_CURRENT_BIT)
2520
+
2521
+ glUseProgram(self.program[1].programId())
2522
+ glUniformMatrix4fv(self.program[1].vertModelViewAttrId, 1, GL_FALSE, self.mvpMatrix)
2523
+
2524
+ self.program[1].enableAttributeArray(self.program[1].vertTexCoordAttrId)
2525
+ self.program[1].setAttributeArray(self.program[1].vertTexCoordAttrId, self.coord)
2526
+ self.program[1].enableAttributeArray(self.program[1].vertPosAttrId)
2527
+ self.program[1].setAttributeArray(self.program[1].vertPosAttrId, self.vertex)
2528
+
2529
+
2530
+
2531
+ # color location
2532
+ color_location = glGetUniformLocation(self.program[1].programId(), "my_color")
2533
+ glUniform4fv(color_location, 1, [1,0,0, 1])
2534
+
2535
+ glDisable(GL_LIGHTING)
2536
+ glNormal3f(0.5, 1.0, 1.0)
2537
+ glLineWidth(1.0)
2538
+
2539
+ glBegin( GL_LINE_STRIP ) # These vertices form a closed polygon
2540
+ glColor3f(1.0, 1.0, 0.0) # Red
2541
+ for (x, y, z) in points:
2542
+ glVertex2f(x, y)
2543
+ glEnd()
2544
+
2545
+ glUseProgram(0)
2546
+
2547
+ glPopAttrib()
2548
+ glPopMatrix()
2549
+
2550
+
2551
+
2552
+
2553
+ def makeObject(self):
2554
+ """
2555
+ Create object before painting
2556
+ :return:
2557
+ """
2558
+ activate_kspace = False
2559
+
2560
+ if self.imSlice is None:
2561
+ return
2562
+ if self.BandPR1 > 0 or self.contrast!=1.0 or self.hamming: # if image enhancement is active
2563
+ activate_kspace = True
2564
+ imslice = self.imSlice
2565
+ if activate_kspace:
2566
+ if self.sliceNum in self._kspaces.keys():
2567
+ kspace = np.copy(self._kspaces[self.sliceNum])
2568
+ else:
2569
+ kspace = fftshift(fft(ifftshift(imslice)))
2570
+ self._kspaces[self.sliceNum] = np.copy(kspace)
2571
+
2572
+
2573
+ # band pass filter
2574
+ if self.BandPR1 > 0:
2575
+ from melage.utils.utils import computeAnisotropyElipse
2576
+ self.insideElipse = computeAnisotropyElipse(kspace)
2577
+ #SigmaSquared = (self.imWidth*self.BandPR1/10)** 2
2578
+ #g = np.array([np.exp(-(np.arange(0,self.imWidth) - self.imWidth/2)** 2 / SigmaSquared)]*self.imHeight)
2579
+ #TwoDGauss = g*g
2580
+ #HighPass = fftshift(TwoDGauss);
2581
+ #kspace*=TwoDGauss
2582
+
2583
+ r = np.hypot(*kspace.shape) / 2 * (self.BandPR1/2)
2584
+ r2 = self.BandPR2*r
2585
+ r2=0
2586
+ rows, cols = np.array(kspace.shape, dtype=int)
2587
+ a, b = np.floor(np.array((rows, cols)) / 2).astype("int")
2588
+ y, x = np.ogrid[-a:rows - a, -b:cols - b]
2589
+ #xx = kspace.shape[0] // 2
2590
+ #yy = kspace.shape[1] // 2
2591
+ #xpx = 1
2592
+ # = np.copy(kspace[xx-xpx:xx+xpx, yy-xpx:yy+xpx])
2593
+
2594
+
2595
+ notMask = self.insideElipse(x, y, r2)
2596
+ valcenter = np.copy(kspace[notMask])
2597
+ mask = self.insideElipse(x, y, r)
2598
+
2599
+ kspace[mask] = (self.BandPR2-0.5)*2*kspace[mask]
2600
+ #kspace[xx - xpx:xx + xpx, yy - xpx:yy + xpx] = valcenter
2601
+ kspace[notMask] = valcenter
2602
+
2603
+
2604
+
2605
+
2606
+ if self.contrast!=1.0:
2607
+
2608
+ x = kspace.shape[0] // 2
2609
+ y = kspace.shape[1] // 2
2610
+ kspace[x-2:x+2, y-2:y+2] *= self.contrast
2611
+
2612
+ if self.hamming:
2613
+
2614
+ x, y = kspace.shape
2615
+ window = np.outer(np.hamming(x), np.hamming(y))
2616
+ kspace*=window
2617
+
2618
+ vis = False
2619
+ if vis:
2620
+ kspaceAbs = np.absolute(kspace)
2621
+ if kspaceAbs.max() > 0:
2622
+ scaling_c = np.power(10., -3)
2623
+ np.log1p(kspaceAbs * scaling_c, out=kspaceAbs)
2624
+ # normalize between 0 and 255
2625
+ fmin = float(kspaceAbs.min())
2626
+ fmax = float(kspaceAbs.max())
2627
+ if fmax != fmin:
2628
+ coeff = fmax - fmin
2629
+ kspaceAbs[:] = np.floor((kspaceAbs[:] - fmin) / coeff * 255.)
2630
+
2631
+
2632
+ #imslice = np.require(kspaceAbs, np.uint8)
2633
+ imslice = kspaceAbs
2634
+ else:
2635
+ imslice = np.absolute(fftshift(ifft(ifftshift(kspace))))
2636
+
2637
+
2638
+
2639
+
2640
+
2641
+ ##############################################################################
2642
+ glEnable(GL_TEXTURE_2D) # Enable texturing
2643
+ glEnable(GL_COLOR_MATERIAL)
2644
+
2645
+ self.textureID = glGenTextures(1) # Obtain an id for the texture
2646
+ glBindTexture(GL_TEXTURE_2D, self.textureID) # Set as the current texture
2647
+ if self.smooth:
2648
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
2649
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
2650
+ else:
2651
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
2652
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
2653
+
2654
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
2655
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
2656
+
2657
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE)# automatic mipmap
2658
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)
2659
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)
2660
+ #glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
2661
+
2662
+
2663
+
2664
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
2665
+ #glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
2666
+
2667
+ #glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.imWidth, self.imHeight, 0,
2668
+ #GL_RGB, GL_UNSIGNED_BYTE,
2669
+ #self.imdata)
2670
+
2671
+ glPixelTransferf(GL_RED_SCALE, 1)
2672
+ glPixelTransferf(GL_GREEN_SCALE, 1)
2673
+ glPixelTransferf(GL_BLUE_SCALE, 1)
2674
+
2675
+ if self.n_colors>1:
2676
+ if self.sliceNum not in self._threshold_image or self.n_colors!= self._n_colors_previous\
2677
+ or activate_kspace:
2678
+ self.create_histogram(imslice, self.n_colors)
2679
+ self._n_colors_previous = self.n_colors
2680
+ imslice = self._threshold_image[self.sliceNum].copy()
2681
+ imslice_seg = imslice
2682
+
2683
+ else:
2684
+ imslice = np.tile(np.expand_dims(imslice, -1), 3)
2685
+ if self.showSeg:
2686
+ imSeg = np.ones_like(self.segSlice.astype(np.float64))*10000
2687
+ imSeg = np.tile(np.expand_dims(imSeg, -1), 3)
2688
+ #imSeg[:,3]= 1
2689
+ uq = np.unique(self.segSlice.astype(np.float64))
2690
+ if 9876 not in self.colorInds:#len(self.colorsCombinations):
2691
+ selected_ud = self.colorInds
2692
+ else:
2693
+ selected_ud = uq
2694
+ for u in uq:
2695
+ if u == 0:
2696
+ continue
2697
+ elif u == 1500:
2698
+ color = [0.9,0.5,0.0,1]
2699
+ ind = self.segSlice == u
2700
+ try:
2701
+ imSeg[ind, 0] = color[0]*255.0
2702
+ imSeg[ind, 1] = color[1]*255.0
2703
+ imSeg[ind, 2] = color[2]*255.0
2704
+ except:
2705
+ print('Please check the color index {}'.format(u))
2706
+ if u in selected_ud and u in self.colorsCombinations:
2707
+ color = self.colorsCombinations[u]
2708
+ ind = self.segSlice == u
2709
+ try:
2710
+ imSeg[ind, 0] = color[0]*255.0
2711
+ imSeg[ind, 1] = color[1]*255.0
2712
+ imSeg[ind, 2] = color[2]*255.0
2713
+ except:
2714
+ print('Please check the color index {}'.format(u))
2715
+ #imslice[ind, 3] = color[3]
2716
+
2717
+ ind_total = imSeg.sum(2)!=30000
2718
+ try:
2719
+ imslice_seg = imslice
2720
+ if self._magic_slice is not None:
2721
+ seg = self._magic_slice
2722
+ imSeg[seg > 0] = seg[seg > 0]
2723
+ ind_total = imSeg.sum(2) != 30000
2724
+
2725
+ if ind_total.sum()>0:
2726
+
2727
+ imslice_seg[ind_total, :] = cv2.addWeighted(imslice[ind_total, :].astype('uint8'), 1-self.intensitySeg,
2728
+ imSeg[ind_total, :].astype('uint8'), self.intensitySeg, 1)
2729
+
2730
+ #ind_total = (seg-self.segSlice>0) > 0
2731
+ #if ind_total.sum()>0:
2732
+ # imslice_seg[ind_total, :] = cv2.addWeighted(imslice[ind_total, :].astype('uint8'), 1 - self.intensitySeg,
2733
+ # self._magic_slice[ind_total, :].astype('uint8'), self.intensitySeg, 1)
2734
+ except Exception as e:
2735
+ print(e)
2736
+ if len(imslice)!=0:
2737
+ imslice_seg = imslice_seg.clip(0, 255)
2738
+ else:
2739
+ imslice_seg = imslice
2740
+
2741
+
2742
+
2743
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, self.imWidth, self.imHeight, 0,
2744
+ GL_RGB, GL_UNSIGNED_BYTE,
2745
+ imslice_seg)
2746
+
2747
+ glDisable(GL_COLOR_MATERIAL)
2748
+ glDisable(GL_TEXTURE_2D) # Disable texturing
2749
+
2750
+
2751
+ ##############################################################################
2752
+
2753
+ #genList = glGenLists(1)
2754
+ #glNewList(1, GL_COMPILE)
2755
+
2756
+
2757
+ def updateEvents(self):
2758
+ """
2759
+ Updating events
2760
+ :return:
2761
+ """
2762
+ self.enabledPan = False
2763
+ self.enabledCircle = False
2764
+ self._NenabledCircle = 1
2765
+ self._center_circle= []
2766
+
2767
+ self.enabledPointSelection = False
2768
+ self._allowed_goto = False
2769
+ self.enabledRotate = False
2770
+ self.enabledMagicTool = False
2771
+ self.enabledErase = False
2772
+ self.enabledZoom = False
2773
+ self.enabledRuler = False
2774
+ self.enabledLine = False
2775
+ self.erasePoints = []
2776
+ self.points = []
2777
+ self.penPoints = []
2778
+ #self.setCursor(Qt.ArrowCursor)
2779
+
2780
+
2781
+ ##########################################################################################################################
2782
+
2783
+ if __name__ == '__main__':
2784
+ from PyQt5.QtWidgets import QApplication
2785
+ app = QApplication(sys.argv)
2786
+ #window = MainWindow0()
2787
+ #window.showMaximized()
2788
+ sys.exit(app.exec_())