scikit-surgeryvtk 2.3.0__tar.gz → 2.4.0__tar.gz

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 (76) hide show
  1. {scikit_surgeryvtk-2.3.0/scikit_surgeryvtk.egg-info → scikit_surgeryvtk-2.4.0}/PKG-INFO +2 -2
  2. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0/scikit_surgeryvtk.egg-info}/PKG-INFO +2 -2
  3. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/scikit_surgeryvtk.egg-info/SOURCES.txt +3 -1
  4. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/scikit_surgeryvtk.egg-info/requires.txt +1 -1
  5. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/setup.py +1 -1
  6. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/_version.py +3 -3
  7. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/utils/projection_utils.py +2 -4
  8. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_base_calibrated_window.py +13 -7
  9. scikit_surgeryvtk-2.4.0/sksurgeryvtk/widgets/vtk_base_stereo_window.py +123 -0
  10. scikit_surgeryvtk-2.4.0/sksurgeryvtk/widgets/vtk_interlaced_stereo_window.py +181 -0
  11. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_overlay_window.py +11 -2
  12. scikit_surgeryvtk-2.4.0/sksurgeryvtk/widgets/vtk_stacked_stereo_window.py +113 -0
  13. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_zbuffer_window.py +3 -1
  14. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/test_requirements.py +1 -1
  15. scikit_surgeryvtk-2.3.0/tests/widgets/test_interlaced_stereo_window.py → scikit_surgeryvtk-2.4.0/tests/widgets/test_stereo_window.py +54 -29
  16. scikit_surgeryvtk-2.3.0/sksurgeryvtk/widgets/vtk_interlaced_stereo_window.py +0 -287
  17. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/LICENSE +0 -0
  18. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/MANIFEST.in +0 -0
  19. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/README.rst +0 -0
  20. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/scikit_surgeryvtk.egg-info/dependency_links.txt +0 -0
  21. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/scikit_surgeryvtk.egg-info/top_level.txt +0 -0
  22. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/setup.cfg +0 -0
  23. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/__init__.py +0 -0
  24. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/camera/__init__.py +0 -0
  25. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/camera/vtk_camera_model.py +0 -0
  26. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/__init__.py +0 -0
  27. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/outline_render.py +0 -0
  28. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/surface_model_loader.py +0 -0
  29. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/voxelise.py +0 -0
  30. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_base_actor.py +0 -0
  31. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_base_model.py +0 -0
  32. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_cylinder_model.py +0 -0
  33. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_grid_model.py +0 -0
  34. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_image_model.py +0 -0
  35. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_point_model.py +0 -0
  36. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_sphere_model.py +0 -0
  37. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_surface_model.py +0 -0
  38. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_surface_model_directory_loader.py +0 -0
  39. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/models/vtk_tube_model.py +0 -0
  40. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/text/__init__.py +0 -0
  41. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/text/text_overlay.py +0 -0
  42. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/utils/__init__.py +0 -0
  43. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/utils/matrix_utils.py +0 -0
  44. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/utils/platform_utils.py +0 -0
  45. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/utils/polydata_utils.py +0 -0
  46. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/__init__.py +0 -0
  47. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_lus_simulator.py +0 -0
  48. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_rendering_generator.py +0 -0
  49. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/sksurgeryvtk/widgets/vtk_reslice_widget.py +0 -0
  50. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/camera/__init__.py +0 -0
  51. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/camera/test_liver_overlay.py +0 -0
  52. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/camera/test_vtk_camera_model.py +0 -0
  53. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/__init__.py +0 -0
  54. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_surface_model_loader.py +0 -0
  55. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_voxelise.py +0 -0
  56. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_cylinder_model.py +0 -0
  57. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_image_model.py +0 -0
  58. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_point_model.py +0 -0
  59. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_sphere_model.py +0 -0
  60. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_surface_model.py +0 -0
  61. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_surface_model_directory_loader.py +0 -0
  62. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_tube_model.py +0 -0
  63. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/models/test_vtk_unstructured_grid.py +0 -0
  64. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/utils/__init__.py +0 -0
  65. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/utils/test_matrix_utills.py +0 -0
  66. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/utils/test_polydata_utils.py +0 -0
  67. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/utils/test_projection_utils.py +0 -0
  68. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/__init__.py +0 -0
  69. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_lus_simulator.py +0 -0
  70. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_rendering_generator.py +0 -0
  71. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_vtk_overlay_window.py +0 -0
  72. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_vtk_overlay_window_5_layers.py +0 -0
  73. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_vtk_overlay_window_with_outlines.py +0 -0
  74. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_vtk_reslice.py +0 -0
  75. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/tests/widgets/test_vtkzbuffer_window.py +0 -0
  76. {scikit_surgeryvtk-2.3.0 → scikit_surgeryvtk-2.4.0}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scikit-surgeryvtk
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: scikit-surgeryvtk uses VTK for Image Guided Surgery applications
5
5
  Home-page: https://github.com/SciKit-Surgery/scikit-surgeryvtk
