Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python interface #129

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7b80fae
refactor(viewer): move typescript package into its own directory
thewtex Mar 11, 2024
c418138
feat: Add itkviewer Python interface
thewtex Mar 11, 2024
7b953de
feat(itkviewer): add Viewport, MultiscaleImage
thewtex Mar 12, 2024
f7362a5
feat(linkml): initial schema definition
thewtex Mar 13, 2024
cdc2f6b
feat: itkviewer-clara-viz package config
thewtex Mar 14, 2024
c66f7c6
feat: add itkviewer-textual package configuration
thewtex Mar 14, 2024
5ad3a88
docs: add LinkML model README
thewtex Mar 15, 2024
839d403
feat: scripts to generate model Python and docs
thewtex Mar 15, 2024
aa6ecc0
feat(model): mark actors as abstract
thewtex Mar 15, 2024
507e975
docs(model): add description, license
thewtex Mar 15, 2024
557ff79
feat(model): add viewport to Renderer
thewtex Mar 15, 2024
c9b9d7e
feat(python): use the generated pydantic model in the package
thewtex Mar 15, 2024
29fbd8a
feat(model): add RendererEvent, RendererEventType
thewtex Mar 15, 2024
bdaad87
feat(model): add UnknownEventAction
thewtex Mar 15, 2024
c692d86
feat(clara): instantiate internal renderer
thewtex Mar 15, 2024
6845862
feat(model): add version
thewtex Mar 15, 2024
dd602d8
feat: initial ViewerEvent SetImageEvent
thewtex Mar 16, 2024
7da7f14
feat(python): ViewerMachine
thewtex Mar 17, 2024
4ab7393
feat: start DataManagerMachine
thewtex Mar 17, 2024
c258a44
feat(model): define ImageData
thewtex Mar 18, 2024
7a0d3c7
feat(model): add StoreModelType
thewtex Mar 18, 2024
1c92eb9
feat(python): instantiate ViewerMachine, DataManagerMachine
thewtex Mar 19, 2024
9ee3eb3
WIP: feat(DataManager): serialization and deserialization
thewtex Mar 20, 2024
05407e5
feat(model): use designates_type with Event
thewtex Mar 20, 2024
591f19d
feat(python): initial pygfx package
thewtex Mar 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dependencies:
- python-dotenv
- -e ./packages/agave-renderer
- -e ./packages/remote-image
- itkwasm-image-io
8 changes: 8 additions & 0 deletions model/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# itk-viewer model

A [LinkML] model of itk-viewer's architecture.

The viewer is built on the [Actor Model]. This model defines the Actors and their relationships. It also defines the Events, which are the messages sent to Actors. It defines all the Events an Actor can receive. The data model of the Events is also used to generate the corresponding TypeScript interfaces and Python pydantic dataclasses.

.. LinkML: https://linkml.io/
.. Actor Model: https://en.wikipedia.org/wiki/Actor_model
8 changes: 8 additions & 0 deletions model/gen-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

# Generate documentation for the model

script_dir="`cd $(dirname $0); pwd`"
cd $script_dir

gen-markdown -d ../docs/model ./itk-viewer.yml
8 changes: 8 additions & 0 deletions model/gen-python.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

# Generate python code for the model

script_dir="`cd $(dirname $0); pwd`"
cd $script_dir

gen-pydantic ./itk-viewer.yml --pydantic-version 2 > ../packages/viewer/python/itkviewer/model.py
258 changes: 258 additions & 0 deletions model/itk-viewer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
id: https://w3id.org/itk/viewer
name: itk-viewer
version: 0.0.1
description: >-
The itk-viewer model defines the classes and relationships for a viewer that allows users to view and interact with multi-dimensional images, geometry, and point sets.
license: https://creativecommons.org/publicdomain/zero/1.0/
prefixes:
itk: https://w3id.org/itk/
wasm: https://w3id.org/itk/wasm
viewer: https://w3id.org/itk/viewer
linkml: https://w3id.org/linkml/
imports:
- linkml:types
default_range: string
default_prefix: viewer

classes:
Actor:
abstract: true
description: >-
In the Actor Model mathematical of computation, an actor is a computational entity that,
in response to a message it receives, can concurrently:

- send a finite number of messages to other actors;
- create a finite number of new actors;
- designate the behavior to be used for the next message it receives.

Supported messages are defined in the Event classes. The valid Events' for an Actor are defined
defined by the `receives` relationship. To send an Event to an Actor, use the `send` method.

Actors are typically implement as finite state machines.
class_uri: viewer:Actor
slots:
- unknownEventAction

Viewer:
abstract: true
is_a: Actor
tree_root: true
description: >-
A viewer is an interface that allows users to view and interact with
multi-dimensional images, geometry, and point sets.
class_uri: viewer:Viewer
attributes:
title:
description: >-
The title of the viewer.
range: string
ifabsent: string("ITK Viewer")
dataManager:
description: >-
The data manager for the viewer.
range: DataManager

Viewport:
abstract: true
is_a: Actor
description: >-
A viewport is a rectangular region of a viewer that displays a rendering of the scene.
class_uri: viewer:Viewport
attributes:
width:
description: >-
The width of the viewport in pixels.
range: integer
required: true
ifabsent: int(640)
height:
description: >-
The height of the viewport in pixels.
range: integer
required: true
ifabsent: int(480)

Image:
description: >-
An itk-wasm Image to be displayed in the viewer.
class_uri: wasm:Image

StoreModel:
abstract: true
description: >-
Parameters of a Zarr store following the data model implied by Zarr-Python.
attributes:
type:
description: >-
The type of the Zarr store model.
designates_type: true
range: string
required: true

