# How two stream 2 different threeD views (via Web Server)?

**URL:** <https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119>\
**Category:** Support\
**Tags:** python\
**Created:** [July 1, 2024, 12:22pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119 "2024-07-01T12:22:00Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [July 1, 2024, 12:22pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/1 "2024-07-01T12:22:00Z")

</div>

Hi there!

I want to stream 2 different 3D views over a web server for a 3D (stereo) augmented reality application in Python. It already works for one 3D view.

How can I do this? Is there a better/faster way than using a web server?

thank you!

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [July 1, 2024, 4:35pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/2 "2024-07-01T16:35:05Z")

</div>

Right now the [threeD view endpoint](https://slicer.readthedocs.io/en/latest/user_guide/modules/webserver.html#get-threed) doesn’t take a view id parameter like the [slice view one does](https://slicer.readthedocs.io/en/latest/user_guide/modules/webserver.html#get-slice), but this would be easy to extend if you wanted to.

In general though the web server isn’t going to be the highest performance option so you may want to explore something like the [hololens remoting system](https://link.springer.com/article/10.1007/s11548-023-02977-0).

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [July 3, 2024, 7:36am UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/3 "2024-07-03T07:36:11Z")

</div>

Thank You!

For a first proof of concept the plan was to go with the web server.

The docu says: “Get screenshot of the first 3D view **after applying parameters**.” Does "after applaying parameters mean “the active 3D view”?

if yes, i changed the active threeD view and verified the change with putting the following code in the console:

```python
layoutManager = slicer.app.layoutManager()
targetThreeDViewIndex = 2
targetThreeDWidget = layoutManager.threeDWidget(targetThreeDViewIndex)
targetThreeDViewNode = targetThreeDWidget.mrmlViewNode()
slicer.app.applicationLogic().GetSelectionNode().SetActiveViewID(targetThreeDViewNode.GetID())

slicer.app.applicationLogic().GetSelectionNode().GetActiveViewID()

```

→ But the webserver delivers always the same threeD-view.

Why doesn’t it work?

---

<div class="post-metadata">

**Author:** ![jcfr](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/jcfr/32/17825_2.png) [@jcfr](https://discourse.slicer.org/u/jcfr)\
**Post date:** [July 3, 2024, 9:13pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/4 "2024-07-03T21:13:42Z")

</div>

> [@thymallus](#):
>
> Why doesn’t it work?

I think @pieper comment above explains what you are observing:

> [@pieper](#):
>
> Right now the [threeD view endpoint](https://slicer.readthedocs.io/en/latest/user_guide/modules/webserver.html#get-threed) doesn’t take a view id parameter like the [slice view one does](https://slicer.readthedocs.io/en/latest/user_guide/modules/webserver.html#get-slice), but this would be easy to extend if you wanted to.

To better understand, consider reviewing the implementation of the corresponding endpoint in [Modules/Scripted/WebServer/WebServerLib/SlicerRequestHandler.py#L1009-L1077](https://github.com/Slicer/Slicer/blob/4c1546f620424a279d6d5daa973292ee2ae32d4f/Modules/Scripted/WebServer/WebServerLib/SlicerRequestHandler.py#L1009-L1077)

corresponding to the endpoint document at [https://slicer.readthedocs.io/en/latest/user\_guide/modules/webserver.html#get-threed](https://slicer.readthedocs.io/en/latest/user_guide/modules/webserver.html#get-threed)

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [July 3, 2024, 11:05pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/5 "2024-07-03T23:05:23Z")

</div>

Yes, @jcfr is correct.

Interestingly, I see now that the `view` parameter is being parsed:

> <https://github.com/Slicer/Slicer/blob/4c1546f620424a279d6d5daa973292ee2ae32d4f/Modules/Scripted/WebServer/WebServerLib/SlicerRequestHandler.py#L1017-L1020>

But then later it’s not being used (`0` is hard coded):

> <https://github.com/Slicer/Slicer/blob/4c1546f620424a279d6d5daa973292ee2ae32d4f/Modules/Scripted/WebServer/WebServerLib/SlicerRequestHandler.py#L1055>

So this would be an easy fix if someone wants to give it a try.

> Does "after applaying parameters mean “the active 3D view”?

This means that any view options specified in the url parameters, like changing the view direction are applied before the screenshot is returned.

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 6, 2024, 12:17pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/6 "2024-08-06T12:17:16Z")

</div>

Hi!

I have started to explore the hololens remoting system. In my opinion, it is too extensive for my requirements. I also do not have a Hololens and therefore it’s cumbersome for me to understand the code.

In principle, I just need to broadcast 2 different threeD views from 3D Slicer to an external python programme. I’ve sketched my setup in the following image:

 ![grafik](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/a/7ac3fb421857181fc60e7ae82880af23dddabdec.jpeg)

Do you have any tips on how to do this and get better performance than with the WebServer?

Thank you!

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 6, 2024, 7:10pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/7 "2024-08-06T19:10:52Z")

</div>

I would suggest you could start with the WebServer implementation and then optimize if it’s not fast enough. On a local machine or local network it can provide images pretty quickly, although it depends on how big they are, etc. Usually surgical microscope’s don’t move very quickly so it could be okay and is probably the easiest just to make the small fix suggested above. If you do need to optimize then a dedicated connection you should first determine exactly what the bottlenecks are and what performance you actually need.

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 7, 2024, 7:14am UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/8 "2024-08-07T07:14:40Z")

</div>

Ok, thank you for your input!

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 7, 2024, 1:38pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/9 "2024-08-07T13:38:39Z")

</div>

Hi,

The following code shows how the ThreeD View image is loaded into the external Python programme:

```auto
import cv2
import numpy as np
import urllib

def load_from_3DSlicer(url):
    # download the image, convert it to NumPy array, and decode it to OpenCV-format
    response = urllib.request.urlopen(url)
    image = np.asarray(bytearray(response.read()), dtype="uint8")
    image = cv2.imdecode(image, cv2.IMREAD_COLOR)
    return image

def main():
 
    img_url = "http://localhost:2016/slicer/threeD"  
    threeDslicer_img = load_from_3DSlicer(img_url)
    cv2.imshow('3D Slicer image 3D', threeDslicer_img)
    cv2.waitKey()
    
if __name__ == ' __main__':
    main()

```

Is there a much faster/better approach in general? If yes, could you pls give me a hint?

The whole function “load\_from\_3DSlicer” takes about ~2.2s on the currently used computer, whereby 2.12s are taken from the line “response = urllib.request.urlopen(url)”.

thank you!

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 8, 2024, 9:29am UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/10 "2024-08-08T09:29:20Z")

</div>

Update:

I tried it now with asynchronous requests, e.g:

```auto
async def fetch_image(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            content = await response.read()
            return content 

url = "http://localhost:2016/slicer/threeD"

loop = asyncio.get_event_loop()
image = loop.run_until_complete(fetch_image(url))
image = np.asarray(bytearray(image), dtype="uint8")
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
cv2.imshow('3D Slicer img Slice', image)
cv2.waitKey()

```

Works much faster, takes ~1/4 of the computation time.

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 8, 2024, 1:04pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/11 "2024-08-08T13:04:32Z")

</div>

Now I’d like to switch between the threeD views (left and right camera) by passing a parameter to the function “threeD” in the SlicerRequestHandler.py of the WebServer module.

How is it possible to execute the “threeD” function via the 3D slicer Python console or via an external Python console?

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 8, 2024, 1:13pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/12 "2024-08-08T13:13:01Z")

</div>

> [@thymallus](#):
>
> Now I’d like to switch between the threeD views (left and right camera) by passing a parameter to the function “threeD” in the SlicerRequestHandler.py of the WebServer module.

You just need to implement the fix described above (change the hard-coded 0 to use the view id).

> [@thymallus](#):
>
> How is it possible to execute the “threeD” function via the 3D slicer Python console or via an external Python console?

Not sure what you are asking here.

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [August 8, 2024, 1:28pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/13 "2024-08-08T13:28:38Z")

</div>

Yes, but I have to pass the information about which threeD view I want to receive into the threeD function. ?

For example:

threeD(self, request, view\_idx = 0) → left virtual cam  
threeD(self, request, view\_idx = 1) → right virtual cam

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [August 8, 2024, 1:42pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/14 "2024-08-08T13:42:41Z")

</div>

You would need to encode the view id as a url parameter.

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [March 6, 2025, 9:47am UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/15 "2025-03-06T09:47:08Z")

</div>

Hi,

how can i make sure that the view\_indices correspond to the “3d-view-number” in 3D Slicer?

E.g. in the following image 3D view 1 is the left cam, 3D view 3 is the right cam:

 ![grafik](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/8/78af79610c9fa0073b3db092e379aa4c6916c2a4.jpeg)

I’ve adapted the SlicerRequestHandler to get left and right images in an external python programme:

```
        def threeD(self, request, view_idx):
    """
    Handle requests with path: /threeD
    Return a png for a threeD view.
    """
    #print('HERE in threeD of SlicerRequestHandler')
    #print('view_idx = ' + str(view_idx))
    #print('request = ' + str(request))
    if request.decode('utf-8') == '/threeDL':
        view_idx = 0
    if request.decode('utf-8') == '/threeDR':
        view_idx = 2
    print('view_idx AFTER IF in threeDfunction = ' + str(view_idx))

```

But the images i receive are not view 1 and view 3 but view 1 and view 2:

 ![grafik](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/5/e/5e54f3ef0817fbe734e457d55840bf7373322e9a.jpeg)

It seems to me that Slicer assigns view indices to the 3D views in a seemingly random manner—or at least, I have yet to understand the underlying logic. Additionally, the view indices of the 3D views keep changing, if Slicer is restarted.

How can i solve this problem?

Thank you and best regards,  
Stefan

---

<div class="post-metadata">

**Author:** ![thymallus](https://avatars.discourse-cdn.com/v4/letter/t/e47c2d/32.png) [@thymallus](https://discourse.slicer.org/u/thymallus)\
**Post date:** [March 6, 2025, 1:01pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/16 "2025-03-06T13:01:05Z")

</div>

…and one more question:

I would like to change the view angle of the viewpoint module from an external Python programme, depending on the zoom of the microscope… Is this possible? if so, how?

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [March 6, 2025, 3:28pm UTC](https://discourse.slicer.org/t/how-two-stream-2-different-threed-views-via-web-server/37119/17 "2025-03-06T15:28:34Z")

</div>

The threeDView index probably changes when the layout changes. You could search for the `viewLabel` instead, like `lm.threeDWidget(0).viewLabel`.

You can change the view angle [like this](https://github.com/Slicer/Slicer/blob/49a251067d9b645bf35c445a7be6cf4a9cf039e9/Modules/Scripted/Endoscopy/Endoscopy.py#L338-L341).