6
6
  Author: Thomas Dowrick
@@ -21,7 +21,7 @@ Description-Content-Type: text/x-rst
21
21
  License-File: LICENSE
22
22
  Requires-Dist: numpy>=1.11
23
23
  Requires-Dist: vtk>=9.5.0
24
- Requires-Dist: PySide6>=6.5.1.1
24
+ Requires-Dist: PySide6<6.10.0,>=6.5.1.1
25
25
  Requires-Dist: opencv-contrib-python-headless>=4.2.0.32
26
26
  Requires-Dist: scikit-surgerycore>=0.1.7
27
27
  Requires-Dist: scikit-surgeryimage>=0.10.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scikit-surgeryvtk
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: scikit-surgeryvtk uses VTK for Image Guided Surgery applications
5
5
  Home-page: https://github.com/SciKit-Surgery/scikit-surgeryvtk
6
6
  Author: Thomas Dowrick
@@ -21,7 +21,7 @@ Description-Content-Type: text/x-rst
21
21
  License-File: LICENSE
22
22
  Requires-Dist: numpy>=1.11
23
23
  Requires-Dist: vtk>=9.5.0
24
- Requires-Dist: PySide6>=6.5.1.1
24
+ Requires-Dist: PySide6<6.10.0,>=6.5.1.1
25
25
  Requires-Dist: opencv-contrib-python-headless>=4.2.0.32
26
26
  Requires-Dist: scikit-surgerycore>=0.1.7
27
27
  Requires-Dist: scikit-surgeryimage>=0.10.1
@@ -36,11 +36,13 @@ sksurgeryvtk/utils/polydata_utils.py
36
36
  sksurgeryvtk/utils/projection_utils.py
37
37
  sksurgeryvtk/widgets/__init__.py
38
38
  sksurgeryvtk/widgets/vtk_base_calibrated_window.py
39
+ sksurgeryvtk/widgets/vtk_base_stereo_window.py
39
40
  sksurgeryvtk/widgets/vtk_interlaced_stereo_window.py
40
41
  sksurgeryvtk/widgets/vtk_lus_simulator.py
41
42
  sksurgeryvtk/widgets/vtk_overlay_window.py
42
43
  sksurgeryvtk/widgets/vtk_rendering_generator.py
43
44
  sksurgeryvtk/widgets/vtk_reslice_widget.py
45
+ sksurgeryvtk/widgets/vtk_stacked_stereo_window.py
44
46
  sksurgeryvtk/widgets/vtk_zbuffer_window.py
45
47
  tests/test_requirements.py
46
48
  tests/camera/__init__.py
@@ -62,9 +64,9 @@ tests/utils/test_matrix_utills.py
62
64
  tests/utils/test_polydata_utils.py
63
65
  tests/utils/test_projection_utils.py
64
66
  tests/widgets/__init__.py
65
- tests/widgets/test_interlaced_stereo_window.py
66
67
  tests/widgets/test_lus_simulator.py
67
68
  tests/widgets/test_rendering_generator.py
69
+ tests/widgets/test_stereo_window.py
68
70
  tests/widgets/test_vtk_overlay_window.py
69
71
  tests/widgets/test_vtk_overlay_window_5_layers.py
70
72
  tests/widgets/test_vtk_overlay_window_with_outlines.py
@@ -1,6 +1,6 @@
1
1
  numpy>=1.11
2
2
  vtk>=9.5.0
