From 4b8132520eb91472a09685115de07d95f89582ed Mon Sep 17 00:00:00 2001 From: Wei Ouyang Date: Fri, 16 Aug 2024 02:24:37 -0700 Subject: [PATCH] use hypha-rpc and add ray app loader --- MANIFEST.in | 3 +- bioimageio/apps/imagej/manifest.yaml | 5 - bioimageio/engine/app_loader.py | 14 ++- bioimageio/engine/apps/__init__.py | 0 .../{ => engine}/apps/cellpose/README.md | 0 .../apps/cellpose/cellpose-predict/1/model.py | 0 .../cellpose-predict/1/pyodide_test.py | 0 .../cellpose/cellpose-predict/config.pbtxt | 0 .../1/build_bioimageio_package.py | 0 .../cellpose-train/1/interactive_cellpose.py | 0 .../apps/cellpose/cellpose-train/1/model.py | 0 .../apps/cellpose/cellpose-train/1/predict.py | 0 .../cellpose-train/1/test_cellpose_train.py | 0 .../cellpose/cellpose-train/1/test_native.py | 0 .../cellpose/cellpose-train/1/test_triton.py | 0 .../apps/cellpose/cellpose-train/config.pbtxt | 0 bioimageio/{ => engine}/apps/cellpose/main.py | 0 .../{ => engine}/apps/cellpose/manifest.yaml | 0 bioimageio/{ => engine}/apps/imagej/main.py | 0 .../{ => engine}/apps/imagej/run_imagej.py | 0 bioimageio/engine/ray_app_loader.py | 113 ++++++++++++++++++ bioimageio/engine/ray_apps/__init__.py | 0 bioimageio/engine/ray_apps/translator.py | 20 ++++ docs/api.md | 16 +-- environments/pyimagej-py310.yml | 2 +- notebooks/1-bioengine-engine-tutorial.ipynb | 2 +- notebooks/2-bioengine-model-training.ipynb | 2 +- notebooks/3-kaibu-geojson.ipynb | 2 +- notebooks/bioengine-tutorial-embl-2024.ipynb | 2 +- pyproject.toml | 4 +- requirements.txt | 2 +- 31 files changed, 159 insertions(+), 28 deletions(-) delete mode 100644 bioimageio/apps/imagej/manifest.yaml create mode 100644 bioimageio/engine/apps/__init__.py rename bioimageio/{ => engine}/apps/cellpose/README.md (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-predict/1/model.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-predict/1/pyodide_test.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-predict/config.pbtxt (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/build_bioimageio_package.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/interactive_cellpose.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/model.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/predict.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/test_cellpose_train.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/test_native.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/1/test_triton.py (100%) rename bioimageio/{ => engine}/apps/cellpose/cellpose-train/config.pbtxt (100%) rename bioimageio/{ => engine}/apps/cellpose/main.py (100%) rename bioimageio/{ => engine}/apps/cellpose/manifest.yaml (100%) rename bioimageio/{ => engine}/apps/imagej/main.py (100%) rename bioimageio/{ => engine}/apps/imagej/run_imagej.py (100%) create mode 100644 bioimageio/engine/ray_app_loader.py create mode 100644 bioimageio/engine/ray_apps/__init__.py create mode 100644 bioimageio/engine/ray_apps/translator.py diff --git a/MANIFEST.in b/MANIFEST.in index fcc7834..fcf169a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include environments/* -recursive-include bioimageio/apps +recursive-include bioimageio/engine/apps * +recursive-include bioimageio/engine/ray_apps * global-exclude *.pyc global-exclude *__pycache__* \ No newline at end of file diff --git a/bioimageio/apps/imagej/manifest.yaml b/bioimageio/apps/imagej/manifest.yaml deleted file mode 100644 index 9c5a042..0000000 --- a/bioimageio/apps/imagej/manifest.yaml +++ /dev/null @@ -1,5 +0,0 @@ -name: ImageJ -id: imagej -description: ImageJ is a public domain Java image processing program inspired by NIH Image for the Macintosh. -runtime: python -entrypoint: main.py diff --git a/bioimageio/engine/app_loader.py b/bioimageio/engine/app_loader.py index 1e94742..079c34d 100644 --- a/bioimageio/engine/app_loader.py +++ b/bioimageio/engine/app_loader.py @@ -5,6 +5,7 @@ from typing import Optional import importlib.util from enum import Enum +from hypha_rpc.utils import ObjectProxy logger = logging.getLogger(__name__) @@ -12,6 +13,7 @@ class AppRuntime(Enum): python = "python" pyodide = "pyodide" triton = "triton" + ray = "ray" class AppInfo(BaseModel): name: str @@ -21,13 +23,13 @@ class AppInfo(BaseModel): entrypoint: Optional[str] = None async def run(self, server): + assert self.entrypoint + + file_path = Path(__file__).parent / "apps" / self.id / self.entrypoint + module_name = 'bioimageio.engine.apps.' + self.id.replace('-', '_') + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) if self.runtime == AppRuntime.python: - assert self.entrypoint - - file_path = Path(__file__).parent.parent.parent / "bioimageio/apps" / self.id / self.entrypoint - module_name = 'bioimageio.engine.apps.' + self.id.replace('-', '_') - spec = importlib.util.spec_from_file_location(module_name, file_path) - module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) assert hasattr(module, "hypha_startup") await module.hypha_startup(server) diff --git a/bioimageio/engine/apps/__init__.py b/bioimageio/engine/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bioimageio/apps/cellpose/README.md b/bioimageio/engine/apps/cellpose/README.md similarity index 100% rename from bioimageio/apps/cellpose/README.md rename to bioimageio/engine/apps/cellpose/README.md diff --git a/bioimageio/apps/cellpose/cellpose-predict/1/model.py b/bioimageio/engine/apps/cellpose/cellpose-predict/1/model.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-predict/1/model.py rename to bioimageio/engine/apps/cellpose/cellpose-predict/1/model.py diff --git a/bioimageio/apps/cellpose/cellpose-predict/1/pyodide_test.py b/bioimageio/engine/apps/cellpose/cellpose-predict/1/pyodide_test.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-predict/1/pyodide_test.py rename to bioimageio/engine/apps/cellpose/cellpose-predict/1/pyodide_test.py diff --git a/bioimageio/apps/cellpose/cellpose-predict/config.pbtxt b/bioimageio/engine/apps/cellpose/cellpose-predict/config.pbtxt similarity index 100% rename from bioimageio/apps/cellpose/cellpose-predict/config.pbtxt rename to bioimageio/engine/apps/cellpose/cellpose-predict/config.pbtxt diff --git a/bioimageio/apps/cellpose/cellpose-train/1/build_bioimageio_package.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/build_bioimageio_package.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/build_bioimageio_package.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/build_bioimageio_package.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/interactive_cellpose.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/interactive_cellpose.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/interactive_cellpose.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/interactive_cellpose.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/model.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/model.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/model.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/model.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/predict.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/predict.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/predict.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/predict.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/test_cellpose_train.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/test_cellpose_train.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/test_cellpose_train.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/test_cellpose_train.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/test_native.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/test_native.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/test_native.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/test_native.py diff --git a/bioimageio/apps/cellpose/cellpose-train/1/test_triton.py b/bioimageio/engine/apps/cellpose/cellpose-train/1/test_triton.py similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/1/test_triton.py rename to bioimageio/engine/apps/cellpose/cellpose-train/1/test_triton.py diff --git a/bioimageio/apps/cellpose/cellpose-train/config.pbtxt b/bioimageio/engine/apps/cellpose/cellpose-train/config.pbtxt similarity index 100% rename from bioimageio/apps/cellpose/cellpose-train/config.pbtxt rename to bioimageio/engine/apps/cellpose/cellpose-train/config.pbtxt diff --git a/bioimageio/apps/cellpose/main.py b/bioimageio/engine/apps/cellpose/main.py similarity index 100% rename from bioimageio/apps/cellpose/main.py rename to bioimageio/engine/apps/cellpose/main.py diff --git a/bioimageio/apps/cellpose/manifest.yaml b/bioimageio/engine/apps/cellpose/manifest.yaml similarity index 100% rename from bioimageio/apps/cellpose/manifest.yaml rename to bioimageio/engine/apps/cellpose/manifest.yaml diff --git a/bioimageio/apps/imagej/main.py b/bioimageio/engine/apps/imagej/main.py similarity index 100% rename from bioimageio/apps/imagej/main.py rename to bioimageio/engine/apps/imagej/main.py diff --git a/bioimageio/apps/imagej/run_imagej.py b/bioimageio/engine/apps/imagej/run_imagej.py similarity index 100% rename from bioimageio/apps/imagej/run_imagej.py rename to bioimageio/engine/apps/imagej/run_imagej.py diff --git a/bioimageio/engine/ray_app_loader.py b/bioimageio/engine/ray_app_loader.py new file mode 100644 index 0000000..9330de2 --- /dev/null +++ b/bioimageio/engine/ray_app_loader.py @@ -0,0 +1,113 @@ +"""Provide main entrypoint.""" +import asyncio +from ray import serve +import logging +import os +import sys +from pathlib import Path +import urllib.request +from starlette.requests import Request +from starlette.responses import HTMLResponse + +from hypha_rpc.utils import ObjectProxy +from hypha_rpc.sync import connect_to_server + +logging.basicConfig(stream=sys.stdout) +logger = logging.getLogger("app_launcher") +logger.setLevel(logging.INFO) + + +def load_app(plugin_file): + """Load app file.""" + if os.path.isfile(plugin_file): + with open(plugin_file, "r", encoding="utf-8") as fil: + content = fil.read() + elif plugin_file.startswith("http"): + with urllib.request.urlopen(plugin_file) as response: + content = response.read().decode("utf-8") + # remove query string + plugin_file = plugin_file.split("?")[0] + else: + raise Exception(f"Invalid input app file path: {plugin_file}") + + if plugin_file.endswith(".py"): + app_config = ObjectProxy() + import hypha_rpc + def export(app_class, config=None): + app_config.update(config or {}) + app_config.app_class = app_class + hypha_rpc.api = ObjectProxy(export=export) + exec(content, globals()) # pylint: disable=exec-used + logger.info("Plugin executed") + return app_config + else: + raise RuntimeError(f"Invalid script file type ({plugin_file})") + +@serve.deployment +class HyphaApp: + def __init__(self, server_url, workspace, token, services): + self._services = services + self._hypha_server = connect_to_server({"server_url": "https://hypha.aicell.io", "token": token, "workspace": workspace}) + svc = { + "name": "Ray Functions", + "id": "ray-functions", + "config": { + "visibility": "protected" + }, + } + + def create_service_function(name, service_handle): + async def service_function(*args, **kwargs): + return await service_handle.translate.remote(*args, **kwargs) + service_function.__name__ = name + return service_function + + for service_name, service_bind in self._services.items(): + svc[service_name] = create_service_function(service_name, service_bind) + info = self._hypha_server.register_service(svc, {"overwrite":True}) + print("Hypha service info:", info) + self.info = info + + async def __call__(self, request: Request): + redirect_url = f"https://hypha.aicell.io/{self.info.config.workspace}/services/{self.info.id.split('/')[1]}/translator?text=hello" + return HTMLResponse( + """ + + + + + +

Redirecting to Hypha...

+

Services:

+ + + + """.format(redirect_url, "\n".join([f"
  • {name}
  • " for name in self._services.keys()])) + ) + + +current_dir = Path(os.path.dirname(os.path.realpath(__file__))) + +ray_apps = {} +apps_dir = current_dir / "ray_apps" +for app_file in apps_dir.iterdir(): + if app_file.is_file() and app_file.suffix == ".py" and app_file.stem != "__init__": + load_app(str(app_file)) + app_info = load_app(str(app_file)) + app_deployment = serve.deployment(name=app_info.name)(app_info.app_class).bind() + ray_apps[app_info.name] = app_deployment + +# Getting config from environment +server_url = os.environ.get("HYPHA_SERVER_URL") +workspace = os.environ.get("HYPHA_WORKSPACE") +token = os.environ.get("HYPHA_TOKEN") + +app = HyphaApp.bind(server_url, workspace, token, ray_apps) + +if __name__ == "__main__": + serve.start() + serve.run(app, name="hypha-apps") + import asyncio + asyncio.get_event_loop().run_forever() diff --git a/bioimageio/engine/ray_apps/__init__.py b/bioimageio/engine/ray_apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bioimageio/engine/ray_apps/translator.py b/bioimageio/engine/ray_apps/translator.py new file mode 100644 index 0000000..8664e1d --- /dev/null +++ b/bioimageio/engine/ray_apps/translator.py @@ -0,0 +1,20 @@ +from hypha_rpc import api +from transformers import pipeline + + +class Translator: + def __init__(self): + # Load model + self.model = pipeline("translation_en_to_fr", model="t5-small") + + def translate(self, text: str) -> str: + # Run inference + model_output = self.model(text) + + # Post-process output to return only the translation text + translation = model_output[0]["translation_text"] + + return translation + + +api.export(Translator, {"name": "translator"}) \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index e228513..d961b0e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ # BioEngine API -Under the hood, BioEngine uses [Hypha](https://ha.amun.ai/) to orchestrate the services provided in the containers. We uses the `imjoy-rpc` client to communicate to the Hypha server for model execution. +Under the hood, BioEngine uses [Hypha](https://ha.amun.ai/) to orchestrate the services provided in the containers. We uses the `hypha-rpc` client to communicate to the Hypha server for model execution. We provide a free public server for the BioEngine available at https://ai.imjoy.io. @@ -17,10 +17,10 @@ If you are interested in setting up your own BioEngine server, please check our ## Using BioEngine in Python -First install the `imjoy-rpc` library: +First install the `hypha-rpc` library: ```bash -pip install imjoy-rpc +pip install hypha-rpc ``` Use the following code to connect to the server and access the service. The code first connects to the server and then gets the service by its ID. The service can then be used like a normal Python object. @@ -98,18 +98,18 @@ if __name__ == "__main__": > [!NOTE] > In Python, the recommended way to interact with the server to use asynchronous functions with `asyncio`. However, if you need to use synchronous functions, -> you can use `from imjoy_rpc.hypha.sync import login, connect_to_server` (available since `imjoy-rpc>=0.5.25.post0`) instead. -> They have the exact same arguments as the asynchronous versions. For more information, see [Synchronous Wrapper](https://github.com/imjoy-team/imjoy-rpc/blob/master/imjoy-rpc-v2.md#synchronous-wrapper) +> you can use `from imjoy_rpc.hypha.sync import login, connect_to_server` (available since `hypha-rpc>=0.5.25.post0`) instead. +> They have the exact same arguments as the asynchronous versions. For more information, see [Synchronous Wrapper](https://github.com/imjoy-team/hypha-rpc/blob/master/hypha-rpc-v2.md#synchronous-wrapper) > 💡 Tip
    > For QT-based applications, e.g. napari, imswitch, use the synchronous api. ## Using the BioEingine in JavaScript -Include the following script in your HTML file to load the `imjoy-rpc` client: +Include the following script in your HTML file to load the `hypha-rpc` client: ```html - + ``` Use the following code in JavaScript to connect to the hypha server and access the bioenigne service via the `triton-client`: @@ -119,7 +119,7 @@ async function main(){ const server = await hyphaWebsocketClient.connectToServer({"server_url": "https://ai.imjoy.io"}) const svc = await server.getService("triton-client") // encode the image, similar to np.random.randint(0, 255, (1, 3, 256, 256)) - // see https://github.com/imjoy-team/imjoy-rpc/blob/master/imjoy-rpc-v2.md#data-type-representation + // see https://github.com/imjoy-team/hypha-rpc/blob/master/hypha-rpc-v2.md#data-type-representation image = {_rtype: "ndarray", _rvalue: new ArrayBuffer(1*3*256*256), _rshape: [1, 3, 256, 256], _rdtype: "uint8"} ret = await triton.execute({ inputs: [{"inputs": [image], "model_id": "conscientious-seashell"}], diff --git a/environments/pyimagej-py310.yml b/environments/pyimagej-py310.yml index 822a40a..9cec0a7 100644 --- a/environments/pyimagej-py310.yml +++ b/environments/pyimagej-py310.yml @@ -8,5 +8,5 @@ dependencies: - openjdk=8 - pip - pip: - - imjoy-rpc + - hypha-rpc diff --git a/notebooks/1-bioengine-engine-tutorial.ipynb b/notebooks/1-bioengine-engine-tutorial.ipynb index 6f0e800..99fa5a9 100644 --- a/notebooks/1-bioengine-engine-tutorial.ipynb +++ b/notebooks/1-bioengine-engine-tutorial.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "source": "try:\n # For pyodide in the browser\n import micropip\n await micropip.install(['imjoy-rpc', 'kaibu-utils', 'pyodide-http', 'requests'])\n \n # 2. Patch requests\n import pyodide_http\n pyodide_http.patch_all() # Patch all libraries\nexcept ImportError:\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'imjoy-rpc', 'kaibu-utils'])\n\nimport io\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport numpy as np\n#from pyotritonclient import execute\nfrom imjoy_rpc.hypha import connect_to_server\nfrom kaibu_utils import fetch_image\n\n\n", + "source": "try:\n # For pyodide in the browser\n import micropip\n await micropip.install(['hypha-rpc', 'kaibu-utils', 'pyodide-http', 'requests'])\n \n # 2. Patch requests\n import pyodide_http\n pyodide_http.patch_all() # Patch all libraries\nexcept ImportError:\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'hypha-rpc', 'kaibu-utils'])\n\nimport io\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport numpy as np\n#from pyotritonclient import execute\nfrom imjoy_rpc.hypha import connect_to_server\nfrom kaibu_utils import fetch_image\n\n\n", "metadata": { "trusted": true }, diff --git a/notebooks/2-bioengine-model-training.ipynb b/notebooks/2-bioengine-model-training.ipynb index ca245c9..b72c6fb 100644 --- a/notebooks/2-bioengine-model-training.ipynb +++ b/notebooks/2-bioengine-model-training.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "source": "try:\n # JupyterLite/Pyodide\n import pyodide\n import micropip\n await micropip.install('pyotritonclient')\n from imjoy_rpc.utils import open_elfinder\n IS_PYODIDE = True\nexcept ImportError:\n IS_PYODIDE = False\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'pyotritonclient', 'imjoy-rpc', 'requests'])\n\nimport io\nimport asyncio\nimport os\nfrom pyotritonclient import SequenceExcutor, execute\nimport numpy as np\nimport pickle\nimport imageio\nfrom js import fetch\nimport matplotlib.pyplot as plt\n\n\n%matplotlib inline\n\ndef display_image(image, mask):\n # display the output\n fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))\n ax1.imshow(image)\n ax1.set_title('input image')\n ax2.imshow(mask)\n ax2.set_title('predicted mask')\n plt.show()\n\nasync def download_data(url):\n if IS_PYODIDE:\n response = await fetch(url)\n bytes = await response.arrayBuffer()\n bytes = bytes.to_py()\n buffer = io.BytesIO(bytes)\n else:\n import requests\n response = requests.get(url)\n buffer = io.BytesIO(response.content)\n return pickle.load(buffer)\n\nasync def train(model_id, samples, epochs=1, model_token = None, resume=True, pretrained_model=\"cyto\", channels=None, diameter=100):\n \"\"\"\n Train a cellpose model through the BioEngine\n # set pretrained_model to None if you want to train from scratch\n # set model_token to a string if you want to protect the model\n # from overwriting by other users\n \"\"\"\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-train\",\n decode_json=True,\n sequence_id=model_id,\n )\n if not channels:\n channels = [1, 2]\n for epoch in range(epochs):\n losses = []\n for (image, labels, info) in samples:\n inputs = [\n image.astype(\"float32\"),\n labels.astype(\"uint16\"),\n {\n \"steps\": 16,\n \"pretrained_model\": pretrained_model,\n \"resume\": resume,\n \"model_token\": model_token,\n \"channels\": channels,\n \"diam_mean\": 30,\n },\n ]\n result = await seq.step(inputs, select_outputs=[\"info\"])\n losses.append(result[\"info\"][0][\"loss\"])\n avg_loss = np.array(losses).mean()\n print(f\"Epoch {epoch} loss={avg_loss}\")\n await seq.end()\n\nasync def predict(model_id, samples, channels=None, diameter=100, reload=False):\n \"\"\"Make predition with the specified cellpose model\"\"\"\n # Start the prediction\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-predict\",\n decode_json=True,\n sequence_id=model_id,\n )\n if not channels:\n channels = [1, 2]\n for i, (image, _, _) in enumerate(samples):\n inputs = [image.astype(\"float32\"), {\"channels\": channels, \"diameter\": diameter, \"reload\": reload}]\n results = await seq.step(inputs, select_outputs=[\"mask\"])\n display_image(image, results['mask'])\n # await seq.end()\n\nasync def download_model(model_id, valid_image, valid_labels, model_token = None):\n \"\"\"\n Download the trained model by its id.\n \"\"\"\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-train\",\n decode_json=True,\n sequence_id=model_id,\n )\n print(f\"Exporting cellpose model: {model_id}\")\n result = await seq.end(\n [\n valid_image.astype(\"float32\"),\n valid_labels.astype(\"uint16\"),\n {\n \"resume\": True,\n \"model_token\": model_token,\n \"channels\": [1, 2],\n \"diameter\": 100.0,\n \"model_format\": \"bioimageio\",\n },\n ],\n decode_json=True,\n select_outputs=[\"info\", \"model\"],\n )\n # Save the weights\n model_package = result[\"model\"][0]\n filename = result[\"info\"][0][\"model_files\"][0]\n print(f\"Saving model {filename}...\")\n if IS_PYODIDE:\n with open_elfinder(\"/home/\"+filename, \"wb\") as fil:\n fil.write(model_package)\n else:\n with open(os.path.join(os.path.expanduser('~'), filename), \"wb\") as fil:\n fil.write(model_package)\n \n print(f\"Model package saved to {filename}\")", + "source": "try:\n # JupyterLite/Pyodide\n import pyodide\n import micropip\n await micropip.install('pyotritonclient')\n from imjoy_rpc.utils import open_elfinder\n IS_PYODIDE = True\nexcept ImportError:\n IS_PYODIDE = False\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'pyotritonclient', 'hypha-rpc', 'requests'])\n\nimport io\nimport asyncio\nimport os\nfrom pyotritonclient import SequenceExcutor, execute\nimport numpy as np\nimport pickle\nimport imageio\nfrom js import fetch\nimport matplotlib.pyplot as plt\n\n\n%matplotlib inline\n\ndef display_image(image, mask):\n # display the output\n fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))\n ax1.imshow(image)\n ax1.set_title('input image')\n ax2.imshow(mask)\n ax2.set_title('predicted mask')\n plt.show()\n\nasync def download_data(url):\n if IS_PYODIDE:\n response = await fetch(url)\n bytes = await response.arrayBuffer()\n bytes = bytes.to_py()\n buffer = io.BytesIO(bytes)\n else:\n import requests\n response = requests.get(url)\n buffer = io.BytesIO(response.content)\n return pickle.load(buffer)\n\nasync def train(model_id, samples, epochs=1, model_token = None, resume=True, pretrained_model=\"cyto\", channels=None, diameter=100):\n \"\"\"\n Train a cellpose model through the BioEngine\n # set pretrained_model to None if you want to train from scratch\n # set model_token to a string if you want to protect the model\n # from overwriting by other users\n \"\"\"\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-train\",\n decode_json=True,\n sequence_id=model_id,\n )\n if not channels:\n channels = [1, 2]\n for epoch in range(epochs):\n losses = []\n for (image, labels, info) in samples:\n inputs = [\n image.astype(\"float32\"),\n labels.astype(\"uint16\"),\n {\n \"steps\": 16,\n \"pretrained_model\": pretrained_model,\n \"resume\": resume,\n \"model_token\": model_token,\n \"channels\": channels,\n \"diam_mean\": 30,\n },\n ]\n result = await seq.step(inputs, select_outputs=[\"info\"])\n losses.append(result[\"info\"][0][\"loss\"])\n avg_loss = np.array(losses).mean()\n print(f\"Epoch {epoch} loss={avg_loss}\")\n await seq.end()\n\nasync def predict(model_id, samples, channels=None, diameter=100, reload=False):\n \"\"\"Make predition with the specified cellpose model\"\"\"\n # Start the prediction\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-predict\",\n decode_json=True,\n sequence_id=model_id,\n )\n if not channels:\n channels = [1, 2]\n for i, (image, _, _) in enumerate(samples):\n inputs = [image.astype(\"float32\"), {\"channels\": channels, \"diameter\": diameter, \"reload\": reload}]\n results = await seq.step(inputs, select_outputs=[\"mask\"])\n display_image(image, results['mask'])\n # await seq.end()\n\nasync def download_model(model_id, valid_image, valid_labels, model_token = None):\n \"\"\"\n Download the trained model by its id.\n \"\"\"\n seq = SequenceExcutor(\n server_url=\"https://ai.imjoy.io/triton\",\n model_name=\"cellpose-train\",\n decode_json=True,\n sequence_id=model_id,\n )\n print(f\"Exporting cellpose model: {model_id}\")\n result = await seq.end(\n [\n valid_image.astype(\"float32\"),\n valid_labels.astype(\"uint16\"),\n {\n \"resume\": True,\n \"model_token\": model_token,\n \"channels\": [1, 2],\n \"diameter\": 100.0,\n \"model_format\": \"bioimageio\",\n },\n ],\n decode_json=True,\n select_outputs=[\"info\", \"model\"],\n )\n # Save the weights\n model_package = result[\"model\"][0]\n filename = result[\"info\"][0][\"model_files\"][0]\n print(f\"Saving model {filename}...\")\n if IS_PYODIDE:\n with open_elfinder(\"/home/\"+filename, \"wb\") as fil:\n fil.write(model_package)\n else:\n with open(os.path.join(os.path.expanduser('~'), filename), \"wb\") as fil:\n fil.write(model_package)\n \n print(f\"Model package saved to {filename}\")", "metadata": { "trusted": true }, diff --git a/notebooks/3-kaibu-geojson.ipynb b/notebooks/3-kaibu-geojson.ipynb index 7e96094..fb9e49d 100644 --- a/notebooks/3-kaibu-geojson.ipynb +++ b/notebooks/3-kaibu-geojson.ipynb @@ -23,7 +23,7 @@ "cells": [ { "cell_type": "code", - "source": "import micropip\nawait micropip.install([\"kaibu-utils\", \"imjoy-rpc\", \"pyodide_http\"])\n\nimport pyodide_http\npyodide_http.patch_all() # Patch all libraries\n", + "source": "import micropip\nawait micropip.install([\"kaibu-utils\", \"hypha-rpc\", \"pyodide_http\"])\n\nimport pyodide_http\npyodide_http.patch_all() # Patch all libraries\n", "metadata": { "trusted": true }, diff --git a/notebooks/bioengine-tutorial-embl-2024.ipynb b/notebooks/bioengine-tutorial-embl-2024.ipynb index 4db1a65..98c1811 100644 --- a/notebooks/bioengine-tutorial-embl-2024.ipynb +++ b/notebooks/bioengine-tutorial-embl-2024.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "source": "try:\n # For pyodide in the browser\n import micropip\n await micropip.install(['imjoy-rpc', 'kaibu-utils', 'pyodide-http', 'requests'])\n \n # 2. Patch requests\n import pyodide_http\n pyodide_http.patch_all() # Patch all libraries\nexcept ImportError:\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'imjoy-rpc', 'kaibu-utils'])\n\nimport io\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport numpy as np\n#from pyotritonclient import execute\nfrom imjoy_rpc.hypha import connect_to_server\nfrom kaibu_utils import fetch_image\n\n\n", + "source": "try:\n # For pyodide in the browser\n import micropip\n await micropip.install(['hypha-rpc', 'kaibu-utils', 'pyodide-http', 'requests'])\n \n # 2. Patch requests\n import pyodide_http\n pyodide_http.patch_all() # Patch all libraries\nexcept ImportError:\n # For native python with pip\n import subprocess\n subprocess.call(['pip', 'install', 'hypha-rpc', 'kaibu-utils'])\n\nimport io\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nimport numpy as np\n#from pyotritonclient import execute\nfrom imjoy_rpc.hypha import connect_to_server\nfrom kaibu_utils import fetch_image\n\n\n", "metadata": { "trusted": true }, diff --git a/pyproject.toml b/pyproject.toml index e825fbe..2b8fcf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ version = "0.1.1" readme = "README.md" description = "BioEngine: Your AI Engine for Advanced BioImage Analysis" dependencies = [ - "hypha[s3]>=0.15.46", - "imjoy-rpc", + "hypha[s3]>=0.20.22", + "hypha-rpc", "PyYAML", "hypha-launcher>=0.1.3", "pyotritonclient", diff --git a/requirements.txt b/requirements.txt index ee39176..d21373a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -imjoy-rpc +hypha-rpc hypha>=0.15.45 PyYAML==6.0.1 hypha-launcher