DirectoryStore:
is_a: StoreModel
description: >-
A Zarr store that is backed by a directory on the file system.
attributes:
path:
description: >-
The path to the directory on the file system that contains the Zarr store.
range: string
required: true

FSStore:
is_a: StoreModel
description: >-
A Zarr store that can be wrapped an fsspec.FSMap in Python to give access to arbitrary filesystems
attributes:
url:
description: >-
Protocol and path, like “s3://bucket/root.zarr” or "https://example.com/image.ome.zarr".
range: string
required: true

ImageData:
description: >-
Image data displayed in the viewer.
class_uri: viewer:ImageData
attributes:
imageJson:
description: >-
The image data in JSON format. An ITK-Wasm Image with the pixel data zstd compressed and base64-encoded.
range: string
store:
description: >-
The OME-Zarr store model for the image data.
range: StoreModel

MultiscaleImage:
abstract: true
is_a: Actor
description: >-
A multiscale image is a multi-dimensional image, based on the OME-Zarr data model, often preprocessed,
that supports efficient rendering at multiple resolutions.
class_uri: viewer:MultiscaleImage

DataManager:
abstract: true
is_a: Actor
description: >-
A data manager is an actor that manages the loading and caching of data for rendering.
class_uri: viewer:DataManager
slots:
- images

Renderer:
abstract: true
is_a: Actor
description: >-
A renderer is an actor that renders a scene to an in-memory RGB image for display in a viewport.
class_uri: viewer:Renderer
attributes:
width:
description: >-
The width of the canvas in pixels.
range: integer
required: true
ifabsent: int(640)
height:
description: >-
The height of the canvas in pixels.
range: integer
required: true
ifabsent: int(480)
slots:
- viewport


Event:
abstract: true
description: >-
An event is a message that can be sent to an actor. The actor can respond to the event
by changing its state, sending messages to other actors, or creating new actors.
class_uri: viewer:Event
attributes:
type:
designates_type: true
description: >-
The type of the event.
range: string
required: true

ViewerEvent:
abstract: true
is_a: Event
description: >-
A ViewerEvent is an Event that can be sent to a Viewer.
class_uri: viewer:ViewerEvent

SetImageEvent:
is_a: ViewerEvent
description: >-
A SetImageEvent is an Event that sets an image to be displayed in a viewer.
class_uri: viewer:SetImageEvent
slots:
- image
attributes:
name:
description: >-
The name of the image to be displayed in the viewer.
range: string

RendererEvent:
abstract: true
is_a: Event
description: >-
A RendererEvent is an Event supported by a Renderer.
class_uri: viewer:RendererEvent

RenderEvent:
is_a: RendererEvent
description: >-
A render event is a message that instructs a renderer to render a scene to an in-memory RGB image.
class_uri: viewer:RenderEvent

slots:
viewport:
description: >-
The viewport that displays the rendered RGB image.
domain: Renderer
range: Viewport
required: true

unknownEventAction:
description: >-
The action to take when an unknown event is received.
domain: Actor
range: UnknownEventAction

image:
description: >-
The image to be displayed in the viewer.
domain: SetImageEvent
range: Image
required: true

images:
description: >-
The images displayed by the viewer.
domain: DataManager
range: ImageData
multivalued: true
required: true

enums:
UnknownEventAction:
description: >-
The types of actions that can be taken when an unknown event is received.
enum_uri: viewer:UnknownEventAction
permissible_values:
Ignore:
description: >-
Ignore the event.
Warn:
description: >-
Log a warning and ignore the event.
Error:
description: >-
Throw an error.
1 change: 1 addition & 0 deletions model/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
linkml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
#
# SPDX-License-Identifier: Apache-2.0

from .renderer import Renderer
from .renderer import AgaveRenderer
23 changes: 9 additions & 14 deletions packages/agave-renderer/itk_viewer_agave_renderer/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import fractions
from av import VideoFrame
from aiortc import MediaStreamTrack
from itkviewer import Viewport, Renderer, UnknownEventAction

# Should match constant in connected clients.
# The WebRTC service id is different from this
Expand All @@ -40,7 +41,7 @@ async def recv(self):

frame = VideoFrame.from_image(img)

frame.pts = self.count
frame.pts = self.count
self.count+=1
frame.time_base = fractions.Fraction(1, 1000)
return frame
Expand All @@ -64,22 +65,16 @@ def memory_redraw(self):
return img


# how to handle unknown events
class UnknownEventAction(Enum):
ERROR = auto()
WARNING = auto()
IGNORE = auto()


class Renderer:
class AgaveRenderer(Renderer):
def __init__(
self, width=500, height=400, unknown_event_action=UnknownEventAction.WARNING
self, viewport: Viewport, width: int=500, height: int=400, unknown_event_action: UnknownEventAction=UnknownEventAction.Warning
):
self.width = width
self.height = height
self.unknown_event_action = unknown_event_action
self.render_time = .1
self.agave = None
super().__init__(viewport, width, height, unknown_event_action)

async def setup(self):
# Note: the agave websocket server needs to be running
Expand Down Expand Up @@ -167,11 +162,11 @@ async def update_renderer(self, events):

def handle_unknown_event(self, event_type):
match self.unknown_event_action:
case UnknownEventAction.ERROR:
raise Exception(f"Unknown event type: {event_type}")
case UnknownEventAction.WARNING:
case UnknownEventAction.Error:
raise ValueError(f"Unknown event type: {event_type}")
case UnknownEventAction.Warning:
print(f"Unknown event type: {event_type}", flush=True)
case UnknownEventAction.IGNORE:
case UnknownEventAction.Ignore:
pass


Expand Down
Loading
Loading