3
- PySide6>=6.5.1.1
3
+ PySide6<6.10.0,>=6.5.1.1
4
4
  opencv-contrib-python-headless>=4.2.0.32
5
5
  scikit-surgerycore>=0.1.7
6
6
  scikit-surgeryimage>=0.10.1
@@ -54,7 +54,7 @@ setup(
54
54
  install_requires=[
55
55
  'numpy>=1.11',
56
56
  'vtk>=9.5.0',
57
- 'PySide6>=6.5.1.1',
57
+ 'PySide6>=6.5.1.1,<6.10.0',
58
58
  'opencv-contrib-python-headless>=4.2.0.32',
59
59
  'scikit-surgerycore>=0.1.7',
60
60
  'scikit-surgeryimage>=0.10.1',
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-09-03T07:49:14+0100",
11
+ "date": "2026-04-20T06:13:00+0100",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "f505235c59e32ca11f5172769e659f62e4e8ed53",
15
- "version": "2.3.0"
14
+ "full-revisionid": "0422a88b01c027061aef20a450a63f3ba6100e3f",
15
+ "version": "2.4.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -36,12 +36,11 @@ def _validate_input_for_projection(points,
36
36
 
37
37
  if camera_to_world is None:
38
38
  raise ValueError('camera_to_world is NULL')
39
-
40
- vm.validate_rigid_matrix(camera_to_world)
39
+ if not isinstance(camera_to_world, np.ndarray):
40
+ raise TypeError('camera_to_world is not an np.ndarray')
41
41
 
42
42
  if camera_matrix is None:
43
43
  raise ValueError('camera_matrix is NULL')
44
-
45
44
  vm.validate_camera_matrix(camera_matrix)
46
45
 
47
46
  if distortion is not None:
@@ -63,7 +62,6 @@ def project_points(points,
63
62
  :raises: ValueError, TypeError:
64
63
  :return: nx2 ndarray representing 2D points, typically in pixels
65
64
  """
66
-
67
65
  _validate_input_for_projection(points,
68
66
  camera_to_world,
69
67
  camera_matrix,
@@ -32,6 +32,9 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
32
32
  :param clipping_range: Near/Far clipping range.
33
33
  :param opencv_style: If True, adopts OpenCV camera convention, otherwise OpenGL.
34
34
  :param reset_camera: If True, resets camera when a new model is added.
35
+ :param aspect_ratio: to adjust for slight differences in pixel size, expressed as x/y.
36
+ :param xscale: horizontal scale factor, for horizontal stacking
37
+ :param yscale: vertical scale factor, for vertical stacking
35
38
  """
36
39
  def __init__(
37
40
  self,
@@ -39,7 +42,10 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
39
42
  camera_matrix=None,
40
43
  clipping_range=(1, 1000),
41
44
  opencv_style=True,
42
- reset_camera=True
45
+ reset_camera=True,
46
+ aspect_ratio=1,
47
+ xscale=1,
48
+ yscale=1
43
49
  ):
44
50
  """
45
51
  Constructs a new VTKBaseCalibratedWindow.
@@ -58,7 +64,9 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
58
64
  self.reset_camera = reset_camera
59
65
 
60
66
  # Member variables.
61
- self.aspect_ratio = 1
67
+ self.aspect_ratio = aspect_ratio
68
+ self.xscale = xscale
69
+ self.yscale = yscale
62
70
  self.camera_to_world = np.eye(4)
63
71
  self.screen = None
64
72
 
@@ -212,7 +220,6 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
212
220
  # Issue #236: Take size from vtkRenderWindow, not Qt widget.
213
221
  # Issue #236: On Mac Retina displays, size given by Qt is halved.
214
222
  window_size = self.GetRenderWindow().GetSize()
215
-
216
223
  if window_size[0] == 0:
217
224
  LOGGER.warning("VTK Render Window appears to have zero width, so abandoning _update_projection_matrix.")
218
225
  return opengl_mat, vtk_mat
@@ -227,8 +234,8 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
227
234
  opengl_mat, vtk_mat = cm.set_camera_intrinsics(
228
235
  renderer,
229
236
  camera,
230
- input_image.shape[1],
231
- input_image.shape[0],
237
+ input_image.shape[1] * self.xscale,
238
+ input_image.shape[0] * self.yscale,
232
239
  self.camera_matrix[0][0],
233
240
  self.camera_matrix[1][1],
234
241
  self.camera_matrix[0][2],
@@ -242,7 +249,7 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
242
249
  window_size[1],
243
250
  input_image.shape[1],
244
251
  input_image.shape[0],
245
- self.aspect_ratio,
252
+ aspect_ratio=self.aspect_ratio,
246
253
  )
247
254
 
248
255
  x_min, y_min, x_max, y_max = cm.compute_viewport(
@@ -332,7 +339,6 @@ class VTKBaseCalibratedWindow(QVTKRenderWindowInteractor):
332
339
  Sets the camera position and orientation, from a numpy 4x4 array.
333
340
  :param camera_to_world: camera_to_world transform.
334
341
  """
335
- vm.validate_rigid_matrix(camera_to_world)
336
342
  self.camera_to_world = camera_to_world
337
343
  vtk_mat = mu.create_vtk_matrix_from_numpy(camera_to_world)
338
344
  self._update_pose_matrices(vtk_mat)
@@ -0,0 +1,123 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Base class for vtk_interlaced_stereo_window.py and vtk_stacked_stereo_window.py
5
+ """
6
+
7
+ import abc
8
+ from PySide6 import QtWidgets
9
+ from PySide6.QtWidgets import QSizePolicy
10
+ import sksurgeryvtk.widgets.vtk_overlay_window as ow
11
+
12
+
13
+ class VTKBaseStereoWindow(QtWidgets.QWidget):
14
+ """
15
+ Class to contain a pair of VTKOverlayWindows, left, right,
16
+ and common methods.
17
+
18
+ :param init_widget: If True we will call self.Initialize and self.Start
19
+ as part of the init function. Set to false if you're on Linux.
20
+ """
21
+ # pylint: disable=too-many-positional-arguments
22
+ def __init__(self,
23
+ offscreen=False,
24
+ left_camera_matrix=None,
25
+ right_camera_matrix=None,
26
+ clipping_range=(1, 10000),
27
+ init_widget=True,
28
+ left_is_top=True,
29
+ aspect_ratio=1,
30
+ xscale=1,
31
+ yscale=1
32
+ ):
33
+
34
+ super().__init__()
35
+ self.left_is_top = left_is_top
36
+ self.xscale = xscale
37
+ self.yscale = yscale
38
+ self.aspect_ratio = aspect_ratio
39
+
40
+ self.left_widget = ow.VTKOverlayWindow(
41
+ offscreen=offscreen,
42
+ camera_matrix=left_camera_matrix,
43
+ clipping_range=clipping_range,
44
+ init_widget=init_widget,
45
+ aspect_ratio=aspect_ratio,
46
+ xscale=xscale,
47
+ yscale=yscale
48
+ )
49
+ self.left_widget.setContentsMargins(0, 0, 0, 0)
50
+
51
+ self.right_widget = ow.VTKOverlayWindow(
52
+ offscreen=offscreen,
53
+ camera_matrix=right_camera_matrix,
54
+ clipping_range=clipping_range,
55
+ init_widget=init_widget,
56
+ aspect_ratio=aspect_ratio,
57
+ xscale=xscale,
58
+ yscale=yscale
59
+ )
60
+ self.right_widget.setContentsMargins(0, 0, 0, 0)
61
+
62
+ self.size_policy = \
63
+ QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
64
+ self.setSizePolicy(self.size_policy)
65
+ self.setContentsMargins(0, 0, 0, 0)
66
+
67
+ def set_camera_matrices(self, left_camera_matrix, right_camera_matrix):
68
+ """
69
+ Sets both the left and right camera matrices.
70
+
71
+ :param left_camera_matrix: numpy 3x3 ndarray containing fx, fy, cx, cy
72
+ :param right_camera_matrix: numpy 3x3 ndarray containing fx, fy, cx, cy
73
+ """
74
+ self.left_widget.set_camera_matrix(left_camera_matrix)
75
+ self.right_widget.set_camera_matrix(right_camera_matrix)
76
+
77
+ def set_camera_poses(self, left_camera_to_world, right_camera_to_world):
78
+ """
79
+ Sets the pose of both the left and right camera.
80
+
81
+ :param left_camera_to_world: 4x4 numpy ndarray, rigid transform
82
+ :param right_camera_to_world: 4x4 numpy ndarray, rigid transform
83
+ """
84
+ self.left_widget.set_camera_pose(left_camera_to_world)
85
+ self.right_widget.set_camera_pose(right_camera_to_world)
86
+
87
+ def add_vtk_models(self, models):
88
+ """
89
+ Add models to both left and right widgets.
90
+ Here a model is anything with an attribute called actor that
91
+ is a vtkActor.
92
+
93
+ :param models: vtk_base_model
94
+ """
95
+ self.left_widget.add_vtk_models(models)
96
+ self.right_widget.add_vtk_models(models)
97
+
98
+ def add_vtk_actor(self, actor):
99
+ """
100
+ Adds a vtkActor to both left and right widgets.
101
+
102
+ :param actor: vtkActor
103
+ """
104
+ self.left_widget.add_vtk_actor(actor)
105
+ self.right_widget.add_vtk_actor(actor)
106
+
107
+ @abc.abstractmethod
108
+ def set_video_images(self, left_image, right_image):
109
+ """
110
+ Derived classes must implement this method.
111
+ """
112
+
113
+ @abc.abstractmethod
114
+ def render(self):
115
+ """
116
+ Derived classes must implement this method.
117
+ """
118
+
119
+ @abc.abstractmethod
120
+ def save_scene_to_file(self, file_name):
121
+ """
122
+ Derived classes must implement this method.
123
+ """
@@ -0,0 +1,181 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Module to provide an interlaced stereo window, designed for
5
+ driving things like the Storz 3D laparoscope monitor.
6
+ """
7
+
8
+ # pylint: disable=c-extension-no-member, no-name-in-module, too-many-instance-attributes
9
+ import cv2
10
+ import numpy as np
11
+ from PySide6 import QtWidgets
12
+ import sksurgeryimage.processing.interlace as i
13
+ import sksurgeryvtk.widgets.vtk_base_stereo_window as bw
14
+ import sksurgeryvtk.widgets.vtk_overlay_window as ow
15
+
16
+
17
+ class VTKInterlacedStereoWindow(bw.VTKBaseStereoWindow):
18
+ """
19
+ Class to contain a pair of VTKOverlayWindows, left, right, that we render,
20
+ grab, interlace, and then display as a background image
21
+ on a separate VTKOverlayWindow.
22
+
23
+ :param init_widget: If True we will call self.Initialize and self.Start
24
+ as part of the init function. Set to false if you're on Linux.
25
+ """
26
+ # pylint: disable=too-many-positional-arguments
27
+ def __init__(self,
28
+ offscreen=False,
29
+ left_camera_matrix=None,
30
+ right_camera_matrix=None,
31
+ clipping_range=(1, 10000),
32
+ init_widget=True,
33
+ left_is_top=True,
34
+ aspect_ratio=1
35
+ ):
36
+
37
+ # Superclass creates left/right viewer.
38
+ super().__init__(offscreen=offscreen,
39
+ left_camera_matrix=left_camera_matrix,
40
+ right_camera_matrix=right_camera_matrix,
41
+ clipping_range=clipping_range,
42
+ init_widget=init_widget,
43
+ left_is_top=left_is_top,
44
+ aspect_ratio=aspect_ratio,
45
+ xscale=1,
46
+ yscale=1
47
+ )
48
+
49
+ # This class adds an interlaced widget and a layout.
50
+ self.interlaced_widget = ow.VTKOverlayWindow(
51
+ offscreen=offscreen,
52
+ init_widget=init_widget,
53
+ aspect_ratio=aspect_ratio,
54
+ xscale=1,
55
+ yscale=1
56
+ )
57
+ self.interlaced_widget.setContentsMargins(0, 0, 0, 0)
58
+
59
+ self.stacked = QtWidgets.QStackedWidget()
60
+ self.stacked.addWidget(self.left_widget)
61
+ self.stacked.addWidget(self.right_widget)
62
+ self.stacked.addWidget(self.interlaced_widget)
63
+ self.stacked.setContentsMargins(0, 0, 0, 0)
64
+ self.stacked.setSizePolicy(self.size_policy)
65
+
66
+ self.layout = QtWidgets.QVBoxLayout(self)
67
+ self.layout.setContentsMargins(0, 0, 0, 0)
68
+ self.layout.setSpacing(0)
69
+ self.layout.addWidget(self.stacked)
70
+
71
+ # Default the view to show the interlaced window.
72
+ self.stacked.setCurrentIndex(2)
73
+
74
+ # pylint: disable=invalid-name
75
+ def paintEvent(self, ev):
76
+ """
77
+ Ensure that the interlaced image is recomputed.
78
+ """
79
+ self.render()
80
+ super().paintEvent(ev)
81
+
82
+ # pylint: disable=invalid-name
83
+ def resizeEvent(self, ev):
84
+ """
85
+ Ensure that the interlaced image is recomputed.
86
+ """
87
+ self.set_current_viewer_index(0)
88
+ self.left_widget.resizeEvent(ev)
89
+ self.left_widget.Render()
90
+ self.left_widget.update()
91
+ self.set_current_viewer_index(1)
92
+ self.right_widget.resizeEvent(ev)
93
+ self.right_widget.Render()
94
+ self.right_widget.update()
95
+ self.set_current_viewer_index(2)
96
+ self.interlaced_widget.resizeEvent(ev)
97
+ self.interlaced_widget.Render()
98
+ self.interlaced_widget.update()
99
+ super().resizeEvent(ev)
100
+
101
+
102
+ def closeEvent(self, QCloseEvent):
103
+ self.left_widget.Finalize()
104
+ self.right_widget.Finalize()
105
+ self.interlaced_widget.Finalize()
106
+ super().closeEvent(QCloseEvent)
107
+
108
+ def set_current_viewer_index(self, viewer_index):
109
+ """
110
+ Sets the current viewer selection.
111
+ Defaults to 2
112
+
113
+ 0 = left
114
+ 1 = right
115
+ 2 = interlaced
116
+
117
+ :param viewer_index: index of viewer, as above.
118
+ """
119
+ if viewer_index < 0:
120
+ raise ValueError('viewer_index must be >= 0')
121
+ if viewer_index > 2:
122
+ raise ValueError('viewer_index must be <= 2')
123
+ self.stacked.setCurrentIndex(viewer_index)
124
+
125
+ def set_video_images(self, left_image, right_image):
126
+ """
127
+ Sets both left and right video images. Images
128
+ must be the same shape, and have an even number of rows.
129
+
130
+ :param left_image: left numpy image
131
+ :param right_image: right numpy image
132
+ :raises: ValueError, TypeError
133
+ """
134
+ if not isinstance(left_image, np.ndarray):
135
+ raise TypeError('left image is not an np.ndarray')
136
+ if not isinstance(right_image, np.ndarray):
137
+ raise TypeError('right image is not an np.ndarray')
138
+ if left_image.shape != right_image.shape:
139
+ raise ValueError('left and right images differ in shape')
140
+ if left_image.shape[0] % 2 != 0:
141
+ raise ValueError('left image does not have an even number of rows')
142
+ if right_image.shape[0] % 2 != 0:
143
+ raise ValueError('right image does not have an even number of rows')
144
+
145
+ self.left_widget.set_video_image(left_image)
146
+ self.right_widget.set_video_image(right_image)
147
+
148
+ def render(self):
149
+ """
150
+ Calls Render on all 3 contained vtk_overlay_windows.
151
+ """
152
+ self.left_widget.Render()
153
+ self.left_widget.update()
154
+ self.right_widget.Render()
155
+ self.right_widget.update()
156
+
157
+ left = self.left_widget.convert_scene_to_numpy_array()
158
+ right = self.right_widget.convert_scene_to_numpy_array()
159
+
160
+ left_rescaled = cv2.resize(left, (0, 0), fx=1.0, fy=0.5)
161
+ right_rescaled = cv2.resize(right, (0, 0), fx=1.0, fy=0.5)
162
+
163
+ if self.left_is_top:
164
+ interlaced = i.interlace_to_new(left_rescaled,
165
+ right_rescaled)
166
+ else:
167
+ interlaced = i.interlace_to_new(right_rescaled,
168
+ left_rescaled)
169
+
170
+ self.interlaced_widget.set_video_image(interlaced)
171
+ self.interlaced_widget.Render()
172
+ self.interlaced_widget.update()
173
+
174
+ def save_scene_to_file(self, file_name):
175
+ """
176
+ Writes the interlaced widget contents to file.
177
+
178
+ :param file_name: file name compatible with cv2.imwrite()
179
+ """
180
+ self.render()
181
+ self.interlaced_widget.save_scene_to_file(file_name)
@@ -73,6 +73,9 @@ class VTKOverlayWindow(bcw.VTKBaseCalibratedWindow):
73
73
  :param layer_2_video_mask: Mask image for layer 2.
74
74
  :param layer_1_interactive: True if you want the VTK interactor to pickup events in this layer.
75
75
  :param layer_3_interactive: True if you want the VTK interactor to pickup events in this layer.
76
+ :param aspect_ratio: to adjust for slight differences in pixel size, expressed as x/y.
77
+ :param xscale: horizontal scale factor, for horizontal stacking
78
+ :param yscale: vertical scale factor, for vertical stacking
76
79
  layer_1_interactive and layer_3_interactive are mutually exclusive.
77
80
  """
78
81
  # pylint: disable=too-many-positional-arguments
@@ -90,7 +93,10 @@ class VTKOverlayWindow(bcw.VTKBaseCalibratedWindow):
90
93
  layer_2_video_mask=None, # For masking in Layer 3
91
94
  use_depth_peeling=True,
92
95
  layer_1_interactive=True, # For backwards compatibility, prior to 3rd Feb 2024.
93
- layer_3_interactive=False # For backwards compatibility, prior to 3rd Feb 2024.
96
+ layer_3_interactive=False, # For backwards compatibility, prior to 3rd Feb 2024.
97
+ aspect_ratio=1,
98
+ xscale=1,
99
+ yscale=1
94
100
  ):
95
101
  """
96
102
  Constructs a new VTKOverlayWindow.
@@ -99,7 +105,10 @@ class VTKOverlayWindow(bcw.VTKBaseCalibratedWindow):
99
105
  camera_matrix,
100
106
  clipping_range,
101
107
  opencv_style,
102
- reset_camera
108
+ reset_camera,
109
+ aspect_ratio=aspect_ratio,
110
+ xscale=xscale,
111
+ yscale=yscale
103
112
  )
104
113
  LOGGER.info("Creating VTKOverlayWindow")
105
114
 
@@ -0,0 +1,113 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """
4
+ Module to provide an stacked stereo window, designed for
5
+ driving things like a 3D monitor that accepts left=top, right=bottom
6
+ """
7
+
8
+ # pylint: disable=c-extension-no-member, no-name-in-module, too-many-instance-attributes
9
+ import cv2
10
+ import numpy as np
11
+ from PySide6 import QtWidgets
12
+ import sksurgeryvtk.widgets.vtk_base_stereo_window as bw
13
+
14
+
15
+ class VTKStackedStereoWindow(bw.VTKBaseStereoWindow):
16
+ """
17
+ Class to contain a pair of VTKOverlayWindows, left, right, that are
18
+ stacked on top of each other. Left=top. Right=bottom.
19
+
20
+ :param init_widget: If True we will call self.Initialize and self.Start
21
+ as part of the init function. Set to false if you're on Linux.
22
+ : param left_is_top: If True left is top. Else left is bottom.
23
+ """
24
+ # pylint: disable=too-many-positional-arguments
25
+ def __init__(self,
26
+ offscreen=False,
27
+ left_camera_matrix=None,
28
+ right_camera_matrix=None,
29
+ clipping_range=(1, 10000),
30
+ init_widget=True,
31
+ left_is_top=True,
32
+ aspect_ratio=1
33
+ ):
34
+
35
+ # Superclass creates left/right viewer.
36
+ super().__init__(offscreen=offscreen,
37
+ left_camera_matrix=left_camera_matrix,
38
+ right_camera_matrix=right_camera_matrix,
39
+ clipping_range=clipping_range,
40
+ init_widget=init_widget,
41
+ left_is_top=left_is_top,
42
+ aspect_ratio=aspect_ratio,
43
+ xscale=1,
44
+ yscale=2
45
+ )
46
+
47
+ self.layout = QtWidgets.QVBoxLayout(self)
48
+ self.layout.setContentsMargins(0, 0, 0, 0)
49
+ self.layout.setSpacing(0)
50
+ if self.left_is_top:
51
+ self.layout.addWidget(self.left_widget)
52
+ self.layout.addWidget(self.right_widget)
53
+ else:
54
+ self.layout.addWidget(self.right_widget)
55
+ self.layout.addWidget(self.left_widget)
56
+
57
+ def resizeEvent(self, ev):
58
+ """
59
+ Ensure that the interlaced image is recomputed.
60
+ """
61
+ self.left_widget.resizeEvent(ev)
62
+ self.left_widget.Render()
63
+ self.left_widget.update()
64
+ self.right_widget.resizeEvent(ev)
65
+ self.right_widget.Render()
66
+ self.right_widget.update()
67
+ super().resizeEvent(ev)
68
+
69
+ def set_video_images(self, left_image, right_image):
70
+ """
71
+ Sets both left and right video images. Images
72
+ must be the same shape, and have an even number of rows.
73
+
74
+ :param left_image: left numpy image
75
+ :param right_image: right numpy image
76
+ :raises: ValueError, TypeError
77
+ """
78
+ if not isinstance(left_image, np.ndarray):
79
+ raise TypeError('left image is not an np.ndarray')
80
+ if not isinstance(right_image, np.ndarray):
81
+ raise TypeError('right image is not an np.ndarray')
82
+ if left_image.shape != right_image.shape:
83
+ raise ValueError('left and right images differ in shape')
84
+
85
+ left_rescaled = cv2.resize(left_image, (0, 0), fx=1.0, fy=0.5)
86
+ right_rescaled = cv2.resize(right_image, (0, 0), fx=1.0, fy=0.5)
87
+
88
+ self.left_widget.set_video_image(left_rescaled)
89
+ self.right_widget.set_video_image(right_rescaled)
90
+
91
+ def render(self):
92
+ """
93
+ Simply triggers rendering on both widgets.
94
+ """
95
+ self.left_widget.Render()
96
+ self.left_widget.update()
97
+ self.right_widget.Render()
98
+ self.right_widget.update()
99
+
100
+ def save_scene_to_file(self, file_name):
101
+ """
102
+ Writes the interlaced widget contents to file.
103
+
104
+ :param file_name: file name compatible with cv2.imwrite()
105
+ """
106
+ self.render()
107
+ left_image = self.left_widget.convert_scene_to_numpy_array()
108
+ right_image = self.right_widget.convert_scene_to_numpy_array()
109
+ if self.left_is_top:
110
+ stacked = np.vstack((left_image, right_image))
111
+ else:
112
+ stacked = np.vstack((right_image, left_image))
113
+ cv2.imwrite(file_name, stacked)
@@ -70,6 +70,7 @@ class VTKZBufferWindow(bcw.VTKBaseCalibratedWindow):
70
70
  reset_camera=True,
71
71
  init_widget=True,
72
72
  use_depth_peeling=True,
73
+ aspect_ratio=1
73
74
  ):
74
75
  """
75
76
  Constructs a new VTKZBufferWindow.
@@ -78,7 +79,8 @@ class VTKZBufferWindow(bcw.VTKBaseCalibratedWindow):
78
79
  camera_matrix,
79
80
  clipping_range,
80
81
  opencv_style,
81
- reset_camera
82
+ reset_camera,
83
+ aspect_ratio=aspect_ratio
82
84
  )
83
85
  LOGGER.info("Creating VTKZBufferWindow")
84
86
 
@@ -31,7 +31,7 @@ def test_requirements_vs_setup():
31
31
  setup_reqs = []
32
32
 
33
33
  for line in searchlines[install_line + 1: end_line]:
34
- req = line.replace(',', '').replace("'", "")
34
+ req = line.replace("',", "'").replace("'", "")
35
35
  req = req.replace(' ', '').replace('\n', '')
36
36
  setup_reqs.append(req)
37
37