-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #61 from thewtex/agave-renderer
Agave renderer
- Loading branch information
Showing
7 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# ITK Viewer Agave Renderer | ||
|
||
[![PyPI - Version](https://img.shields.io/pypi/v/itk-viewer-agave-renderer.svg)](https://pypi.org/project/itk-viewer-agave-renderer) | ||
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/itk-viewer-agave-renderer.svg)](https://pypi.org/project/itk-viewer-agave-renderer) | ||
|
||
--- | ||
|
||
**Table of Contents** | ||
|
||
- [Installation](#installation) | ||
- [License](#license) | ||
|
||
## Installation | ||
|
||
```console | ||
pip install itk-viewer-agave-renderer | ||
``` | ||
|
||
## License | ||
|
||
`itk-viewer-agave-renderer` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license. |
4 changes: 4 additions & 0 deletions
4
packages/agave-renderer/itk_viewer_agave_renderer/__about__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# SPDX-FileCopyrightText: 2023-present NumFOCUS | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
__version__ = "0.0.1" |
5 changes: 5 additions & 0 deletions
5
packages/agave-renderer/itk_viewer_agave_renderer/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# SPDX-FileCopyrightText: 2023-present NumFOCUS | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from .renderer import Renderer |
116 changes: 116 additions & 0 deletions
116
packages/agave-renderer/itk_viewer_agave_renderer/renderer.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# SPDX-FileCopyrightText: 2023-present NumFOCUS | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import time | ||
from enum import Enum, auto | ||
import functools | ||
|
||
import agave_pyclient as agave | ||
from imjoy_rpc.hypha import connect_to_server | ||
from itkwasm_htj2k import encode | ||
from itkwasm import Image, ImageType, PixelTypes, IntTypes | ||
import numpy as np | ||
from PIL import Image as PILImage | ||
|
||
class AgaveRendererMemoryRedraw(agave.AgaveRenderer): | ||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
def memory_redraw(self): | ||
self.cb.add_command("REDRAW") | ||
buf = self.cb.make_buffer() | ||
# TODO ENSURE CONNECTED | ||
self.ws.send(buf, True) | ||
# and then WAIT for render to be completed | ||
binarydata = self.ws.wait_for_image() | ||
# ready for next frame | ||
self.cb = agave.commandbuffer.CommandBuffer() | ||
img = PILImage.open(binarydata) | ||
rgba = img.convert('RGBA').tobytes() | ||
return rgba | ||
|
||
# how to handle unknown events | ||
class UnknownEventAction(Enum): | ||
ERROR = auto() | ||
WARNING = auto() | ||
IGNORE = auto() | ||
|
||
class Renderer(): | ||
def __init__(self, width=500, height=400, unknown_event_action=UnknownEventAction.WARNING): | ||
self.width = width | ||
self.height = height | ||
self.unknown_event_action = unknown_event_action | ||
|
||
async def setup(self): | ||
# Note: the agave websocket server needs to be running | ||
self.agave = AgaveRendererMemoryRedraw() | ||
self.agave.set_resolution(self.width, self.height) | ||
|
||
async def render(self): | ||
r = self.agave | ||
start_time = time.time() | ||
rgba = r.memory_redraw() | ||
image_type = ImageType(dimension=2, componentType=IntTypes.UInt8, pixelType=PixelTypes.RGBA, components=4) | ||
image = Image(image_type) | ||
image.size = [self.width, self.height] | ||
image.data = np.frombuffer(rgba, dtype=np.uint8) | ||
# lossless | ||
# rgba_encoded = encode(image) | ||
rgba_encoded = encode(image, not_reversible=True, quantization_step=0.02) | ||
elapsed = time.time() - start_time | ||
return { "frame": rgba_encoded, "renderTime": elapsed } | ||
|
||
def update_renderer(self, events): | ||
r = self.agave | ||
|
||
for [event_type, payload] in events: | ||
match event_type: | ||
case 'density': | ||
r.density(payload) | ||
case 'cameraPose': | ||
eye = payload['eye'] | ||
r.eye(eye[0], eye[1], eye[2]) | ||
up = payload['up'] | ||
r.up(up[0], up[1], up[2]) | ||
target = payload['target'] | ||
r.target(target[0], target[1], target[2]) | ||
case 'renderIterations': | ||
r.render_iterations(payload) | ||
case _: | ||
self.handle_unknown_event(event_type) | ||
|
||
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: | ||
print(f"Unknown event type: {event_type}", flush=True) | ||
case UnknownEventAction.IGNORE: | ||
pass | ||
|
||
async def connect(self, hypha_server_url, load_image_into_agave_fn, visibility="public", identifier="agave-renderer"): | ||
server = await connect_to_server( | ||
{ | ||
"name": "agave-renderer-client", | ||
"server_url": hypha_server_url | ||
} | ||
) | ||
|
||
await server.register_service({ | ||
"name": "Agave Renderer", | ||
"id": identifier, | ||
"config": { | ||
"visibility": visibility, | ||
"require_context": False, | ||
"run_in_executor": False, | ||
}, | ||
|
||
"setup": self.setup, | ||
|
||
"loadImage": functools.partial(load_image_into_agave_fn, self), | ||
|
||
"render": self.render, | ||
"updateRenderer": self.update_renderer, | ||
}) | ||
print("Renderer is ready to receive request!", server.config, flush=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project] | ||
name = "itk-viewer-agave-renderer" | ||
dynamic = ["version"] | ||
description = '' | ||
readme = "README.md" | ||
requires-python = ">=3.7" | ||
license = "Apache-2.0" | ||
keywords = [] | ||
authors = [ | ||
{ name = "Matt McCormick", email = "matt.mccormick@kitware.com" }, | ||
] | ||
classifiers = [ | ||
"Development Status :: 4 - Beta", | ||
"Programming Language :: Python", | ||
"Programming Language :: Python :: 3.7", | ||
"Programming Language :: Python :: 3.8", | ||
"Programming Language :: Python :: 3.9", | ||
"Programming Language :: Python :: 3.10", | ||
"Programming Language :: Python :: 3.11", | ||
"Programming Language :: Python :: Implementation :: CPython", | ||
"Programming Language :: Python :: Implementation :: PyPy", | ||
] | ||
dependencies = [ | ||
"agave-pyclient", | ||
"hypha", | ||
"itkwasm-htj2k", | ||
"numpy", | ||
"pillow", | ||
] | ||
|
||
[project.urls] | ||
Documentation = "https://github.com/InsightSoftwareConsortium/itk-viewer/tree/main/packages/agave-renderer#readme" | ||
Issues = "https://github.com/InsightSoftwareConsortium/itk-viewer/issues" | ||
Source = "https://github.com/InsightSoftwareConsortium/itk-viewer" | ||
|
||
[tool.hatch.version] | ||
path = "itk_viewer_agave_renderer/__about__.py" | ||
|
||
[tool.hatch.envs.default] | ||
dependencies = [ | ||
"coverage[toml]>=6.5", | ||
"pytest", | ||
] | ||
[tool.hatch.envs.default.scripts] | ||
test = "pytest {args:tests}" | ||
test-cov = "coverage run -m pytest {args:tests}" | ||
cov-report = [ | ||
"- coverage combine", | ||
"coverage report", | ||
] | ||
cov = [ | ||
"test-cov", | ||
"cov-report", | ||
] | ||
|
||
[[tool.hatch.envs.all.matrix]] | ||
python = ["3.7", "3.8", "3.9", "3.10", "3.11"] | ||
|
||
[tool.hatch.envs.lint] | ||
detached = true | ||
dependencies = [ | ||
"black>=23.1.0", | ||
"mypy>=1.0.0", | ||
"ruff>=0.0.243", | ||
] | ||
[tool.hatch.envs.lint.scripts] | ||
typing = "mypy --install-types --non-interactive {args:itk_viewer_agave_renderer tests}" | ||
style = [ | ||
"ruff {args:.}", | ||
"black --check --diff {args:.}", | ||
] | ||
fmt = [ | ||
"black {args:.}", | ||
"ruff --fix {args:.}", | ||
"style", | ||
] | ||
all = [ | ||
"style", | ||
"typing", | ||
] | ||
|
||
[tool.black] | ||
target-version = ["py37"] | ||
line-length = 120 | ||
skip-string-normalization = true | ||
|
||
[tool.ruff] | ||
target-version = "py37" | ||
line-length = 120 | ||
select = [ | ||
"A", | ||
"ARG", | ||
"B", | ||
"C", | ||
"DTZ", | ||
"E", | ||
"EM", | ||
"F", | ||
"FBT", | ||
"I", | ||
"ICN", | ||
"ISC", | ||
"N", | ||
"PLC", | ||
"PLE", | ||
"PLR", | ||
"PLW", | ||
"Q", | ||
"RUF", | ||
"S", | ||
"T", | ||
"TID", | ||
"UP", | ||
"W", | ||
"YTT", | ||
] | ||
ignore = [ | ||
# Allow non-abstract empty methods in abstract base classes | ||
"B027", | ||
# Allow boolean positional values in function calls, like `dict.get(... True)` | ||
"FBT003", | ||
# Ignore checks for possible passwords | ||
"S105", "S106", "S107", | ||
# Ignore complexity | ||
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915", | ||
] | ||
unfixable = [ | ||
# Don't touch unused imports | ||
"F401", | ||
] | ||
|
||
[tool.ruff.isort] | ||
known-first-party = ["itk_viewer_agave_renderer"] | ||
|
||
[tool.ruff.flake8-tidy-imports] | ||
ban-relative-imports = "all" | ||
|
||
[tool.ruff.per-file-ignores] | ||
# Tests can use magic values, assertions, and relative imports | ||
"tests/**/*" = ["PLR2004", "S101", "TID252"] | ||
|
||
[tool.coverage.run] | ||
source_pkgs = ["itk_viewer_agave_renderer", "tests"] | ||
branch = true | ||
parallel = true | ||
omit = [ | ||
"itk_viewer_agave_renderer/__about__.py", | ||
] | ||
|
||
[tool.coverage.paths] | ||
itk_viewer_agave_renderer = ["itk_viewer_agave_renderer", "*/itk-viewer-agave-renderer/itk_viewer_agave_renderer"] | ||
tests = ["tests", "*/itk-viewer-agave-renderer/tests"] | ||
|
||
[tool.coverage.report] | ||
exclude_lines = [ | ||
"no cov", | ||
"if __name__ == .__main__.:", | ||
"if TYPE_CHECKING:", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# SPDX-FileCopyrightText: 2023-present NumFOCUS | ||
# | ||
# SPDX-License-Identifier: Apache 2.0 |