From 0fd0eeec4fabb42ae1171fbd694c86a785ea6eb0 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 1 Apr 2025 14:24:54 +0100 Subject: [PATCH 01/82] refactored to use ezinput --- notebooks/ChannelRegistration.ipynb | 29 +- notebooks/DriftCorrection.ipynb | 29 +- notebooks/ExampleDataSRRFandQC.ipynb | 337 +++++++++--- notebooks/NonLocalMeansDenoising.ipynb | 25 +- notebooks/ParamSweepandeSRRF.ipynb | 224 +++++--- notebooks/SRMetrics.ipynb | 135 +++-- notebooks/SRRFandQC.ipynb | 133 +++-- notebooks/eSRRFandQC.ipynb | 496 +++++++++++++++--- pyproject.toml | 1 + src/notebookchef/pantry/image_loading.py | 4 +- .../pantry/methods/channel_registration.py | 2 +- .../methods/channel_registration_apply.py | 4 +- src/notebookchef/pantry/methods/decorr.py | 2 +- .../pantry/methods/drift_correction.py | 2 +- .../pantry/methods/drift_correction_apply.py | 4 +- src/notebookchef/pantry/methods/error_map.py | 82 ++- src/notebookchef/pantry/methods/esrrf.py | 95 +++- src/notebookchef/pantry/methods/frc.py | 2 +- .../pantry/methods/nlm_denoising.py | 4 +- src/notebookchef/pantry/methods/srrf.py | 4 +- src/notebookchef/pantry/notebook_setup.py | 5 +- 21 files changed, 1203 insertions(+), 416 deletions(-) diff --git a/notebooks/ChannelRegistration.ipynb b/notebooks/ChannelRegistration.ipynb index 62eab4d6..598ed1a8 100644 --- a/notebooks/ChannelRegistration.ipynb +++ b/notebooks/ChannelRegistration.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "313252b6", + "id": "c8226d8c", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a7fd8e2a", + "id": "6f11b9e7", "metadata": { "cellView": "form" }, @@ -54,7 +54,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -77,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "321413ab", + "id": "0f360c1d", "metadata": { "cellView": "form" }, @@ -91,7 +94,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -102,7 +105,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -161,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "a84604a2", + "id": "c7f6f43c", "metadata": {}, "source": [ "# Channel Registration Parameters: \n", @@ -179,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7a99907f", + "id": "d47c3053", "metadata": { "cellView": "form" }, @@ -229,7 +232,7 @@ " gui_reg._main_display.children = gui_reg._main_display.children + (stackview.slice(dataset_registered, colormap=gui_reg[\"cmaps\"].value, continuous_update=True),)\n", "\n", "\n", - "gui_reg.add_label(\"Channel Registration parameters:\")\n", + "gui_reg.add_label(value=\"Channel Registration parameters:\")\n", "gui_reg.add_int_slider(\"ref\", description=\"Reference channel\", min=0, max=dataset_original.shape[0]-1, value=0)\n", "gui_reg.add_int_slider(\"max\", description=\"Max expected drift\", min=0, max=1000, value=10)\n", "gui_reg.add_int_slider(\"blocks\", description=\"Blocks per axis\", min=1, max=10, value=5)\n", @@ -247,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "eefc7618", + "id": "ef3079fe", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated translation mask\n", @@ -257,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55cdcf4f", + "id": "ad3082a8", "metadata": { "cellView": "form" }, @@ -312,9 +315,9 @@ " gui_reg_apply[\"Register Image\"].description = \"Align\"\n", " gui_reg_apply._main_display.children = gui_reg_apply._main_display.children + (stackview.slice(aligned_image, colormap=gui_reg_apply[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_reg_apply.add_label(\"Load translation mask:\")\n", + "gui_reg_apply.add_label(value=\"Load translation mask:\")\n", "gui_reg_apply.add_file_upload(\"upload\")\n", - "gui_reg_apply.add_label(\"Load image to register:\")\n", + "gui_reg_apply.add_label(value=\"Load image to register:\")\n", "gui_reg_apply.add_file_upload(\"upload image\")\n", "gui_reg_apply.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", diff --git a/notebooks/DriftCorrection.ipynb b/notebooks/DriftCorrection.ipynb index 487745b9..2220791b 100644 --- a/notebooks/DriftCorrection.ipynb +++ b/notebooks/DriftCorrection.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "22187bc5", + "id": "8fb2655a", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9c2411b8", + "id": "e3f17e2d", "metadata": { "cellView": "form" }, @@ -54,7 +54,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -77,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18021935", + "id": "eb67e5c7", "metadata": { "cellView": "form" }, @@ -91,7 +94,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -102,7 +105,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -161,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "03e411b0", + "id": "65e5af99", "metadata": {}, "source": [ "# Drift Correction Parameters: \n", @@ -178,7 +181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "96c40ea1", + "id": "b7f24156", "metadata": { "cellView": "form" }, @@ -230,7 +233,7 @@ " gui_drift[\"align\"].description = \"Align\"\n", " gui_drift._main_display.children = gui_drift._main_display.children + (stackview.slice(dataset_aligned, colormap=gui_drift[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_drift.add_label(\"Drift Correction parameters:\")\n", + "gui_drift.add_label(value=\"Drift Correction parameters:\")\n", "gui_drift.add_dropdown(\"ref\", description=\"Reference frame\", options=[\"First frame\", \"Previous frame\"], value=\"First frame\")\n", "gui_drift.add_int_slider(\"max\", description=\"Max expected drift\", min=0, max=1000, value=10)\n", "gui_drift.add_int_slider(\"time_averaging\", description=\"Time averaging\", min=1, max=dataset_original.shape[0], value=1)\n", @@ -247,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "86fab6a2", + "id": "803f977b", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated drift table\n", @@ -257,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3d5028a4", + "id": "7203020b", "metadata": { "cellView": "form" }, @@ -314,9 +317,9 @@ " gui_drift_apply[\"Align image\"].description = \"Align\"\n", " gui_drift_apply._main_display.children = gui_drift_apply._main_display.children + (stackview.slice(aligned_image, colormap=gui_drift_apply[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_drift_apply.add_label(\"Load drift table:\")\n", + "gui_drift_apply.add_label(value=\"Load drift table:\")\n", "gui_drift_apply.add_file_upload(\"upload\")\n", - "gui_drift_apply.add_label(\"Load image to align:\")\n", + "gui_drift_apply.add_label(value=\"Load image to align:\")\n", "gui_drift_apply.add_file_upload(\"upload image\")\n", "gui_drift_apply.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", diff --git a/notebooks/ExampleDataSRRFandQC.ipynb b/notebooks/ExampleDataSRRFandQC.ipynb index 8594d77d..73bbb71b 100644 --- a/notebooks/ExampleDataSRRFandQC.ipynb +++ b/notebooks/ExampleDataSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "35223840", + "id": "1557495b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "aef3a5f2", + "id": "cb120f83", "metadata": { "cellView": "form" }, @@ -47,12 +47,28 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "9c73791a", + "execution_count": 1, + "id": "5fc82466", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/miniconda3/envs/aiobio/lib/python3.11/site-packages/pytools/persistent_dict.py:52: RecommendedHashNotFoundWarning: Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.\n", + " warn(\"Unable to import recommended hash 'siphash24.siphash13', \"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cupy implementation is not available. Make sure you have the right version of Cupy and CUDA installed.\n" + ] + } + ], "source": [ "#@title Install NanoPyx, import necessary libraries and connect to Google Drive\n", "import sys\n", @@ -80,7 +96,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -102,7 +121,7 @@ }, { "cell_type": "markdown", - "id": "c46af7c4", + "id": "0dae76fd", "metadata": {}, "source": [ "## Next lets create the Data Loader GUI.\n", @@ -112,12 +131,27 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "e86a2c07", + "execution_count": 2, + "id": "b6e1bd3d", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2f92d1db91d74c41a2bb9e6a4b6f096f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Button(description='Use Own data', layout=Layout(width='50%'), style=ButtonStyle()), Button(des…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title Load image stack\n", "# Create a GUI\n", @@ -127,7 +161,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -138,7 +172,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -197,7 +231,7 @@ }, { "cell_type": "markdown", - "id": "5f2998a0", + "id": "3631c841", "metadata": {}, "source": [ "## Now let's use SRRF to generate a super-resolution image\n", @@ -206,7 +240,7 @@ }, { "cell_type": "markdown", - "id": "85caef65", + "id": "3d0566ef", "metadata": {}, "source": [ "# SRRF Parameters:\n", @@ -221,12 +255,27 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "980a7aae", + "execution_count": 3, + "id": "eda46129", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f9d2b449319040eeadedfc747738f638", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(FloatSlider(value=0.5, description='Ring Radius:', layout=Layout(width='50%'), max=3.0, min=0.1…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title Create SRRF GUI\n", "gui_srrf = EasyGui(\"srrf\")\n", @@ -273,10 +322,10 @@ " tiff.imwrite(name + \"_srrf.tif\", dataset_srrf)\n", " gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5, remember_value=True)\n", + "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5)\n", "gui_srrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=5)\n", "gui_srrf.add_int_slider(\"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3)\n", - "gui_srrf.add_label(\"-=-= Time-Lapse =-=-\")\n", + "gui_srrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", "gui_srrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", "gui_srrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", "gui_srrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -291,7 +340,7 @@ }, { "cell_type": "markdown", - "id": "51e4d923", + "id": "68a331c9", "metadata": {}, "source": [ "## Let's use NanoPyx to generate an error map of the SRRF image\n", @@ -300,14 +349,100 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "4abbd64f", + "execution_count": 4, + "id": "81b7bcf5", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8de8142ffd8f4c0684018201d413b78d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Checkbox(value=True, description='Save output', layout=Layout(width='50%'), style=CheckboxStyle…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SliceRSERSP
0033.7130740.969177
1132.9383470.967807
\n", + "
" + ], + "text/plain": [ + " Slice RSE RSP\n", + "0 0 33.713074 0.969177\n", + "1 1 32.938347 0.967807" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAGFCAYAAAASI+9IAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/cuSZUuSJYbZebp73EdWZReqG2jBgIQA4IATjsm/4m9gwi+iUIQDjNkkBmxCWkiiGt2VVZn3ERH+OC/KWrqWmpqdfdw9bsZtcBA702+4n8fetm2b6WOp6tLV5XK5tG/Ht+Pb8e34dnw7Wmvr/6UH8O34dnw7vh3fjv//Ob4phW/Ht+Pb8e34duTxTSl8O74d345vx7cjj29K4dvx7fh2fDu+HXl8Uwrfjm/Ht+Pb8e3I45tS+HZ8O74d345vRx7flMK349vx7fh2fDvy+KYUvh3fjm/Ht+Pbkce2vfP4r/+7/6794X+4tP1fDu28Xbfz3bq19apdVuVDt36fj8sr77uU7tLa6uK/L/11fNV/QqWtWrusVu1S1NsK9XjnS1udzm11OLfV87G1w7Gt8L/VprUVf+O/+ME9XHjGczuvzu2yurTL3bZd7rftvN+0y3bVLhud+3Rpq8OlrQ+ntjqccHGdJ369rC/xs1+18/2mne/X/P7q2Nr6qbXtp3Pb/HJo6788ts1x1darbYzlcmnn86md15fWcM37Xbvc4dpr3p/vm/NyjgmIvzUZ/FuTdI7X+S1/pvzO73Nujq09Hdt6tWnrza6tttt22WzaZYP7xZzGfXM+2qmd26ldLqd2OZ9aO585x/7BWHgf63VbrTdthTFrcfRxaCz47il+eN3dXWu7uNez5tr33M6ndjk+t9Pq1NpuHfOyxQdaW+M5HON5rPAvxoJLrFetbVZxLswfPo77wWPC/R/Obf105LpYH1pbn9fxLC6rtsKHOL8X3uflcubzPGMOcH2sC/y7Weczx/2sjufWjlhn55iL1bqtcG+bTWtr/Ky1RjURfG4YM66JdbXiej0fX9r5+Bxr7m7H79U13U79u+O26fO62mzbardrbbdrF8wrxou5xRpej89hdTxxb7SX2CP8PuaBzxH3uG4N98rxxzrnvl+P6z4Gqfdxev7b/1486nf7SilCYDznbzq85otcuRoG1u/RsuLQVo+Htj7h0pu2xrNbrTm/WPuny5HP8fJhx7VI+YD5wdxSAOhaSyXB5T6GOZve++L7q39U+Sl5tX4+UWZ/+i/37f/2f/o/fj2l8If/x6X98f/yD20FgfNyaBdsAArXekPTDfqGv6RouiiBLLae/61HTmo8EF57t2ur/b61D3ft/OGuHb/btsN39+3lh3U7fr9ux4fWzg+tXfb6/Lm11cuqbT63tv/p0va/nNv+L6e2/fVzW398apfnlxB423VrEJz7bTt9f9cOf9i157/ZtOPDqp3uIHggqFrb/Xppd386t92vx7b5y+fWHp+54RsExIf7dvzxrj397Yf29Id1KA2su1Nrm8+Xtv/13Pa/ntr2L8e2+fiptafn1qB8sOF5f12ZefI5BfEfrQS9j43bpLjLaxCqEBSnu207/LBvT3+zbS9/u2mHP67a5W/PbfPh1Db7Y9usz+1yXrXT06adH9ft8rhp7Qk/ra0OjYJ4DWX30trm+dI2+Pelte3zpa1fzm0NQQklaiEEBUxhvWmn/a4dfly3z//Ztr38/aqt/u7Ufvz7X9q/+sPP7cP2pR1P6/bL00P75z//0D7/Tw9t9w+79vBPl3b3D4e2+fkXbt4+H3guEGAS6BB2p3O7nE6tQeidzzIi1m11t2sXrIvv7trh+217+XHbnn5ct+OH1s73q3bZrVrDs3i6tO3nC5X47tdz2308ts3nc1v/5SUUAJ4HrrdZUzCc77fteL9vh+9W7QVr7PsVf87ft7b+8dzu/+ax/eHHT+1ffPex/Yv7T+277aEdz+v2l5eH9o+ff2z/+M8/tqd//9C2/+Gh3f1p1+5+OvG6MD5y7qikQzjHM4UC1IbDsO9X7Xi/aofv1+3wN+t2+mNr6789tfsfPrc/fP+5/Xj/iNXQDsdN+/yyb58+37fHn+/a8ZdtW/28a9ufLm338dy2jxc+w83TOZ7j4RDKAwpDyjyMgnPs0TRO5p/Yy8vbf1JufpavyoUbx6sGpr7o9Z8yQwYL9sh22xoU8P1dO33YtcMP9+3lx007fr9pxw+rdt6vtD+xDuK53P1yapt//7mtHp+5Ry9QqHmjSwPq87Eoz67ufcFyXjKmfY4rGRz3uNpu2urDQ7scT+3Dv3to7znerRTu/vRMy+f0r//Q1p9h6X4MS1PCahDguUigQb5Q01NwydKEpZIWyfgTVt84KRcIB1hGd7t2fti1w/d4wNv2/Id1e/lh1Q4/tnb44dKO351b+3Bsm/tTW60u7Xxat/PLpq0+btr+L+u2/2nV7v5yaQ9/vmv7nx/a5vMhLKnVqp0+7Nvhb3YUpI9/t2pPf9fa8YdLO384tbaHtbhum5+3bf/ndbv/86V9+Mf7dv/PL7RCYLUdvtu2p3+xaZ//ft2e//bSTg+XUApneBGruPbPl3b307nd/fTQth8PbQ1Px5Y5jmFOoNO6BWfPqVpztvBCqIQFfbqDIFy35z+s2tMfV+3lj+d2/rtD+5t/8bH9/fcWXM/tfFm3nw/37afnh/bz03379HTXnp927fSyae2wbu2wauvndds8rviz/dza7tOlbZ+gLGTJw+rCcsAYcO1dCLDnH1ft6V9e2vk/f27/xb/8qf3v//5/bP+HH/5t+9fbX9qprdr/fPyh/fcf/6v2f/6H/7b9x//3H9vnf9i27//hrj3840Pbfj7SCvJ8wBKiFUwPEdeV5UfLPZQ/5v94v22HP2ypzHHv+Dn84dJOH87tcnduKyhCeA2fNnweu4+rtv8VwvIS9/UYgpL3s27tvIORsaYgfvke62sVa+zHc2vfH9v9D0/tP/vx1/bf/OFP7X/7/T+0//buf27/1e5j++Nm1Y6XS/v3p3X7vz//y/Z//fm/bv/93/+v2s//8GO7+48P7f6fGtcBlJOtXFj6513xpGCx2u7aNBomUEq4/uFvz+3u7z+3//Lv/tL+N3/4D+1/993/p/2v939qd6tTe7xs259OP7R/+/Qv27/55V+3//Gnv2t//vP3rf3pru1/Wrfdr/EM8YPrb57PbcNnCaUgD5yemdZktcblZcXf9mgXJPosL+bf6+fo9c5vVKFS3qsfk5yg0bC2kTQJYHhAu007PWypEF7+sG1Pf7tuz3+zai+UFZd2vsM9Y31v2w579OcL9/bdX6AgDm3z6aXv0Xru2SiuSmFQpPPgX1Mq0/irR1YMRf972UO53bXV00vb/flT+6pKgQ+XFopcJFti+OFCgHLwZ+0iT+dYUoZV/VVr1kpBr4WSsLaXdaxJ5eX41qpdTlsu1vXl0raCSlanTds8r9v2MRb86R4beU9PAYtmi2d5WLXtJ2zE1va/nNr+52Pb/+WlbT4+h1UKBbgGzADLDRYSNiLgllVbH1fteNjwvBDuFJAv4TVQMD6d2vrpwHtaPWN8+3ZZ7eJ7D6t23sU0bB9DIex/Prf9L8e2/eW5rT89txUUEqwzz7EfvuaHc1OUxMXKVJbloCAw3u26rQ9rzstlBfc3zvmy3rZfV9+148um/fJw3+53B17u+bhrn5937fll1w5P23Z+3rQVFMJxxXlbv0AxlHvWfW8GpRBzTYGL7xzjkZ/3rT3vdu1P6x/bv9n+63a/OrT/+e4vbb8+tp+O37V/fP6xPb7sqGx5HjyGowQzLHbiSNCMcNNgLkt4HQHvCR7hZ2Ku1hRqrZ/rAK9AG+g5MEJ4PtvPWA+rEI5UCLCgzxSQcd2ApPj5He4HkCLmonEu1p9X7dw27bndtX/C5RuQpXX76eGh/dP9n9q/2n4iJPGn03ft3z39Z+0/Pv7Ynh73bf24bpunVQhjeCtP54TFoMxhwBjSq3sKawvXxj1BgOGKL7u79h/WP3IvbC7n9uvDfXtYv7RTW7efjh/a//fxj+1Pj9+3T5/v2uXzlusPe6DeM+53DbgNc8m5sycWMsBKwArgWuBNQmAWhHp/UBwT3Dm8NsgToQOvHJYb3BPVU6hy9bxtG51nB+W627Yz9s0F66W1EzwFzCjW+GOsl1CWp7Z+ObXViyA4zMeVYJ7uvc7XfK9VKVzpvEmeJjpQ5gL727/rX76MZ4cxvupu/RalQJglFgUucHl+bhdAG8djKIe6GHQn7+LaG3SCoAArAisBaPMq/PK7uoYtQSgLQDT7XVvvA0La/rJrO2F/57v4wUOGpYxNxvnHszzFpt/9cqAiAGzUPj0G/LC2Ulq31fFIl3Hz8b5tHz+03edde/5x0w6ACz6sJdwbN9Xdz6d298/PbfPPv7b2+YkCar3dtM1P37Xtp+/by0/7dvgAGCeU4PYZyuDEMWx/fmqrXz619vkx4DrixR1K8LwQ9x08h/4a54J450ZKAb8D8gj8fnu/a9unXds+76gwN0+b9vJp3T59v28f78/h+eCAkDms2uplTQWwo/AJl5rY6zFgI0BIvPfPZ0IPUAq0KAkhCe/XYoaVvfu44TW3n9ft+eOH9v98+i/aP37+of3xu0/tAfDKZd3+468/tJ//+fu2+2nd9r+Ewt79HMqazwbn3azber8NwU8rNjYpYz4nuPWNbjQ8SBgMWwhXYPaYK3z9JSACrkHc0wFKIYQi7mX7+dQ2jydagxACxPXp9eCcG34nQlJrzgfOd3xc00g4/bJpnz/s2r/7+aH9+x/+0P6H7/9V+8+//6X9/f3Htlmd26/Hu/aPjz+0f/jL37SXf3pod39et7ufLu3+pzBMNk8yuqTQI57hzR//SaWxi3V9+GVNi/bpadc+f/yx/dtfHtp/+OMP7d98/6/b/fbAZfJ02rY/f/7Qfv7pQzv/vG+7v2za/T+Fh7wXXAZvjPdLJSvlVGNCkAeGNSfIqArAcb+XPTu8PH23CsJ3wc+z0CwKwGs/jcrxwD5Z7bZtBaX8fN9Wx7u2ed62w+d1O3zAnEr5H6GoW9t/PLe7n49t98sL5QQhJOxRKoUqy8r10oCNeelz8E5ofZ4Hez3D/XbIOMex3zHc0R6fEDH9ukqB8Md+S6G63m9iIiGsOeAZHyzxgKW7u1ooEhS8HSwyWbwK4nLhSTFcTXT+G+40D3welmI78Pf186GtiR1KOAKXBcwEgenjjIDMsa2gCJ5eQvPjgHAtHgsFDB7+5+e2VXAUQ16fYH3H0C0g8eOA9OXlJZQorSIIpk14FU/bdroPa51C9eOhbT++tNXnJyqSy+NTu7zgPhDgHRcRPaNZiVIhhGJA0Bj37GdlDJyxje2mrc+XBr0oCUfrEoHPzfOK8M5pH5YzxgnLnl4BvABao1KmVKgR9N3wPSgCfYZCQz8FSnCgnNYnz7Fp69OqfV7v2z9ffmy/fP/Q7u4PbbW+tMfP+9Y+w9Nb6RphlITQF46L+8E9bC792RMDP0pxaNM4KJ4QUzwrfOAMpXfuMRIqg0d5B/D0sDZgFRIylXei9UHP5bCmEgwrPuJU8Bxg9R+fN+34smpPL+v28rxtj4d9+/PDd227ObWX07b9+nTXHj/etfVnKOa+dmyle51fzut25rnXRbYJzsHYqbSx5jDfWuvwGM679svl+/Z02LX97tjW60s7nVf0TE6fdm3zadM28IwQQ+E9Swk+eR6LQqjWruIKoRDltVUvwcLPRr33alUWGVueFMsttGFJotyQNSGYmXnQGhI4nBRioel5leXO2dqs2xaKF/uea3/djocI0DOuIA+YSlJzgzUGzD7go7gpfp97s8uoVARL97l8B7fl5nlOHJWxKNmZ92hvDvGfd+aafoFSOLXz3bYdP2za+ikscWZb4CdmeLgXTCkzIq7ud3wg8dLsBfSMGkwqLTrusppiFMKaD1SCkD8bZH1AwipdCMLD1g0f9Oo6BuLzGiPF53bbZeUjy4ib8Bl44rbtPSS59l48dLVt8WCDYK5w/k+PbbXbU4OvDvu2edjFIjqcGb9YPb601fNLKBL82EMYptFjCyiL9zTNDeaAc4FAmj0KvcbXkf2DRZAbedtWx3U7PgasdcKNpScVgt5wUM0Oi+wZZwM5O2p69sKbA4+ODbV+inteH3Zt87INBXfeteMP2/by/b6t9lCmG3oxqYx4/RN/HESOZ4jnColsWCOsWVpvEJxlR1iZ0fJ7xu9QrvG8qNheLm0HZUDv4JSKPYUAN5nun89tI8Wwos7FAWjw8nwhqkU46GXdDgcoiFX79bBuT9/t2mZ7bqfTqh1x7x+3THRggPcZYzj3awoeQvwr4ijnIa4WAX9kzcUY1oeYh8s6tjcE2/Nq255Pq/aCuAnwUpzlcdPWULjIiivKKILLguAAlVmQvyWhvY5qnLGiI7mXyrmGeMIoMP9aVn/KBssm3PKVHC3X4l5ctQYD8nHTtn6QXNvOHNMaV7Ybz5lrvcC7oU7o0WWm5LAfqpx7x7wuBuALxFJfuxGTea9D8mVKAWKZGRaAHTat3e3bChYsU+6OzFIZo/1KUUy5+tbAKizkP/0dZMHAOvK5i3uUsEgRghaAEMS07Jhf1mhmcUNDs4cVCVgGqZir+7vIWLrT9/l5Z1loodMasoV4bu3p0Da4JhfGrq1PwCKdChZCJ77je5EL+fzcVh8/NvhZ2Phw0eFxUFgiJY4K4dAuh/B0RoXZvaWw/qVjHdexkNS4YcHgx14E5ic9CJxbFp/x4s1hS+gDWSyEtdajRxBQUHlUfg75GXsG14+W12JK7yniNIByMIeft23ziPStO8Y5XuC2w8KGAXIqMRoJv0j/hFLAutM9YC4ciM/nhh8IVaeZduVEhXCwxxLfC88B8YpL9w4wVikywndM2TQUhlTjeI/CAtpVSpTpn7r30x7WZcReNs+bdjiu2gviFzuNCcrkI5SfYgm09kMpALrJeBm9ZmciSeA5boZnrPunR4NAFdfMJgTTBh7Dqp3vzu2srLsVMHJ4CLruVkHlQSE5mD9k8Uz7tR4VJkn0YIR8B3iovu5U4FtxhFk4zjDUwthCMejg3MWeyVuyQOef8AbXzLDb2FCUi8MUYW57reHq7XjsXn+2kbH/Bk9ouudZMV5N6SvxlEGZWAhMxrnPsfQcvkpMAUoBeDzT3jZtB3z2edcaBBcELK015cgvSH8u6hTy14O3Vu8Lorh/NfCi1yInvuBoyo9PgY7z0JpUVoA/C2G4v8+J5QaHtbfftwu8A1oI+L6xaXwwLPWYV208jA9KEdtOggOCBtkoDkAGDuvxSzDxdOd2fnxqa3hAdsWZe3+J9FMGleUO4n6GBdUfcIRSFhRGnS8uTuTbw2rpmxWKIN1mjv3cdrDeEV+4B6S1bqd9t5B4v6p/4GkZvOv5//mZkmm0uMYvEmDPL61B+WGTrZHlc9/a5Q+ttfu2QSYY8vexrODcwJq3h2LrTAoPzzi9RVt3qsNgHKY+M3lsvt8Lz2urJeAsKIXwXuwddK8jYJTybBJGkecII0AGwQZwhRQo4hdbCODndTvwmmt6DAiy00M5A7qBcHZqry11rUFlkHEPYW0ZCvGtGRYjlIFctPgd2bXxmU0oqYtiadC/a8VOPkXGGK+dSkE1OPaKIqvjOuslIdVpL1/t/hpYrvh4EXqcyvP7gsv5nRmGtsk/StlRHMpl8H5KT157fAWjLIxKQM4bpgBjCcS/XOM+oTOaahC7ohyD5zApwFSCNUGnx4nGzVM389Ik1/sed10Yi9NpvpqnsEKWTARFUbgGKGllb0E4K4uaZmhouDUphjjh9G4NTDvFqi+8HripwaMp4MoNoy9yPNiYxxAa8BIw3vv7KApKbPvCgOEZr0EwO6AOoYFNLYXAMdwBUlL1jwUChNvh2Na00hvjLS54oxBVXUBAODAbldt9OLTLE/I2I5c479xKarNtDZ4L7g+Cr2YfUbiXXPk5e2uY22lFyIsIJYw5iqIrBNARLEPgGbGjLeJHiCnIQu1KWQpBhWaRItk/Q8VjAwv3YUVYhhDK78gYy+V0DOz1+aXtdN7VZd/OjPmswvMiJJVeeb8vzAEUP+4Da8AajM++pPBWxSlvBdZwhS4IJ0EJQCCmAgjlQm9t8BaLVWqP1nEK/Cnrmh7EEWNr7Qzrk9b4mh7J4SVqW5js0IpQdkyA2T5SRvKIOZ+IFVxvrLRM+Sz1vY2LzeitRIwB8NIJsa+ts6ysDCZlVK59da2yFkaBVyzfJRFQcfTy8656pPn11z6zIATDIIV3jb+8mKYsKAjVI5Iqjm0lGBr7GXNGkcJnJUWjzL4RmagQms69vqEQfO/FuwjPzxNYDeJ5rMU4K/MQqEnxGOa5/tqeQlaJOlcaiwwRewg7YvgI7EbV37Ib47up/1bMflQICvMM2UiZjto1xigQ66TKU6A1idcw1j2qhHfMTuIDUbbABa+7MtHKwhijN/0OlayoMA2FQwvueIggMC1upMG2djlsccJ2vsBt1z0Z2kLh2+VAAcRq2RdEpJH5sOt1Gb4XBrBR7Ww8Gfchr8FzJrw8v+MsqauFVF3/Uush4RyVuKjF2Lb2tG1rFeit4TmxkrXkeVsh7OBx4R7hUYT1GoHNuE7E9C6JhQ/rEUIbzwbXtKUFAf/Tr233cMcMGxSAIfMDARvOlyATyv2q+BVvWQHCdNl5tcJqqq4GEZXup4ACOI+C0FylXqEBK4SCldc1l8kPVugRAlDgN6BBnmYLLwwCBlsukgwA0QHlwT3BG4pAfYfgOiRB6VLw63FHVcSE3xOUhDRoJlUg0wxZbjvcbxgr563hsig6zOpwF6j52jbChmM5i2ew8q/em4RUW4KZLJxfEV6vxRmWPIdp2OFdF102CNcwAgEtI+V6tcW/W8ZoCNdxjwkS0j4IxaB4JmQV5V+Rc+lpObaxoAz1UXqNg1ewsIfTKJwUaIAbg42YBvoXxmbeDx/lhpdsYnVvZPFEXIE+lj4zp2L5Rsr58umUG9b548bsAXRvYLSCq+ZEfcIpvSdOGITOy3M8YFYs7lnpzAwkBIAoT1XdS8s+Xo9NKBzfwWsrpvRO9FCRVQRPQfUa3JB3+7AWL/u4PVt6FGo4X1Sq0ZLGfMFj8BjrNTODCEF20SRofjEnHEJNByxzlIF3jqMHCodF6GQA4hsQrqI4wHgwxgPoJ3YRuMe14e1YgO82VIAUivSIukKoSsDJHiE45W1xemXhM6gfVeJ8mLDQPj61zfd3bfOCGI3QSMWQz/BIsOaYUSVs2HNwMiRQN8MEc3jtmNbAufYWhBCmVDJF0qarby9NAUvDVRC68lCTjsS4syhW+DdqQ3BNnGqnYjsu0yhGo3dgCou+SWRYhOfDeVYGFxWcx2iFbZjJFmMJ7CN+AqVz2YSiJlzmLDHEvwosF15HiZ3cFArF+Mv9fluEzNosrPdJKaRseCfWUb/z6nsL8Yjh73i+FxoNqMHRnnbMCF4C6E6wayjAe02VkYyBsmMW5LduJ4W6YheE6fCMPbfXnsaQ0lvg9rSJIzm//dbjizwFWhLeG8STw30iHv5Sag6rMPdNSGgu3pCPkmvflpSBFkrAQZ4JPVBmkMhMA0SDICQscYwFQeS7fQg2f80aHJvaD7TyBXE8E+Sh8SaUgFw1Zl81pY1CyAMOAaeRLHfCPgUGg/AAlIV5wxeNV2sjmFMnFYoVLV8eaS5yrBJ8mXZqy74INHhFmZXD+ZJlBMXoFAl6VpAWULASQBgPhLCeTwQ4JRR3RYjQeuqPI5fk8Et53mnJR3IANwO9lRcFSrXGwmEKI2uPH6cTC9qyJc1gei9S689PazHrOfScVfiWwVzEkOp8L6U+a8MOlBNSUhDyUJDmgQqYSjEBKpuooSBO/QKPYU37gN4PU5r7PjPvVAr5CsFJ2WVWS3J/hSefUKS8zVBUAW0Q3jqhAE52TnE0x504QQ5WhFMMoWd42gSvVu4s6KugfIflWoXl/Fouqfn5vOMcgpKq0AwotgSKTxGPYoGuoDTEgVYbeAtFANtzTkVcPIKr+Sy/zx+ZlWAqzKXY+oISXZifGKISEVIvfmX4CLARqiuZGuiAH46K9ZeX6ubsFgfATeX/3zgqUV1O9IyTG1P3HDB5W1OB/yszhVe8uwtLVEFdZuIQK0Y0EOYnLLBVa8qsNZyQFqgXsqCG+LNYj+XguRG9RBAeEBWtQQuSPjmGeCAQhuwHKSkLO5OS5TU1N0PdRImrJLxDLyFWnhWYUzgJt+B+abJKQfj5CeNcgfSOWVVIc4yURscv+DFlcSU8lmSAukcJc6Z6YgxDLCBnS9eqizViLikEfU65xoQuRW4XSi+C6MZu0yBzskDSG9j7EvxoRYLxs/JZqcK1YrymLw4bcJxzKgYKcQvJSoom4kHFkQh80vJU/AGXg5A2SlA8ooiPWXJ34cc1nPBWUX5UJsho0hhE3xAKdEw8YHKBzNow7vzjCviFQq9b1m5KyVeOCg+nfr2RjZjrfGnulz576706rGXlMHw2jc1zSSwIz5Ee3wZZirF3o+bmcm3IGqbUnu8GpvbbFx0xD6+m5d5UnFOgeoDnv6qncGmbz8co5hHZWec+meyMitt5dMSNMTeCQWYMcRL+iY3Xky4Gacp5Sm40A0X7SDNlMAincn47DigOxRYYVJYwTgUgOCFTO89RCRqpj/JEHIgrD4VQECAlKCNMb8Jn4RVkeijGhwWUAkmB33Em5WWVVFN7E1nN3BdjMlkWhYzc9VycmoawHjEPETQPzF4enhaVs3kikFaEYGXH1L2HRY94U1yTuo6WfrGs5jWZMQ8peJwwn8Hkrckq7ZCOoQ7VIUixpRGnVN2Q34bS9L267jI25Ir8y7BWl7H0Mv4qOGe4VBlJVxtDCsM1HlWZBi9VpD8iqWONuE2SznUYNowaF4j4PEhOwK9RxU6PQ0kP9K4M/1bj3UpoG1QOyDZb75EavaE3Q+8yrfsKZ1zf0+gZlIykuv7zc+Mavz5ew1tuHFdf8UWvjcrRS7i+/sVGlNhj4X2txRxsW4Hp1/V+02Mq81KV9nCBGx5PfS9wwilIMMdlFtboa4rvq3sKWDDPJ5b+s7hFaWuYvLFSj5/uFXdFoAQkEVDEoBh0M5mCWjeeJ8gwSObjW5iME0LaZgaVEUMogXAcFPSR245Mm/a8zc9AiRiS4KmMe7twDPf5KO8Cw1YNAa8J7B3DZAUtUh1fyCxp+uBMY7QVsgmsntfCeeJj/eF5g9mjwZhd+FboK4j9E76rMZ+CdV7mvwPjSdsOQTHj/b42az3WVJj0dlQEl8rAfxtPl/tsa5Onwe1iEznH3cKsLmTHajzPWWzXBXr/bk8FjPTPqD/gnKQvH4yuYUw4H92Cu8QZai1DfSY1NrOEQddnpDntikoCgmm/GuO0tqsS6QynMW+IMWSNx0kxh10E89eG/XIP2PO7HhM9Ai1PKhYoBGQM7gv1u6dVcB8TWFcBKyENeX1EIR5qaaAcYMBV4b7ANZRC8Hq6rpRBEWo3LWDP/6xQ5/m/us6UdZQW+vuVSyydSxp9lFlUDBHTQ7XzWTLqOhHAYylGq4PTdfAaZ95/hdyq0eL7ms99a+DTffbrzjf4NZXCHSboRE4UulOPEKpKR2UR2KgYaKEyGm9PQJaotzE/epq0d/3hLHSrgwpBVaouqW8jZuxKZmbzQCkwxVTXxvcQY0CFMNIhQQfsymcVcwFmCq/CD7Bcb0hx1Hue6CSmg5wVr75gkJ66Jk56CTRCMpOQpFKyUAQGPdVcxHddtwABaurvVXm9uunFivN1aDkrWMlMqmJ54yMU9pqHVD5lLCbZS4VgCKJ7CoYoArYQjLXgKcR8leAug7XCwTkmW2QunFO9gorX6JXZC+D5DJuV/PFMFKjCQ4J1WLNViC9sgAXLrCtXjZX1ASNRHN+2FVlgmUp9beiJMQbOo+aOPzXdUQrRXEs5Z73HQc8SBKV3BLSD56n0J/ESMcwHxtbzpW3ukLIKqgx5KYi3kHbmy9Max3kbUz+d6bPoQVbI9upcZdZnz2wQnvMTsoHkt6eaqPx4icMJPmKKLyrEsYcxJ4Joe4FmQTp8yfnw8ze8feveFuMEN+aieqgDulKuWWXpazDUX6cUDm3zC3oDnNoaHEESri4Qy+tSmOLWo5oyreKqGHKhWTF0V68Hbbp1B4EYeLhpDQC/9AYgV7QOzJYpxUxOu8TvCJyKE4gPCeeF54C8eTx800J4UVWLQBtRCc8aHyxdKyBVQhtDM0upvJcViASx0FzvgMM4rgKXfJwWBlq8UDb0RIi747PhtVSMmVgxBXah8E2LuMRJrGAHyK24qgXKI/VEjVdYMVT9TehIuHRazkGY55iAH3GmI1hIQkkDUsNKBP4hq5sVx+QSCis62EyjuIrV0Mr6opfme6geQd2fliPsq6CYQq1UH5RzDRgW72LERLvChRJYn8MRg7AweZyymwwXRTKDBbfklJ23CSJL70vzHDi2vGsQEdJj1L6gwaGgt7KzHPR2wyJD2lmRrsY+ho/CSENRamTebA7on6E9Ys8E1xK9Rm7XYZLfch8m2Kha9jPUVD4Yb43nyXz+opX9udFC1k3GwutUF375xnFhPM9GqIwWJQ1YKVx5MwsQ+OLhzCLHHt5z5N585ZwDVFUy0255V19FKaBSF7AI7gMCCkpBaYyZK+6IPNLsqEmPwQW0FDCGa0Xenp6hlHuxfs5FcdbcDMZCYwfWXXmP0lOAQrDFb6gJDxBCWVBR9UAoLLAAnl/a+TGYUVMxWMFjcSRWjet16IrYa3FVKcQdbFTx2uoUabGr530IQgt4CqKAY/xjT6ETevVgNGESWqJR1Gb4ZUU45RQKbyDwK+5stfjS7TU+My2cCnmYRDAVxfxvyRISkeEZefKZsXItCBhAN3cV7yEUrakwSH5HSmsXlqHA69I2j+gWJ3JBQXe8L1WXjxtjujbTLHvF87Cx816LAZMKoVT2biq0F8KjGq9BeaGMJj4Xe5Q1g6d0lqtSdehiNsMCiv3AiOI4S7rqehvTqr4S4SHIa/DpYEPoHpjFZf46BZrx+/ouOJsAN4Hs0V5rduoj1HRDuMze1pLlPqzB8r3hc68pBL9eYlU2MIf3ZsVQ5jTrBvzaeD+X9LALXQpTU5VFpr3Q7zlPJNaGbsEPjAz1PqRURgaHGwpifn9hjyad0FuC/51K6P1KAQEwwB5UBqegKJgxWW92W2Xc8BDoVgQh6Fh9Chc56scXBtuttKSaNX+R2z1mwUjJyJmI8fieq0uxyCFAgJP7vZoOelGNAcYPoW1hVZ9nKh9kBMkT8djF5JqTX70FMbRS0YBOw4VbGBdgrtlTwP3A0yoxEcQPIg2xBum1CeRJsPpa1b3dI8sVMS6w9KyDU4oudSlQq0K//z5bhuOm0Cvd4h3osCbMtCxiKjN7Rxp0chMxrTeKrJj99vhCGmDCf/ASEPsAlNReynPS+uC8lkA5n9ONOEIKYCtTj7msEQegNYdZ8eyYAr5LapReFT0ULzkoXyEoxyIKIno1z543GFocm4SJoUMpdUJGoM+u9CSE4EQRYsQJ/QGQTowiVGxBKAUFm6GM8W9UqsswsVEi/rHLLUjwtePyW4TU9H4upuuUjPG0qolZulbGjJaF6KrYUUmk6TWjokKmcFf4puwZK4NK1zNgV/1KVavdupHl1xaF/+uexJcc788+Ar4JC/zjJzXXsXDqgtuFTs7QSS9CFMfRA1iKQUFhVggu4bp54TErZFAISzc/PaD8gUJAK8bkNzLdrwJ0FMTnqGlA7IEQxXOmndL7QKyCsJQ6w6HKWRZqKhEflVbY+eb4LoQY6wECcqtWvekjaOFdNiSnQxFZO+7jO891I9YKZilQFvAJWoD7+6rl4LnpmTtjYE7ZUJr/nknj88OqXNMjSMFT+XgyOGzLugTnbGGloMZ6Ut6zoAHTWDMIegz2UPQ1QNOhy6fPMSZ4XlhT1Y3HfELxU/mr6C/TCE3zXH7ngKe1YiFshUDoRM83uaikEIzVu5LZvZqdwcZ9UdamxhKCOhSe+aJMKjhg7UUh1P3B+pIyfvfVjsI41RVJiAWzp2jK0WWQn4lqatpMhpG2Mv6yTqIsF3uWrlPx4F6FkeoRczguwSlwPRnUg3Atl1zi+Lm+nOlWyufya29LyZWNlnn+vXZLG9TBGC3XS9rwen83xnpzEO9VDK/e0vQsv15KqqAQYOIiMePrNS2V7nXJNoKrjSpKxh1k5chaymwYKAnwuahJevDXeKXWlMXwQsKaLArBnyEttlMsFBy1MGCan6qW4TEYE4QFvlPh0S6swPWHfVAgPB3a6iN6K6gHQvVAjLGfwkq9slAwPluKxcsgjASldERDd7j8ynDwXsiiKCkHfFaWcHD8RFrt+LxHmIjCi78XGGReGCWnOk168TTlZ3lDdrf5S8436zpwn1tVex6RtbJqFxHYRZHU1HOhYNOdYTLWxKUIW6+rYP5kVCqoGEhlfWTTIXoJ9/fysoKlN0NSSWeyH1NziQ0LTpxhoxpLMMSQCqH0IY5S9VSSkbrYg/RWFNnnIQ2ayAVOcapiNQhtZP2wYFAQT86VhNFgrVLh12df14xiCmq3ykNU50whR28EKAkoclVUg3uJz7JVxRCB7mj1Wbyr4X5uWL4V03nvUWOH9fd8u0Oy7xXob1/TvxQ5U95cDddxPc0Ee+Fw4ZqN3CuoND4csqlQ2AzHF87XreMdevK9xxewpGqRWAjusaJgGUGYi4LaG84wEQOfm0ynJMRBTFb+sdNUM7FdQdAMwpbXB2uuaFHzs4RUVV56V05oNwlCtaFHq4QONhI3B/qz3oXL7ZS8DTqSMeMAfnUpZCsLIWio1xEoHbA+Ce8aGO9mXd7DaoDIJDAdlDQ5HK4PQXeMgGo7iU11fjaDxdU3evzrBWkh2CvOc+MHb8Vg0Y6/W/FIWCnwxkyV7bltiGHrFBBsoLlOGu3e3KZz7UdwjFAiYREo/G3nWXLoTOfagLkUfXCfX6KKvngCac2JRJAQIbw4clkVb6Uq6TpXPZg1WIZz1ll0aqsYuNdfr+x2UHKAyXzu8hjis+UnCskVCI5/u8cyCePMXip8VE4Lri1HFJtBCvnmyTTgIDuM7oMgenNvDMeFejZUEdAVZs3U5LLGKtxV9mL8XeMH0/pctJC7MF48bgi+rjziu8spmdPfnNK51uhSGl977jsVe5JoltjBaFRMSTI4XLOT35nG8N7jFdhrUFiDMfjl13o/S2oW3EjokzJC9M2cN1XL2vJ1bQALoTaqQFbefR24JhQpmsDLqVwSANXGpAXJiPXVpNO7qJNd8+lVEcq5gOBBANDzCuWDPs1QCvcgX4MF1fsGgM2S/ZshmNnpSwIeBzYghKID2lSM44xzLtSzwRz8A7EaIYkoooNliR8qF55XwUjHGPbbtoJXwkzUDtv1i0158YKWUsjXgNS0eFM5FMVlSKvyuwRDJ83onit/OLVNiTXwGUIZO1ef6aMmnHNw1lQS8qoI7wT0CEGOew3vrae4RuaMigzZHrW66lpTONf9XWv3d+1yv28XMNraCjTl9bCgp01dg8tWIC5e9NpKrVd+Kp1IPlvFoko8phf9mVKlNCdyG2l5WEFM13mZhued11ViQqHYcGDZ3oYVAvtCwPN9OZIEcHO3aVv0KQcjKyAs1Q52xVCEipmGQekiYy9jchNcEnPgxWAhdcN7uKUYPG+JFPBDMjLj94WTTaetEnEhA+cWLj8ohtaVfioEZ+25uFGfVobYlVKo92c4K1+fDLm3jkHol5MoWL2oBK/u72srBeVJh45UcZAECEi20FIRbn0UFNlA8uqHZV3y/zMoK4vQm4Y58529NDfZKxMdz28qjCqbkfAUXn9BSqpohe1dQClsVu20K53GJISQbcWMITj/j0dyGREWwGGB62wnXMuZUb570UoMlmMpn/fcsQiLfD+7UA6AlpSWyn0BpeQ029oTtx5zdbdd4EXBN1l62fK0CDZ5EzmPrkvA9R2wtpKFkNFzQIOciIm4I5hI56QQhgp47xDMnzzKYKINyu4IdBbDrXLTDFZsdLQjbARadCgE9eSOMXUSwb6YJ6MkFUJfcxE3c4tFz1cncXRqbuLOXDc1mF6Nk3H9ZqzFnEega2Y/h85WmoylA9xWUjGdESbIyOmrPCf2gxhQ2TkOXsITuvkFo+/6acve2OgGh2s6dTih+poq6/0IBQkv31l9Zgu1lZ5rxgK8KIbfgCrlcx6gJD+8UaC+Kg/rPniP1b3y8xRBnqqbkzvMPyUzsV9nOp9pYwh8FPhrst/eH4Sf4a6+JvvtzDxUvyd8RBbFYpVbMUSCtto7qsCK2SCyQlPIdBcvBeRw/2HJBhGUeRemIFSZl+nL139P1jQ3oRqWMAAOq1TndTUusVghV1CCgJTQGAWQEhhRiUln0xzFMZgZtO6VyY6z4NKF2z6FjQUOpYCClvASWFh3F7nQEK7FujF1Rc3OGRYjH8UUqK8KAcdS8L3OlyGQKgQrEyipvAuniwrIho5fZzUZsmLgbVaPRv+pcJoFpxQfuXrcqyEIZQchnjEIeQyZSw5aEcQSoFhMacz7WdocS9ZjTw6I5j2qIk84dGwFO1pdFW9+S/pJgUKukPsIpwvYMJWBCvR6tzcZFn5u2eVwPC/iA/A2oj+0qLvtJSBjC139QPF+t2ubu1AM6AYHGIktuodzTjCtFTCE46oWOCxYoO+VR28pi1fee5dlzHO84qXM/86fv4iahrAe0u/hseNZRC0Qumdk1pmMAMcX8lx1r9qArfCuu1G95TXUdXrlnfU9kmR/fG00nt97fJmnIJqFDEBCEBELFhkZBa+a2pD+YkzT4pR4cJkonXendRgw0ojDlwVSXhvSvqrrLos023f6K84UIhle72i2lFqXrrTIzrqGn6xAsohuswiL79tqT4tTNMSEI2x9Il6w6xg7sHI2LDq3tkPspWz8IaPKWqvEADwvdXwa8gwhDZZxNTEo8GUBuYeClAEb3jgQzypX566HNQvBvHarzbstO3sB0uA9z3TOOcE9Myu7ptFTCGoGwiEicgOMxLx7VJTiM6glQWZY1qCcI7icCiGUe0Cbk2cyW3N+zVxKrNCHx1ugo0EJl/4SGYOYBFhxGK8EpDO1lCYKB9oQTvRajkyh7PzGmgdlqwma5cFOfbpMZi6ZiE9KAV4CFAHqOh6fo6kTFei+be53bQOPgYyt3dap85TekGM3vHjH2bOp0WsCx4JP3usImVSYqa6NSS4Mn1/47OQ5XMUllpyC2YsexnzpRiknxtXNkBuCyDkv5TtTzDLHRG8Bl7B3UTxGU9dbnsxKZD5uxQrKPBBOkpGRr38JTPVb4CPemKllC64Yyk5aCgs4By+BwAEWC34Y7LhJB8VQb5qzO+aKJ8tnCQ6GdV6aXdh7IfmclMYD2j9aGBjb7UrL9Are8INwqVauKTIIBb0Ul7GnLibkk9AEhL6UAl1TxVCQVaO4QgTb54fg/MeJ14jvTQpw+M71JshOabTEHQSVJ1TiCMStycMDIR9GanQXO7fVi3ogcI5j/qmyUIWsyl3eRlbn1hTamMeEHgqnEsn1FPBMTw5KQSm9VqJZNIlnAGp0xCOQYUbG1BUV1aj0q0Isr6VFKJ4pr5NcY6W4L+dSQkOKuG710ZIrazWVQXgDju3TRgAlFq17pY8ijiXIk9l7GBP7Vyg+BW8ye2sHtXOuGEB64idDajUzBp+eVLn/Qq9hdb9nl73NMxSDrMwi5Dj3KqYE54+D9tVr+CILNO2Ua/gn1+Xw2tVCXhaKi9j88t9hoxQIrhisvVVw6/uM5xatyBE9FtSRzdlGhhHrPSQUWWRF/n09WakYXlMEeRsLSMBwm+bukkcyQE1v4Wy/BT7SyRlDSBIzgY/2nhxfaMGweDVYDFPBQ/MYRfR/CQObsDlYJqRirlTC0/g8LnLilP7GbLiDjXWIjVQ4hdxbF5ZVUhBAjjG3Wz904/XDByy6UQnP6DmwjYAiYwvrzqVUW/VVYaJ4Bw8LIDfrIWY5Zfy0tyzRwQxfhkhSIRR3kzBwPeGCcrGS4OsQ2pjDQl1tmo1Vqd7W90K44KcEKOfNPVjvoYVnnh6+IyUMJXA+oQ0qxrFpq4d9KAV5CiN+PxsgxYNaWDuuH7k+Fj7vuRmusZBtUwvd0GtD3lZQeCC1VwV6uB9mWTn54BAKAQoQ/xL6jGy4NCpkta5JyxBjpOJha1E0ogc0eYiUXEJPZ6ZZw3tYP6MfNta4axukpASl4mezvW6iFcZf3dcL6zE9hNnwmw29halemvdbVvJbENQrp/Q+6UqlwC1pv2qMNizXQVFD2NiJGnkvxVis47VdYc+Bb00ewyADb8zLe4R6eSbJqlAN2a+dfdQ1UbF4c7ASkFAIVpZUDEFlQWVgviEuzs7vT8ssaS4mYVas/YCo5vz7PhkU+DjnStarx0Drr7OaRr/ksHyZfvp8btst8FjVT0GGoVgKufFPxwjUvYQVH5kZtZFLt3JZfYyXoAxUbRupkeq2NhfWWXA7ALsvTWsMBS25gbPA9vv8O//zysMcIaRo5FL3bcnF9wbx5xFgrBTXaonJvxhfUIvT0iwG8Y5oVQr6EEMR08KVUIYCDkWsrmAIjDp/X3EYzO3aHhliQ8g6InwUfbojxtmznXpzoRlH6HEUrhNj90uZSkVJRybTxL9TOabyOUXfbn4Nc6P5ouwFEymUAix8xT9IR4/7L14CqVdg5TuBA1l4bJMqdl6jFprvWSm4s18fKzoGHgkrMeZw6PNLhSC6ElJl3AHKiz0LQRhQx1tHWhw3pfVi1s1rFm3KHD2zJcjn1fGUj6dhVvbY0jgu+o/+tRxJLyHje5XFoCRpOC1/EPRSyqz8TNdJt/RG74Tfcszy9J3HF3gK+pHlyw0JmEPeQ7ec8StgAH2eljMbHGSGCV02rD4LV0EoakOSsnII2OBgDUMpEPONy4XMpjDYA8bqi0KgsAb/EQSI+UfQqvAF/RTQkaqn9eE1Zh09oj/CIbBdCr/g60mjwmMRJs4COygHMrXu2oU0FXY/SrvM4cmpD8MQExHvjK3MUtBUwwZvVj++unmK15LKxJBL0JT3zBe9XRr51GBftIss9RzmwdK+oZdnGOKqgYs72UGQndp6j/oHVHQ7K0eprc4+wnN0YaKUAqEjxCIS6xdUU+dumI8yn/LQIo4gxt8yf8nCa+iB35nmujbuScjArwcFScTa1OzJdCbqDR6wkrp8wcIXXXr07Th18kaMD7ECruWXhN/YojU9T/eJULA8M+P0nEWzwiLNg7qK8XbifWfk4WdLRaufWgVeY2tXUFDVC69kIb0mBBfX7WjJv6UYxtqhBUVySzldiovq3+1Jev+6Nolx1RRC8R3F0AZoiZmbisMk15ufyXXiyPuV3sL4l777Bef7ouK1coWO5zteWBUDhQfc2ojcjyX61qRx9aBYjsylcK1c5HHD0kjXri/01ZRfzo/AH2cc4RAPwArBvZpd2IQWiVAA5NjpWpX53U8QTq41KBkgvFYZn6AzFk/B5IJygLAihOQNxUnqcQgvNsM4IrpbZ8pb4V2ywC2NdhYVw3uP175rC8m9HEh/qkfOsSv4jkwtewz+TlraXeDWtpIDJj9jwXhGamqyflq37ROykOJz8OQovCyIi2IhIR6Vr6i8U8FNMaClzeIxis48jBNDmoYGwwPMvhyD9/qaZVdhhfIMlT0FQcxLAA6yMDfkWp81M/7C+4zPRBpz9vY+xjojV9aqpP+qjWT29fY1fChwGhxTCgM66U/Dzw53VwWMJamhCrArwZsS7jYk8luPAoncsoeG/P0KodgAsty48rZXoxfBf7A/IWucmSaqG1Z/z5lpTgiZDRBBLkZaauLAu+75fcpwkLWjFv6dlIJvmlpzRApZi6ALV9cpgjWRzcGq51QMgHJg2QMrQBzi+Iqmc/ZDdfGkICq8hYPFZGqUgYWx37c1lAF+2Cazb25unvWJsDe6K3kxMAPkKQJ8QTMxNTpxs2rfi3oZMxJb+zSoW1mFjohHOhDu5+xN78wpQxi14KvCGksw0a3d4aNuiquPFUsXByxWKCa3xyyKgZlFVnb4IX1EX+wYu7mQRtZP/bKIcYLu4dhWj8+EhlBN7jGSLtvZODQcCgEilW/vLBZ1FDVRoN9TXa/C7bohcSwKAa+xL4dgwJ0MifWNuEKdf/9bnw/nrz/v5FDye+4dns+wwJJpTZoMEkpBAqdWzFcvpXqc9u6nPWODyBlP8MrOSM5ToWD1hBfXkPeBe2ikMNQa13wvrrVqDc9Y/GQoDLGxcSCDqLhV3BaGxziI1VueThu/kgk0auREL2ETgf+gMKlwsAsyZzhWSz+zIlWTlWP/Ao15S0bW9/P3+vrv6Sk4H9a4buANqi6etCY5gkrTCtA0eMEn/5FoerNRTBUgxRoXZBTR+unHtL6Zby8lBBgHG3q7K4yjxl4Ff6F4yP/q+mtWGp+uFYJZSWsXqoTOYrNmcLkuljonvNdSKcsMj7sQcIYzvChtQdtb8TmXCAHfuYAy4274XFk0VnpOBsjCOAeOe9P6wJuD6iQFEjNi+nMJZVLiErrgkHlkeAZQyfOhbX5VQR0+7yI4pYlmooAUb/SkdmV9ue/BOy2ioFq5xUvoabJRsZ8srPugGgk9srD28jkXQVOVQvFiaz+QGLM8FSs51sr0gHokMDgF3OtH/F0+d1+EMa8JW9lmkcCsQkHCMGM4/IkvMMZQPzukUpZ5m+9/MR5YIEpb9q+xnA7eyDLfURX+8XugBbdtollRCFqqUzdfv83XDPqRTFVk7FLZdDCevB+rp5CDsmKpXmu/xnU67Q2ExEbOlPp6Dd/V97/cPftypXClyaE9ZaHRO4hU1YimF958BpaVa+37zuI2bUR+TnUOLmYri4TucY0nFH52nt89HTh554U2n6siPGKX0HPxHDqj5opieeo+ld5CnXQvEG2iKhBKU5fOEOuFA9c+IIRQXtGXF0SCufEEgzlQnjGJDFaVay3tjNcsi0F45q0M70Wjd9VMyWWOzmCFHJCtw9YpvIMoD0pERX5DWm+BgThn6oFBnidAQQFZgSabc+W5tvB0jn7CRcUit670LZn/vgj0iH+EBmEsK72ErrQJ/7klqcfEqYFimuYup7YkQQx4soo//ZpjGDJM+CrmlPtGQtyJC/hBkPek5ImqMGxMlSZIpNWWIggPFt6roCR7E0nxXviRWE0tI8SB5yobva7kfZDLh8/jmlAuyQ251itN+6zIFtbiq0daSuMzpkF643yzIhy2yoJWuMwCuqBf3AfyFpTNRQgou/+V62jdkfqFF8PzLqnLVaHOB7MVb3gvi+O89aGlv7+2UpgGlVYXbxgCQVWmgzul9C3MAYFLwTbb2n/AysMVY5GnbwUQkJ77G9tdW1/HEsxoqfH5WkPtggPMJuJjxpS9n4VNXV3y8je/n1Zn8RbqIYVAQVKzWmxJKJ2Q51FwOvs9aAG6rzQrplVenwKKcYz6fGb3exLw7wk2pT8+vV7uk8KLmUWqHLaiYvoievsigHliTGAtojZ6DoXuwumltsop/ERzkUy26ehLiONvQy36m7On3P+B1A/fqw1RgAcTDrsstlxMviJlN7USf0q69WyeVKqL07Mt62YpkO6FkkR7kcrLt3ivuIYNK6SpQpi4h/imndFpbgVBFEH24B+T0lAFd2QfqWgQ/2dLTdQzYI5eestVKxAFt9nQ6OXCrCOmyJqUr64J7zW3TuV8hMIZ103x/gZPbVpMFQ3wHNV/Y6ALnke7LcBnA/lVHWMIp399PlZKlkCsMXa7kREGg+I5uW1uWa8J9UHu1TW/OKAgg8wxFAd7eU6m+arrbP59jqe8Z/9/kVJYcOPi2WqhqfnEijX2LieVpS5yvJ4JdJmwVH0W6XYlwGYF4MY9gekFfGAit+T64IZ3H+Pa10CWNj73og1TmuUQfhCEE+75bOV1IRIuZClKG1y9BavdggcpuQO5mqyI2koUwpB9AMQUKjcxIJWYC2bHAHdPS1sB62Ex6D+ZDfNKSlouuvLdfM7zHEzFp1X4VShHQjhoGlBYZiu/nyOtVRIdqsZjv6QUdCBmgfPhGcCT4jkUfzI5H4wCKvjaR0KZUqBhoUcLy63DXJGGGllWuVYVVI5YAhSC+lSbNmOQlvNR5qTCe3UDcy5r4xbRJpDzonzOiRCk/gherJxzX8PzhYI9QXpBWIjzdT6pkC9Yr+LuypTiiKkhfrZ5iVjRwF6gmALv3SSJrklxQkTeek00mX9mYe7qZi+zKSHAc/Auy9jrdzQIljXCNSx0UyusYpz99zoUxdrIhXNul60C/K7at8xSu9ygt1Hat5/FDC3n/U578qZXVT5UFcBXOH5j9tF02GQmzCK8PD0FfAACUM++cMo47zw1K8+lPgPiCQp21KoYHODqGpmHMWnFE7JKWA+RtBvgF7K3YPccHahsTU6ysN7f0AgoH1RZiNUiL0qJ2QrOT64LXsLQwo/WqDe4KcXxBqwMKE9kO0DoOuuksnPWxe45uvWc2oI7n4v+FsTUoR8Gj32feZMSCuYwYsxEQXykiWJD+FR1ugRvBIkd+h9E8RtTS51y6mGm0MA5sCEhNN2O1Js0vD2uNW0yQ1fxR9lwEs7u1UGvI3H9ouDswfYJnJSolcCoEIb4iW6a90HFpECkLO/LQacS467nLzA0VdwSMiqBZ3qM4oqqbVx9XRdvunaGntNobNDqVbHcerdqa3dsW9VK8nW7IG0WUBoKQHFdBbijpSw6XqhAMYnDpqVk/L5av8Nxw8ofPruAu9c4ztXyLcpo2q7X63tWKNfPlx3nsgCrx6IuZwn+AoPzA0zAmJrvuJIsWxKWY7DrSoLGm1DR11MGX0EprF4RnsbrZPFQ6BbaXVM7YDMQOoieyw7cEFJRfnZawhSUqBg+RvwAm8vBOi12PjNm6xRrXd3akq8kH5xy67EJ7yZ+oav7KnGEiU3UadjDZ3FkQFF4e+HYT3oQNf7h64XILepA3NinZ51cvNDIhaO6Cfr8Fdcuj+dqE3SXuYf65g1VN9zoLTDhiu6zrElCQj1905xATDFmta4a2GdFd2yyrhtl7X64a+eHXY6Q1jnnWEqe62KaXwfr1dehB/og/FRAuVoQDs7+EZyXdOKCRJIOvbR7DQ9ppFEeLdoOB/Y4x6R0DQslw6aI/dJbEKcU7SLff09TpcKo/DrOdqsKwVh0JnCosVN67FI2WrvcKyxkW7fNDk13AkKKyv4gJIwitm1boQPgy6615/DmY+/IW/Mc5rrxvLs1pY3GeYm5rejSnluqKu9T3r2Ma/Ri+GB94wp9qpTTVbmsyinUdxkwp4Pbwnii+U5hZEjvWV4eSPPoxcq4SOveMKQ85QHmKXM03089Xgs2z/M5ox//STwFD4rRSP6RmUDZohF/k/65tjQUAyE3TO/fnOcjqZ4KPpKaoguDyOXvi5GFZWYLZcm/Arp2f6EcDNkMAcrztXVSBUARBDEeK4Zr62JoTG74J+G0ck1tWAZXKQj0N16vySsVo1VzHE7pFbXz0mK4oeCG92aL37/0z0cRmIKS3jfuUTBU8NZ/hU1DKZh8sJ80r0eq7PtdOz64qLEIcOO3dvoSG1XRoL0yxgWk9N2ciI9HMYcB0puSBgx9YTjC6CNLrWSQZdV1KebLeZwNitl6HWszaiYPFb3GRe8PhHUehzFq9SZn4Ls+JxMWepyOTeVUdQqWcJTkCdX0ZiY0wOtct/UexHibtiYhoSubo9/z+h51ELu2edm39rwXJGIYzt3+FuxEK8ssgJisegejUzGkO5Gfv/JKF4/Zk1uAVG94C69C7asi6F1NrX/inovc0Oez4yNgU2pVQL+Cwjlfr3gsZS9q5Y4IxML4v+h4cx6/slLg5GbrRj+FIgxsrazCI6CCEBeRqwM9oQ7S0IVXnCCEuDOUVJQjfP0CP7dmDlko54UN2Szgnfy8/mNLvnQWS6vcHefWBb4aJqBAWyl0zB9tze77k/uPoivw/nt8IoMb7qFih3lLyukHWdr2JChJfEw+11sL4AaUOjxQ3w/nAs+ANxJfU+A4BbM4ENNzYowJAm5SCF4atpgRRN2vSalgTy96CUwDrQrbMKEMh4QXkVIs7L9DPn0fZapmjTspjmCM3p3uci36/txzubZGNSY0kBcaYpsgiQGqE2z6cF8cNCVFsHZG8QPHk5iqLOt5rpz21MqDC4r7qb+IvU/H4A7dm2WlPvh8XrZRm4N2nWxfu6JdBYOM3ihOxfhDrDdyiXnurxaPFoHWegg4UX7nhychlxJwNFqqsqlMAuPandbXIPXnj8xStXvP4zl1eG2RKbWMbzAii5HgvU0qcxgn24hdORPJTarsbThVtY7hteMthfAVYgu/TSnIWsspSgXtrIIShJ2talrPEBTb+DxhIqSqanEZKmF2BSAFuNlSDFn8JfJ3Zm+oSXppeBLwUsluynHfuJdy9A5hgLcUGBdHU0AJldZ7pKgNag0LnJqN4t7O4RFlj4I7BVWrJZNjKBlTNb/deKatSwgQ98XOHtWTshtusG/M8bK2cv1qWNWGWphRVIq5XTEbhV8hxAgX4bXtKYOTCCCPVqR+gbKXEoy2qHqPXtBCG0/cL7JpLKT1DIKKQz0VRNqWEGbxSDxv5kHKLDUmM0SSw6q28XTnQCqe8iwyU6kUl80GBuaNrwXnfq8p6AFcdolDTIBro8y7GyY5mKsmRJUrLMkZXSXNOhulN7vYcKAnKXUaFTLxvMHLAuUFqvj3oD+PrndIfjqwNSee05bn3cJbZJLEc1DR4xqZEaj7SNiuXzuLWfne5NOkEp3WaFUYszda1+2VIC97+x2CNryFG1ph1ZEOhgLmVNEhxlgptCPjklOA2qzTVnAT5tOFjE61n2zpt4R99RqqJ1z//SuUwxcqhck6SSvBecL9vbhHkeEpBZFQkh54pFOGSxUc9qUYpI2Mq5E0USe+5J1TOUBplFG6BsELxkLZVpr7QLvuwXCULFBbhBR23ohzTnZ1GysckamzQbrXG9UocOngpXsVIIgnD5vsmakQQjEN3oqtWzb1CYrn1e4oAQxvaV4gtzbFAuzlr1SrLb0fjQdCTvh+50QyHQayfxQfOiFGFN7g6rBvKym+8C5GOM7z54KykaZBygEstaik3m3YAS88SwnMyuvj+02YydbmFA+oHdyKV8rML0NHFkxeD+bTV75/b5RSPdDSIc4cXYZyimJiLMmZZo4lZUGlFQKUftR75DpYSQhn06Yp+aE8L9ZYlX3KwzGFsg/SO8bcvuzIOUV+poiJt8sO28+9tNFkJqqYoRiG+Z8Fk5ZQZ1Iuc1SjqKkk8o9Rmfk5Lnm+b0Ep70NL3j5WffyLhXdVaWV8BdJVFMFktg2yyDAcSypzNZjfO96cjzlOM83VUmzh6yuFG2cVdpst+FqBD2gt9eTN8AZ6GiP/duxACyzrHNLKxoemoAzPI6tKFrpJ8AwJuCI0MWJvpOh0ktfideEWe0PTrYYHo3NWQTJbBvUwJUUJDlclQGFgLyEb2ER+uSGrICiL6yfLJb2VoD9O6c0+0aG4GBz15qwMphVLXziuqprTspsss0IqF1lR1xYJunqR1hScPgrstzsVskHZ2buAkHNqseYZbJ2bpw5LZccwQ1SnUzt9QL+ETTsnVTmE9F76INZSKPrpeRCqinOwW1wJ+KdCUNGg+zGER1ju23ELehiFTrZUro80G2Nh0pCxRTbcKIxDLIWtR5H14xasCuibkysb7YCll95yFL3lmpxI/0yqNxQ/JaQyPvCk9SBtw5od2SK2gJojFcJhaanZ1EoNF0CSt9lt2gawlntPYI7rOqMRhX8X0lNn9CYx9AUeovKZvJdbyuDKiZjgnj4Z1x8bznO5NpwGxTBdc1DMjicqmQXLCT0w5CkM9CNlrcS2fqfUHoT+5f1K9J1a4TdWNC80is5GO/xQd4ULY0MGd5P+KHLIaaXBGrY7a2Xg85ZG4QMzJxWJqJrZxSoWfuZnO4uk9lGmttY1DOVYmNtlN1btYJq18uJqHnl/wj0faS5cRGVFkL+rvy4yPKKWYd3Wj9Fgnf100c3t6UVd3UR/XKqYeU+wILExAX/U1Lcly+09R4GXhr/rgncBW3IQoUgnFKs7k5HvH3ML5eZiO2Py3hz4/emlbT6jTaQuB8XxEv0EUogi+LlHQHpDKhJitcdN2zjTxoc7xglG5DvKgAqqDNNZOKNJ2U8Q0krvdHoyrfRBMUzGQDFakuxv9hw5bzEnYRArYw0KDMrgbtNO91u2fUVPam+UiKsg6HuKdrAuuOT8dqHpQD+hsIyxxL0OGDfZmidT1OvVbWTh4T3t22a/bZvnCDqv9qpP26GGQWNfbxh83kGR7dZt/bylEqcR5eu6hidb164mr2kJPy9B5zrGuteGGM3S4p0s5/Qgy/sFxlxWBq0okwWraQ46C2YdWYK1LiW3zCgdHrfaFl/wYy9WRal5i6/s2VnYV/mTENScXPGKIfvVKppnzXT1/nAXo9BMmobqfkPYxyS5qIhCotI4UMgwAT3HkBaYLsN5UG42A4a21q1gUuCL88j57GrOTRyHysA8NUuTWValU1LrvdbPJMZac9ktRNo1xTQUBn6HsERjlCd0zHpmlzF3nXPBXSgAWcYUdqBILoHswY1/I/CcnzWOpYDlDJXNFpviAU4pPqMOhAU9+P4melCwEldCWorD/TNICY02pJ+e2gb3jPMLLvMGg+V+/uG+nT5sKDxNPc1gNP4V5DcIQT8Tdzmb4zI2UAwbMTVYyloJC1kUOf8Q2uzPMxVCLoECy1ihJtWD/pVRAC/xfLdup/tQCqbmp7eEUJFDYmfQiEcvjwpbdebZYihUCMkGWKWTqYeIKqMT4DNjKoCRNvdbstTaiyXJwHrF0AW3CMYK5HeNJjzrtoGXQSUL+nczFYv4bV6HJt4aYCMvqFAmQxe0aZnaiP/iI72lfq4bH1o+nCrvYHkdxwzf+c64R1FnhElDPcM2jDhCcd1wznMYWpuNspvxlPmwYljAhr/g+DKlcHXynrJ388pZCWyFUCPu9hii+i/rF3A+Btg6Ju9ag64ZJczV/CM7ICVGDE+hprK64rArleQyocttZSAK77Qop3tN4f7Gyqwe043DrRn5LLXJUjihHqMqBCoxeVUYg1lVdS+euwi2FngjL2ZXs7yktM2wgNzZ1AKmrHrlVHvOhnmAQhBFBSrGe+ruhc1hiD1DCVAGKTDN/soSXEjDxKWcmy9Ig2tlv2/tw307Peza6R5KIYTcyj0WRKUdmL+oRzw2W88ubMv02RIHgMVe6hKsrIPrSRZvtbIMHdiTrYHmpdhSxdGdKp1rw4Jd1jiytd0kh8YKFIMF/4JBlrQgodCiUZJ4pvxM/VmOpVI59zUaz+QYz0oFpFvAmvBeNjumpa7Yczu4LPH3+g5jjmvuTK/tLZEelgc9EuZxvyKV3F5EzkWauaNgTDikTtzNHfVuCTjWKFQDrpW/b1xjDkoPEFL5fq0Xsecs3iruI9agVGUyXnNI130NFhqUyULg3crnnajB10lJTUHtZzINLPEzLAZZxRUKsiCnlQCMUrip6gxmOEYRvYGILU7qvPP+ADLfW7DOIDTygciKZJ3FFENIZTBiyIMwqPeZlmLxNDIgWDycJCND03ll4dAiM5/QWi0YEQTcF2XnzW03w7n34QkhsyFhumqtcnz19wIzr6YK4CRvqwtpXIxWYNk4CVQM2127HKJPNRXD4djOnx6jEnbB4uZ42TdYrSar8L2/b5cfdu38sE+FcLxzdXJra2SRQkqR0VapldxkfS1mNlTyTplOpfTWNmfQnCFU4wn13ueMo8X1VAyfGY4r5Ih1zzC+M5D5yXL2eBR0T3iF6czbSOVNfqeShWc5KyLJEFILe9ed86DEP39ua8zZZtO2OPcWBW2rdnyI8aB+oe0DVeXeDHNYsGtU9gbdgxVidaxtSKjh0pIAM5Q/o0tpRc+Dn9bm8OcXuBPzXl5Nij7/NT3HasHorTCzFH6RG7F30egLdDWdzocG6eTFvDnWJQjpCukqk1HhzN+VEO9dH+64G8fE4PO5yKgy8dWSSIBZGyUVgbV50aqybkLvFE4RC1BfA9kcFNQli6dNFl+9wXqvtWPaksU13LL7ScsD2ZTfyUmste+ivpfG4iAKJkNm9o7M/eSmQIwbxDgvs1KrRUlWROm+q7BpqCqdAvq8zwgMMhsKf1QOqeIaV4WT6ZvO8U+FLq8Df7+I3VXXybhTKuNipbluwGR0LOpzimTkzsfaaW2DaltCZpBD3phaB2po39NJCwsqjQxRqOv5U4Qb463f8zl93+klTAytFsK+wcHw0zV5zxGYtZeY5HOqEI9ajTAY+o88WddIqMtfeAll7cDDoldd40ALVm/1dKqRg7X78tJWHz+39f2+bfebtn1Yt8N3EPi6NXq0rZ1xG3jcqGlQ21Xv6aG3iS1mw1g2OLyHZyOlvNyh4WXrv99W7aXwHm/hhgIYT3p9VAVnmMgV8tp3qbjz/jtTb1KVkAm51F/5eC809hYcXM/1hdDRlxPi1Ye9NPohcWXG45EppPexIdyxqNwgf2W9Qm+wcmUNV/OjamLDE37dG56X7uMI8rNxEWSntyurRmOx+ZLEYMVdTLjFN6DYBbF5k50FrBNBUClF8Z9AmCHLJLI7ZDEa9ir0yZHJZOEy0lBHpb1aRFrQEkbq7Jxjnnh5WEsPsN7P/FMfizOBMHYrBzGfphLwV8qzWNUxQ9GRhVSdrDAp2jzRvtKCU9WgW+WLI0Cv5vLsg2HHxoVmSuXMn7pRHQR0bw0//qoMLIBdl5KWb+WzWd4qtihT+GqOHKdheik6zL1syDe0PqzaZo1ajTghm968RO9w8hK5Z7NrJACr7UvmkgQyHeejoKRUtAtWZT4rwbLVIMI0PT9TMWx3m7Z72LbDd4h5xPPGcq6oj7+TqcTZe2TaS2kNXxaEYDUOy98LGPmtKU8xchNC8ecW9no9SR63axx4rSoPafSJrJLpp1i/rkFwsskYD2T6NuWDTlTWZ9yyDL8vDBJfj/8Vef3VPYXhGhWo9k0ujVN9f8Ubb0jn6iKqIZhjEnn+mVIXRxLqTQuyjtEYnhZp5xYqC9euNzyWFHDVulpShl5orp+IRZK9E8rGYN2FsV99ff3crb3AxXVJ0WBkxW3S80pA8lqTq2heGlNBJwlQ7YRW7jXlgaWqUhHZV1jEbabWKPPj+87EAUN8+x1Yrib3VUVi2BTZG0LFgAxuyhMasFPg01G1jfz5oHYOTwFFVaGUio1gmMVFdaopyCDzYLlKQTtd2FKu1Kj0701MoKaXWIIPfbi6Pq1xXQ+3iJRdth09tPXThkFazgP0j5QC2GU3z6e2eTy1zeeXtnp8aaunlyB0hMKBQlB9AyAeenaUyagf6Ou5kgqOz8zZYZ30r5jloQwB++13bftp33afkSEV8BGYG5yPYLsHSoyVzm5KxbhC6eds6MrpqQVlWYaQZMh4vdZn54f9auLE7bfSwJs/swQFv8dKz/4cprlHei8KM9WK11CTDZFSuwTIrcdXJuN18eKvjOP1D1dr/T8hS+owhMLbMQ/EObkEJpFb3tlMx5Nox6uugOyXNK11wWqhJ5RUiNdy8SjQxsWqngSuir7bF05681awbFEKKwJSvqd0BZeEgYU2BAaE2ewRO2jp3qyl89ba9Qs4teoUuDGB07O/QqkYzSCmeVQKRk36jVJ0ZkE5u+rD8KvlNAl8Q3e2dq7c7k45HUFnBPfVoczzqGY23fsBCVsYCKRAdzMbCSY3S2Jqsns2v/TuYMz7pmYoS8/5+ayJQBbWebFj3gCb1MCy1klUB5fvDllEJbhe/y3Gbxq1fj1rUhS/cHri50cKeYwDvcFxDaSAVk+BtRpIT6ZCQMKBKojv9kk8OOyBpf3OaSpegD+bEGU0N+L5xDKbMNLhwOuiXoTU2odVO6EUBZ7a2oogAv5QYOun49i+ll7gFIivc2gBcTV2u8tFfgwwc97Y1U2/1dZykfwuL1tfu7QvOhJyVLKHvFMaAJawNq6kjMna7LXoroV9oO+7Zv5+wzOo1D85iN+lTqEMakkw68GlYphvQH/zFdQLzAu2CKgBfmBcAhpZ3PO2OgaBJhMm2Sd1bVinSPHEpuJdb1u7v4smKshPd6WxMP0MGgHaqAHjXDjT5Ba4imyvuHcF76wQ7CFRIbE6tXsVzNCBxbdahcWFOgWM35z+Jbgc7nkJ0hprrkJvtZAJk5lI9ZmV25mgob7xqhell41K5I8Up3pS55FudZGa6XKjn0bPxuD3Cslf1JSEYFkjf/5J3pTy7WmlojxDhV60UpECS5hlrEfIZ2TPQAHmDmkJ8quexcSPdA2j1Qks+LGfAQ2YooBwTdJ4X1p72TKrzEJqw3qBbVj5hLGiJgG1KvhhBhpqVdju1vVABVZj0FftIjPDqqzNOjYcTGaQJ4osPeyBE87v+gLNHQU8+JAAeWm+sYUwlc+tbZ/Obfv53Dafjm396Zn9tekFed6H6vCiEEClbpJIObM9djNqiUQVqiypAd3XjrQYSiqvlU7F/McvvFM4j9ZA1IqIObZS9eBtExbaq1ZRbax3cUJVaH5et1UpLsjSJQWZaEIf4LsRpC9XCosnXtCwFBydbPbqSPxsqsgzxl8rH+vrnPySqTEoEn22pJa6QAeWFi34+3vyziDd8QJsNlkme7GcsTwGhkkX6UBlzaqowkELz5lDEAIvoPjugjlTZvEdCiBAAbbkEVyN6m5WMsOKLE1UErIqRVRBPQ6rGPUdaqhSU3A9x7XHhTK+Jvtqwn31uMqmHa3SsgAM61lRGQbyOaj0FevxWJx2zPNOfPNWFm6Ag7mAj3i3a9u7gNACTlA8BmmpIHGDd/WieWMRVecnImZOZaIYQqWbdv5+df/ngH0VZgWWqUpyhEIKzGeSx1K8yM/e7VSH8qTueid5rOojYY+FnhIUAnpoIMBsnqyO3zOeQppyKceb8OmCt0CqcBXuud3rWlX8HocoTtasnYDREr7H9unSto+Xtv18bBsohI+P7fL41MkCSdnd01X7OosGQH19hqeW/dVzbVX7ZaGHx3uORcEfJ0+5e/OUqz6IN4VpX9uo6L+sDmoEZsZjNfLC4ay9s+ML8NRE1VP7OOvamZxRIfmqPCrkNGed/kbH57fDR1U75eRe4UjjoOZRT9ic/w3K43B9GZypl/XEZZXmZBUprZQFUnidXsJLuN+ANwAZ0QKf6JF9JGWx0wo1rvPtBRKwQc3oCMs+swvqRvV3LLhUkc3NiTtGJTODiWrNScZOU0qraXhdDEpf7UHwaX5dfVljIbMVaWttgCNk1bq4Lq2dgmcannIHrtLJjvubRWx2raXAIOTIDRXQUXoJ1fCW8o+U3BUhlPXdnlALvQXFoYLVU1Y1KUGOnb3T4+F4NS48c0F1Cde5TSit7pKdNsNGw8+0ruu8FssuFQKNBdWg4CN36KS2b5ePn9vl8VkkgjG2XC9qw+qmSvxeVWLug4170TNGLCLpJi6vWLzC+DP46X4mvJbu34Wikk/s2xyEqqoT0fyjmvnpoAZWUMhufKQeJvTQ8HvZn5kmXmJBVhpXgr96YlPM6T3Sbmm/vpYsk0e1wGfIWPeRsI1lRJBDRlOkY++BwfkWPF2hO8d0SkGc+yxkX4ylxjs3lWNQDfWhvtM1+Os9BePLPbW0C33/p2CFV17NDeysjt9wS1Nzc74o9/stvM3qH4pA9Nq2stYfPgQ7ZUIyxf2aqSGIAKiewK9ltLXeeP1OaYjjjl/+uIPPsOzMLoqNxH7VqP7FptJn7u6CVvtO7SDVkCW+W2hASI1QejbY21kgnPODyIVVFUAW440Cj1+zMhg6kMlLkKdAyEXekpUGGZKGdEdAp8JcqZwB392N3oVhMis9egtB94HK5zVoFcDNY+xdQonZOSIIpPLhm0oSULqs55PWuIU1r9FZaeeq5x6DWMi+GtZAn89K6ph7pcQfzArL+4bS//zYoQcJ0nxmTnO0YKUX3Mn5qAwZH9P1DCct7bFcusWL9HldfEh+Lczfqq2lQLm+pBSggKmUCd1FjCcyow4h4BADg/AikeSxNcRCHOwmfNa98T6XhndMqlnGvWR4foliqPTpCf1+odm8dMxjweGaA79FZQsCwSD2DDhaEBIVeW3HW2KrDlzrUkM9V15/eQ6uivGqN/u7VjQPeGp9rz7UISR5wwKYj1GA5PWsIEh+Vzjw7Ub786mEQgBm9Sw+gkAmhJDoirtAULcrp44Vyy6vr0XbrcCy0WqIQa5xTz11cxEpBAvP7LgVuDcX0i5iHoSDICjUjY7FSffbsLxZ6Cb6BwgEXt8kUmXsM+6YENLCM7MAH7DyOpdyd2uGFt52jj0C4iJru37GHULpKxzPEZw6UYOQmPPQhKjjs6wrIZz2REUCKgVm6+j06ycR5wE6MmxUA5yuWoZSgIIFVbm9B5wim/dMRYtzULpAXJmKWAN6C3Nap+DqfWefALbBZ+kJyDOd4T8C+J3Pycoys3yI0dsYKAyqgctMQ5yt3cISizl6QeHBofepFh+P4SOQ5MEbYcYRA/+K5ZhaxltSgo3Bap8jBVZRmDmFvZ5mURnUvTZP+xfJ+P7hd5PPLT5q8Y5VSGdSDGZZjm53wc4QRW91vyoDzC2CZVtZftkY9fwtxlcW9VxVul/uLbxfKcz7nfMxYYDTeK+OWXAsgGDRHjEEQ3Q3koCA25quWLjMUBQhGEtKnV0vDYqpdxBCTuskrGHO+Y51BwRjHhxbZbXT1nRzs1ZO5aH6BvKkSJC4KMrnqRj0RZvH6bqwaJmHv26nhy2rebmUsAExJ2k5FLe2KqthemVBkPZafZOdJWercVozA15eS+eN/zrXXoHd9vgkCm8tpYKpD4s5PcyC4U+9KepizpcwXgjAx0feP0ENZ5ABujBPlDHxxK0V42F2EzwF5PRHsDqgrcjKuuqoVp/p9NP1qsaaRsPCWq8f9Q1V4eIURay3J0BfYUhF/rrTfItCwJvmy/H5XFMxXGPeqNO6nQdWajaCuFDxqazwDuWf2V+qeI/gszLlipdcf+czQc2DZwy5xAP1Si66oky95nyf8cGUfTedtTq5X8EbeE0r5J4oMq2gAtkADDULJUWV3lAqhakfh2E0p/IWZZBkeUvHtOcHo+Q/WT+FtChnATe5p8NhOGnh1oZIe7+pjsXLGuZcFdfc1iXfdk5+d79S4BurTWhFnPiwzpRpkth/xhyimGrcZEVwzeOexl5/evOfsnD4U2iXSY8tDFLnRV9cKARw0OAe6YEe5CkZ6vA8LM27x24K85yTktx/pRAcwK+ufRdoKUyRQvl8bO3zM/PZ3TDH/ZYjoKoiLQSbFVPnwXFHUx72D3bx1Oyt5Jz2ADSrbX8Jj8m9vinM3c+b3ynnwXO827Uzf7ahFLDHEmaZvITc+1Nw2dXLuQ6mtZx8+FIyVsau4AdmzCLGQnvi67iYya/TuxTUQuK0Pjaca1h38tjYzUuKu8dql735xd9Vf0GlYE+LW0/NlRTMJxcTigVLtXVteJSUILgn3LubVFlUOyFiWLN9HQ6Ecyl0x+VwuSU3NO/vAiUKnPTF8nPVoZuAvGTEVHTEPdlx/55XogSClapBNgygQFF+llXe3ZKvSXh4U2N+0fGb+in4AffrVhhn9utmt/GGckie8uJzZ2CNapRuchTqyHvw94qiyowfZ/XURYjXj1IIoqKmm4v4gzNA8PCsGKobZg0+90L2206uzQf+StZCCh3hiYw3iGKazV6iTafpHQY7wfdeoK8U9Ax+KTvJn9W4ucG9cSu2u4RveN5T0BRBppoAUF4TD0fO/T2I6x7a+YeHdvqwy/zo9cuxbWBJIggssj6KsMML7ymb29Sc/1z8MaOm+sj7RuxFAWjel40DK0l7arDA7+/a+UEK4S5YP+PMsLD1vJaCsrbkc11dB5ej+rwo+gKd5nMqirlDO6NFH0FJCf9aA+HiurLWSI1S4bFcD378C15tHVbeQ1GIMyll7h/V9qBO5OnQNoXKAgIf1dZDLQiUgBUrjRtwdgEeRfA/ajKijgBJFWWe6z6qrS+H1p1vKIYqEGvf5zePSeu891jNvGJeS32QrLcxfHQEHBf1SbExakaWv2BPWefT3oWSjLNr374iP4d/PT+/4fgy+CgnoizIOgBOVqkA5SDnk7znQrJecuJcWRqLOWIMbuQRgiYNeac0wkLNlopFmHqx6wGke3Y8tTNyx9HcRNhgYH4jFYci0Gqh6HuUQL5ldgzehp6rBReF7nq0Gquhztxz5a6b+A2xB9NP2/XPE08Cydeu8IixSGcW1QU1GC5SCKaf9jkwBsBGUAoQ3h8+tMt39+303a4dPyhbClCPrMq1CqNYSYvzID1Y/1IQ4JxO17TAkpDM4j0LwrMsMFvQxsSdMcP4EdKNd+383R2VFOkgSAOtuWT7SveYrmuueizdtZ/DMTNzxE3ZMimF2lWtP496PQcee8vWMU1TvxvinGI9V4ppGF9Naaw3MxspUWBnI4vU55/WbSPIdQUBB7n2jCItJwSoTSnXJCZ4W9YYoF4ZBdlES2wCt/ZLjqdAQ1dzXNCKQTGY/NLTdfkyttS3jkkG0lsgpUzEwHI90VsQTTwykZhZhqp1GZvugOcqee+9hNLcb9tdK2vcrQaaX7m/CZp/r1v0xTEFLpr3+mivuaw+XhOk1WojE5qrHcsCH0jb7Cmo+chaaYbUuNpg5rOp1jw2A4QPU/IO7fwYQpBBIMciOFYVaOn3K8bMfKPPGYOltI5GmIRC0vEBWX9UQPJmnH8/VLgCsgF+bnpvejyRzkq7umaupALIgeRPt7aK9zZlFvWOXm5SI/we84kxfP7MNN/199+T3hpdxMhmet97ElA5nHbxPa0dwncMqoklkjTaSMlEVfO0rrBh8DozxhAk3mRWC+IZhCayTmMdQXp4LA/7drnfS0lJIWhqMJexbnqv6wE2sjWWAvd67S52rLt1pGehtTk9i0Eh1f4fSldES9t8X/9GM6BQCpHUoLUv2yTHm4JmQTbqftLDnT0j/q0UWnZ9U13EszxBz5+qoSPtO9KNV7toWZrp1oAIBzqIQglt4Vrv0c8+rIOuYIb4wuSiVYRCXmTBNZYfjefgDVm2Stx+IZbIAjUxLtBb8v5SZhvmhwozUnz5nmVIm5JbqlFGnLPUzMzXnW+rGnzDfPR5Wco1+auUQgINtqCWjqsBTa/fPLnP/orlwDzfnhvsddLXRmEnLC5w8ib51DXlUOfrENNKqY1Y3NHghcrBwpad4gol8nxf9b4rle4VPCFrhliyLPLwObOTGqGX52CnZKAPQVVk2QA/H6AJNQwiHiwFltfw3BTPbn4UshQTEdX8ullKZjuJPoK56BDIbp6DqnDCXcHDA6qG4HEKriV06VrfbYnj87XDPp9D9IoI7JmpjILswvCwRxbCHrEKdF+jxwS4CtW+EES/fpJhEGyyl4d9eghQCGxgY7RMZR78veL/qzENuVvPxWt7de3egGVyaU6KoBQ1xnt5gvJM9FyYho1YSIHXzIxq4V2e2+vH7edfFULCsVMqLmposC4HHp9qfBiKtfXN/IbgfFocR4FMDAXnoAa4rgrEcr0lxKI+lwnymz/3XpjpQl1QXJUhnuGsw+hHH7IpPku0AVabs83y7hyj6kWvmeGWBgoUjBQzY1ORgTl4RNVguWWoJ4Twu8JHo2Bb/uxvw7KuMMbyB7NmSkDPE5WTUzdkJdHLxuLCbK3NJ01ry8UppczXFi7oFo+sUnRxnTdEaqc67mKaaUyB6ZfPmfuImVSyBCA4j6A2iBRBFCah7SQtNBVnZfN4e0jVK/BGvnicRSnNlm/euJRShZ4q42VSSJeUTcNwhldmkkAJ3MjJj6ItWLfoPNUOu0xZDD4qFfFpLng+NIjfKsWUljFiAuitEJlZ68OWmUdr8ALheX78lLwyUBxWCMeHdTuh1ake/+owWeees5w3T4Lv6RpOuzqKMB4oo/PZCB8fChmV/XalLEI5R6MkKERRUnC9lCyoklZb18Cwvt4QiEsQkutr3ABnSZREP3N92Z4tDhhQgO28Lmd4yPE9jr00jjCNzPw8ZgWR61OwFiuvi7dREYOrQd+WVZ3O/R0y63LD6O1n656ghXFNzeVwnZ3YU6szfjQrWae1E+qs1D6j5/j6uL5MIfwGltRXrPnfcix6CMWF1d9xr6EY6sR3Ggl9VBveqbJcoIAYBM/U4q4I+uggHMNvpJUTRUjKwoC3QO0engep+VSEwkKUpe4lFgj1bw+0sq0ey7gFzcQGQovDFtYzLWzl4tfe0UMGUu9j7Tk0Q2ZmfdReEKlAtdFqLwLThDh115QhwyOSJZREdMhOAeTVsWAKYiJ+onkw6ysVThQUZo9gr4cMPpdgOL67h5CP4DuKvECSx+bxuDYgKFnN+BzadkIhHO/ttSDxp2dQeZ/5Xvg9pzXnsnuvt7ugEOpn6aTKikwFMCrf9GixFlCPkxXsRdGbmZXPu3p+VghTDCmDl0tD7h73YiyFCfOeHI+1NjCyQi9wqNK+UUvC/tbJ11WaGmUr3m4ouWDQyqT3Q6nQrMdSLDlV7qYwfwvSfhOsuK0cVkuGwaDwCmIxQFr6HDPnyAJZYka1nkZ1Hqpt8Pl5S65DcU+WhDhvoSr5n2k+1Dzr6xevvZ9U6d3H6sZNLb1eMU8qUlutFsA1TVaKI1MgS9AYR/awLeRreZ9dGHEykwZY5HosYe/9A9JCc/ZFQafiH23aei9V6Nla8lgcY8B7JM9blab3vcAqip3kwTgwSYtDGUm6pyGzZbVgXVq5pKAqLSw9bi5EY7uoxt1HK00IdSosVBafI0vFcAMzlUoNgAWcY0QeSwZWNZ9KI0ZvAM8R+yegpzEsf0wP4wSrtnq+b+tfMRbEJmITkWKbzebF6GlYvtRZ5KNOQfUOr/dN73gWzOX7bAbklOIR/suPOkMM9+0ajqTJuGF48HxTIsFrR90f9ajPJgYjAsoJnrKX7uw3K3Hi5AIhFUvweIYMwLL/oASD98cZTCWj0Gm5UwbWrLQTnntLKYw3u+wxY/yr1W8MSnfoLfVG8XL4XXj64AfJ+GI3VhkX3PV5CBmkntWeN2cjpVu/MJaUjzNUOHnIX63JzhdN/G/xEnQYa+TnFm6+WvnuTUu2QQeRx/qCqEPo/YXdvaxDUbJUcuHrkjXA4/Gy2Ew4ITuhSZjxq4KrZPn17mjTop1d91JI56pcp6fy9ge3slhYxNx7TnvSNPswTOZNavDWwt+pfx6UIaOas2+hVOstYNXod9YOfH5q6/2ubVgxPGbDRKvM6KE8Qnyl1sKKwOR/JgszLGCPB3pOTXb87+Zp19YPd5EaixTAzCiq66myui4oBG3A8fks4O83ln4o/Eplfi2g2X1Pc521B1YIgF7mDZz3XSrKcbAvtbyeWmtRvZDXBivh0I2UWvxUnh2z70QzUxWdhZaDzGnZ09JpbYf4lvaN95HWZ23yFG07wRN0SKOJNChaszT0mKorxeCxLXhyqRimfTL8Wz9dldNwkhsKMy+5AN3km8XoServ7n25d0uwBrsK3P0sgvJPvkrE2viMNSiLSXuWVKRqI7C4Rv86Gf3lnkKBdd7EW+djaTLzrWKRFCuDmyI1eAl6WbATNsDCU1aD0+PiQ5M+0SZLYV08PQglyskalNYHMj3Rkkn58nqt5tKb3yc3/NKGGryEgK+gDBhk9jmgeLaBRTogla6mp1Esi0lEV3n7iaMrI6WS6GkMPEXN5EqW0FIERisNzVyCciM2fmQOMcUOY/34qV0+fWb21nq/DdI6NxDCsFH8xDqFsfAO98p5MvSAYLJYQANCKCmoFnaKUbDRi57v6cO2bT7ct/aXdWQlwWNhtS36VOCrYTREU5hQGKtF6KjWY5QNOaztSdBX5eHfa55/fe6ME4xwjNOec21V3iTTRRgKTTiwKJ+hmtnWpW5s+P2yrCDzngoOrjVGigathSRpq93AvEZsjDjuxmlTR7vqcbtQszL+Cn7NgC8hS6UbA8KCd2WaiOSgWqiILvGP+tiuvLyl34cHOmdrLci0KofqeZyFhHieYGZ/fjWnDhMNcOMs06fEs+c8m7JFCTUjZOWYaKXGLsdXMNrfn31kLQVM+NKDtu8+Zoubv5fHWXTNeGh125rLquaKgSpV1sujFnelgLmhQO09JA1Gt+oTVuKltChVpcqDTKwQVPYoOrtlFy41t3h5Xrj1aaFLSIpUjKmcW6VwOkBFV10pqLnhegc5bjq53AFHoL+DW00afuID7fdfvSWnESooyGAxegGDKgKQjaCsNdNNA8+/ALqBQH66i94QngsphRVSaU3HzGA5gubCWKHYXEE+pKRONTF+NeLoNAQQ6TmhyO8OCmlH3BbXiurbdTsfMV/wfgxjWdFXhaAlZwQyY3uVgmNcO0PaZ11HVga0eIsy4xjUO1nrM9eHg45LUIn3iwVqpTMaFFgdT13o07msVK6UXb2c0lzxNp6delpEcLs+nzput2Etld+WD44JOe3SyQfM+lNWjtaL4coLAtlUDMi0k/FhZlFYyXWOplswZCNVOk3FbWjoas7bZBj4JNVbGS88Gm42gvrFu9I3OWbCg+FlXS6It4kx1zxe9fpWEKzben/dwaJn9FWUgjI3Vh8eQjCQ80ZBXN3wl2ip3kCnKoVbs10OY+n+fsIaEqYrdxR3wVvhkqnex1xk5wBQjSW4xiGVQ3glWUiC/ytXPveiaLvz/CpIMzX2QMed1qGEeFZFmwtFAcgcdufLGZ3nYWK75cFNaCDdhGlFYCXXShEwGnP2Wt6ImA8VwRDAGCrx8VXbIGcdCur8lMJ+dYgllYFSK0WuocieCgUoFe74QRU69J7G6threKTfPr+LHhn4DtIm0coSLU7X7tQmrh7SSmtYmiflEpS4UF2L9WKl0O1qmY+eYCr4OgeZDFBYbGUtp2D1PEjouzOXGWo5LNghpa/G1Z4r8z3gzo4hVUUyeRDhdavPAp67qLtDyPVugQkJLUEwAxV9gTssrJG2TOZfVDwjs24rCvWYn9X6KdYvWQaiz4CIzgKCq5BVXfP5ax3TDQF+61jyLC7l+d46lyEj1EVRPi1cV4WBua4GRt4CwZVMpIiHTkP0OnyPqJ1l7OUrK4X1MzqBbdv5+w/h5qChBq2JnlI4PKzaozVHZEvcA52zC+pn9drig/BmuHblVrilLSYd7IOR0x8ke2UR4+OMQaBasF/KNMLE82vKKe6RNNdtmWOp1g6YEZXEZtrghnVUxFOVU1oWuZkWrDhvQj4ICWyOYaJNIDwiwIF6RfBWuuyqg2BvA1lllbtmQTnSS9ghowc8TCN3EFNmOWQ3YxFrKg7SIki5Gq8HxFdTY/FlsKXOtA3ZiCeypgIKE30zBAXrRUrQGF99uA/lihqPp0Pb3gWHP2hCeFtm+rSgpvCDNyVdpMzljnvdMHQWvU0bBI73iFfLQvYy9c12SrEtwZxrscYKsuEy1zg7v025Ziry8uJkzVfqkzENduFmCv7NGh18Qv0cOvuvlan3R637KTIg1yOUpDwQLFveZ9Sz8ON4pqRDR7ZdQQE4P4eggEnYTOvH82pvuSiiHreo9ToT5DV4GtN7Pua5no8KD1XvROnEWXTIzyxY6+7b4dhAkQU0lLDHuQ8m7+8WrDUfeZ81hnL5ukph83hs7fsP7fjHh7bBA4Srjs359EIum4AHijZkL4Lbi5D8M6sSFNW8TL/o/mblUB98+byFMEBnUkiUjaeJ8rmiulSbhm/JAkdVLKASkpFpM0Ew4UPE+aZFxkXbGQ5DIaj3sC3D0uEqA5vZO7kulgo3CcvN+7U1ofHwHAUOKYrBCjPBESuOzCF3A5UYa4eOnHpaMl6Q+w/YCLUG2NS14Cfvu88Fz0GLtzNtkhTuIg55c+C425wzMaQMmHarbmMgs2OjISmGeLZifYVnaEgI53tASuQ6itrQMxh1DPjbFFZpXWvosmqt08LZVJBzWG7v2EyDYo4ssewn4TPVytRcP+W8TjtVr/AU4Jz3mH+uRa6DeTyvjO/qo9N+vPK8bKTA+HAQvYx/iIeVpAwnJNj7dSKE54S1GnEPZgEmDLiD97Aa75m1EIfwPKtwS2MQhpqeOz0vKSpDxZz3BdqZBThyUXG0G3DL/NrglUnxZb8EKzbvvwneK+m4nBfdIz1Ce4700GRcpIKvMOA7j8Hr+ZqewqdDO/7xQ/v8L+/a3d263SkXebX63FZoJSdhGHBJKQuvmTWGVPQAyE9EIVl3oazbnGzDRL2qOK0Jn6vysRdXnO4m2VCxmKfPWdHY2vQDhkBT45fwBGIj8ly4Xrp0Y4bTYHXb40jKBOUh18ppWtf6rjei7b4aUJPF4lz/wCFrBWWHf7ARL7LGnR3UWTaj0QmFcE1/5e/gpZEKUVYEM4HYDjSqZ60Q6CGwuQooD9TRLgPcwozJzeP+DtoGhHFq2mwl5VPMA8L0KTiV+Nr9fSgGwA3iU+K14ZxCOaolJ4vWHnYBR0iZd5qQEpivmyKpOPS6q5lrLINLxYITG37qE+IHVoWfvYBbQiV/pobtFSLkUOI5n7fmbQIxIiCFMCoCW7+OdQx09uV83krdk1iCwfp3IkaHa4lJ4DQJTq4179923UyIdQR6/vCgIC+oLJRpRcWg+AJy9iGKQLRHbihV0Dtvf2quNRglBQojkR57tJeq7GotjxN9e55ajUW8gd3bM7fxqvhAZ1mu8q5ePxAIJEZwVWU8qccPu6woD1DrsToyg5d96/gCeP/9nsKhtae/u2uf/x4Lddc2zx/CCGO17Ut/eBA8Uy/ZnocsdzNTQOfGORaGNfWs3LRz92dXTAI9efshMM6GkXQtKqAFHLJoXTNFhhCCcIkq44Av1EQEMQtCZhbewRF/genCa0gwWVDK1R0YQe1tZMCxjyk6acl69gTkIhGjpgN8LroTFXgoaW1QC0EpsWhFqZafpJvWhqZyIKYR34XyQv9qdivbkLqCG1eZEcnD9KReCi8vbf3wEN+hQrUiKU1/Wldm5JUv0BoPe1kIWn/6zHtZP9wzTsDzCQJCRtGGdNkhUJzySi9GypRnReMdVmLDqwI2PwrPpBhgUDlqGcIK94IraYCDF+fvlkeWAqqvs9mCzPXm58a9DUE5piPnmlC3MyoDe2lIwaUSw9oKCoSBk8djrMLuCmpYgiHeUF6s4pen7B7WyLLJ4ioxvPp7Xivar8HfEx4dFQNSlFkDozga24KqCh97DIrhZd9Wd4dSeFoaXyXCN3s8leZG3rsMsiz4GqbDbKWKRQ5zsSqY4IJ3MP+t5x2x39JPImgYer3HrGCg+HZq35mGr9aeyQnrM+O6KWNLVGGShcOzfIdi+81K4dTax79dt+d/EYx9+4+7tn65axu46+iMJR6bfIAOjLnQKqmbpRiYflnoGswGKGs+gjV2C8WfokBTYHgFBxhkvRRDzYpQ74Tc0PjXRW9cHGpJmFasBDCsHRaCTZ2SeA7hw25wbpdVVlO+V3lNPDdezE7LzKBkzSYyFLe0UUvqHIUQzlUWPmC8mJz4m1BWtxQYwLISZrW2ZXfwBwE+SwI6C2R6IqDDPrPj2QrU2Y/PcU23bmRtxTiHEbjVM7XF5wY4TgZgL2dVn2Od4FmwO5sxdiuA0vGtUHYwQ4rPNlofDnUouSkEGcrj8LoxZNSttIo9F/diwcjqWUgFHrhq1qP/yNPLlGKORVa49wkrVuW6cC9F3YaVsusVsuWnlW6h5xiPpQya+p7+XZIXSdpYaFNI/Ob4xFRrgcNeojPgzAQKRQCDKXm00FsaeznO76ZP/N2UKFxPorJ3nGsYvuuAumFn+KkzHchz0wLvUJRlgK67VAu1KmjE1evjOPqjLuc3c6/SzA2vJ1rB7EYlZ6geg+OQYRwxnEBdMuD8BYhRHW9mfi0+6L8m+2i1aS9QCn8MLvr9L5u2ed639dNdW32+byv0Y2VTdnG2pADTA6FJoDsjlOHgZLBcVmupNxyR7MtOWmJbdFELBUPHzYMlssMxzGgQTURieLyZUjGKPy3UKt5vfpqa6YHBKLeaR7p3SNXdhcKA9m/FAnQTGR+VrE90wySYwwKRUHYmxijUpnQ4jlOZLsl8GgIv2/bx/qfMHrE45pywEixw0LYD5fReWL4t9FUIYz6rS1s/HduazXWeaNmz1akoDuwhLLayzoVUrGbWZ1AjULm5lzazUXA+pzJKKbn9IzyV4FQyMZxpYiSscEmR8yHQnOewkIfVZtlfrek631Ww39yMC7GB650TAjbprqNegZfJgHp4GqQ58HjmCuYc2zROez2I4V3FGuJciym0FaNPmTgZIUkLM6VVe/1OVe+XUnlum4nJCIgPPWn/ITsM65Rex45wEp6TO+Dlw1SL0GQTmCGS7L9SnoM6NuaY4AVmnEIKzfemjL+hj/gMtTWvm1eEaSqZMn/+h4K9xxq5FKqh5/T1ajRWWhD3V8n01LzbXlA4jHn8fejdTui5fe2K5nU7ft/a5YdjOz7u2uG7FUnHtg+7tmVTml0IxJqfveS6GOsGjihLMShnS4l3ob9OiMldqXhzkW4YYpDRMGHrIg8reGDAOBDAWrgUoCWjAVpZPWltxbM+gJaECr3sNitnO4t1bBlTySmGgJx9QWnEoWvpfKldcKMfpt7RTW7R9c2ZB+P66oIgV0ZRpAWWGOZa1MpVKbhBOgpsBs9jfxfN7bPBj77H+RB+Dyvv8dBWUAqPj6JJfkia54EmwfdbMl6GVDpfm3PdiwbpqfhZSBl0zF5d1xIbcLatYyoRS8B5SYmhH8cNWF4DqIyKwZ2tPJYeu4m2ia/bVTc32K2gpeJMYUHLK3bdiIQWITzTooMuvK7l8SLXhkKd28uNFNp5XH7N36kKpwjE4N+x1y57ytlUhXYl4gSCuyTb4WnySaJ9q2IFjH2hL8Px3E7nXVvvSoaZFAMRAcgU/JFB6zdwc6d7w+o2DIe05EwRdjwiZAnvTUykvThuNc2HmJFnGZYe4NIzkmFGwxSeuVNzg8I/EPLSGwQewfbcVvCeVmNasNEOpgcnXFY8U/87KachpuI0dszBV69o5kO+tPMWtNLiljFsoKYfQZLmxvI10FMERkIgqkRi0LDfoLVgKgq1deS51XqR1bxnCC98vlj5aTmp41ixLGLxjnS54SWU4ilPuLyYrPJl7QHoJ0TFsJqyRfCf6vnU+501uj2hUsmM4ita61XAK4iVc9PXXD9PcvC/hRvWcxThgH+2aGov72CnIHHZHMmlj43NH1VfW8jVa1dPxJfYKpOrVvn2WdPnDBMWmIVC/qQApiAMjE2QkeeIdNjqG01I7h7B6cDhwYHkmALhbabU6spM96sz1OHLriimzLe0ioXvDhZi+Tu9XcEGTPMcYwqJd2vtmF6ETx1ZV85+U2X6VYyUprhuzlBN7qEq8GdF4P8svX8DMhH0V6kqKmuurfzgngq4d2UvDQ2XXqJfcbD8qloa8QPcm71ZKnnxbgnz5/5UX4FEEGqN0QDz2BgVcVwWcGJGiT/nvISIMcGcGndx0Ks+D2UtJEXOkjdRO/AJtQivQMqRmXeCiOk5lOEO/coN94QyIZqC+085KBRCzyxqmFSLtfT8qldOD/VrKwXcJMIGB9A5m15XB63nsJTPgJCehf2XPsRptQ9R9tJ+cZ7wcvNcQM5EkFIgTQIDV10pZb9fHF5E/HzBPrPBjmgisi6hpLPZcntWIxf2EpAFR5f2TlaGLHrTGWMMsHSTvXRhEWWArGOfWRdhmKx6ysVj6imVHbYYcNJLxaYRQ7GOdOC/bDq3UiQDqRSCPR+dLwKCGqObtDs+g8VcaZD1mfhiF5LnhJRgKRYa7syAcQGXMFgrFUNfO2Tf4F+nZfr+TbgX4+LPMxhmYSgKxqhKgbEURh1SQVzVGKWAL7U0s4eWD8bf70kA7i6X66+wgmZmjrNT7KkpSyVrXpw84KAsIDNY28D17a06sMk1ozocxyG4zqvSrzBYgbuWUK8bkERWOTvWVfD8hH7ZWU3X1jMgWaMz1hCngpeAfavYEdKO18hgUwFjygKvTc4P3rMXoDHC06DB5qC+n5u9QBVtGlPmmE1JZITBisH7RQbtavYIRojJ8Uork5uB3ekc1YPsiKOQjlRm/hFzAO4ZJHklftML2qZnPMuZXL+FO+trw0fQqlvQ1n/atO1Taxu0XSXWbDxP7SQ5uaKaZhaQFg87oUVz+kzNpDJBYLD8Xbhgkn+egk69BJjGFZWWrBw+Bh6PykharlUpVMoBL3BPqNPGMvg3Qjy8BtIjEUBP3Ne1ChqbBFcWnxi7Mx48uPhFEdqbwa9OX7XF7RE6UJaQmnLEZ1hiMJRwz8jS6R5X0GYX2mPj7vhRwRSzrSBwGdDMB56N2d1wJ6m0xc4am0OfVWFcLQ673NmrciaULcUpSGlCPFMjyPWHkoJCON+rmnozeS/IhFLBHs8LpUA3uWcKBYmePAWsSdHqDDtkUgYO5KbSKE3AZiHQnYXaRat3lssNW4TK4I0oA4znN+eVIEysQdRakC0WVi3bok7pkirko3LgeTzYG4oh/16QEAP2Wo4CQ9A7p8HQYyFUCKpUz/GZF45oQlQxb2D1ggcL44QywDiewqCMdGnBht4ert2woVJ5k2gMlE6EVXOHU9AVmj0dUkm4dgBruRrtJb5yqbGHDp05FhEZbUqlH6ZJisLKp54naYHMWbSQoVbXEZUWmkqdxz14xrx1jzB5kNI9kaFSDRraIcv9Mf4qpXC+HNv+L6e2/2nTdr+0tvt0btvPahFpbFSWfzRcL+yhvNdzt/CZuiocv2mRVUwsU7p0k0meJQ7+5CTftHZYh3JQrwFXWg7B6loaL+ubNRIc93YQUMFNEuyf4PRhgww/KHydcZPPnVQO7zn3v1o4ddMP2K8WnhlW4VnQRXYcoRT9ZcVq8RDqYq2ubmKMkY4Xwgy4dUlPLV5KHoYnsJkhWPxhexVWAn42FR+vQocuf3HX6TGZdRUd7XrueaQJd0/FPZWTUphWYDc2WIcguJIb3ZlQvlFyLMGqPgT/0X7b1g/o9rZpZwR1l1Lyi+vP92h0l4wjw0cDTj98uQuv+XnXGoyrZzWdJr08xa2EHVPgsQvficFaEK1VOGn4vhFTtoR0U6mCOy9deF4LSwqkjLmne5f8Xsd2UkiHd8M0YTwzVmJji61ZtXwBBcpLGICc9eOxnVHv4jXFbLOAJHsKd+k54MQDr1miA+vIfJRiyNEXSzm9M8JHlg8Bca0rPFQKUFtNfLHnrKY35GOCqzk908x6tFW+tN+yaNXWxLTGElrXGkJdx3kbHgLqAvi3qcnLwsxTTkbooqv7lZTCcXtu9386tsOP+7b/eGn3fz623S+Htv78Qrc9+gBIACvrJxttYEzC0LnoD4d2RuYKBK4qfdsOGDUKkLAodKP6frjeolIu2j+tVfKERCaHrQa+h9S2Uh9AoWVICZaKeV1cW1ErKpWDz/Pf3QXGi99hmTw+tjOomomzHwIyKu7hkH5aA73aSFhQoR8El5gQTufguEwf4urjWeiYNkNsiokx4jNudp8B4Ai8Dbi+Al+08AXrrI6FIns6oiNYWO+RT74PxelezoziObYCsi9z5uu+Hp/ldTnopxaaUAgP9+38YU/Pgt4A4AY0hmdwsmSfwJPhaAoDpw+8BCGK54KP7jdtA1oO7q3IiLKlnaypxJoF15lojzqhECMWqG4UmOXfQfgrbdleacUCSeFeA/B1v+oXPFs8fyg3Nn13MoO9twU+HLOTes9AYKoJzaJSsJyosalShJVGwPCdno0UBpwgRM+RvEBmhjFwjFatrkOImovV3aZt0HkPnFnYR+qznY7PC/ahKNRlLBF+koHoKnTCK/iSiCg5VveH5rTaNZW3wMys8pzW67a2nFKWn/fGxVX1kBGuOSlrJBZKp0HX5pgMttJZrsxvGqupvmZer+Jll9RlwkhiCqA3OazJeumqBA1pGuK7Kmv8Cp7Cd7v28KeXdv7hvm0/ndr+z09t/em5rZCayGYrSpuym6kAblrphkEI/wSjJXohsyORu3ztRRPBNFKt2+I6Rs8zQyOFKVTWZU+PKxF81iC4qYVy1I+bsBtMp0CPxsorxngGt9NqHYVZyARBIRWUAu4TQWF4EcqtT+73kv6VCsHxiiyegesZSiEEsh62lYLgHSotzIkD4FnhCJcRGCmKzHZJ0x3V4hN3SrVavThreb03TdZ0zOPvefEBU+DcesyGbBLH1iagxxRWbo0hkUlVKcu2xDCvl7s9FcLp+3073W8ZA8CxQcOeT0Fu5wykFFVa82yks0E/hW1bf9gCqYhCyr/8TOt6R6MCRHm9MJf8R6p3iLhEtboNe9j4mPhvUnotbJCCP0ctTV+HWWTGjVm62flkFZ5pW0Gj6EEt6nQrBeXim9MqPYJU2pBXMogotCbMuSqodC9K0Ve/mZ7enRatvIVSZZuFpGq6w9TTZ7SRtYILL4GXZKoqaGS2TKqg4YL1QAi9y4jw4LH2ND+5HqPxTtZryHKPmIDYRTFv2W+imN+2oO3hKuV5iG8WHrPGuOiarXFdid2z6kqcYYB6S10E8EkrCj/7ybvif9NIK7Eir3N7C0hldu0Ovaiy5zqOdA15eV0pTT3YndvXVQqn73Zt8//6td3/6a6tHw9t/ZePTL8kxAIBZrrogSxLX5b75hxd3rQnUd3EEBtIFxobZgdhUKghMme6ZLBUF3F20xNiciFYt9aTrkGd2UjV4E3q+gFU735XFAIa1IsgjtwmdFvRBtJDKFq6KgRnKJXMphWal1D7yxXMnS33tba/zIUcuHpQMih+4+8ggJdWxpi9xPQ/Y8AKmCf1R8Ehjeu7EpsV0GKY7ammrlYNwkHOrbvBxcR2+gxu+JJJJKswBRJeZ03EnhQVxw9bttCkUoCOf1k1/Io4WwSS120NLFfPEB3YjvAEhFAiu2V/v2l7tDH9D//cLn/+uW2Y9vl9O323j5iEE0qUvjpUk8vzyOC8BGsWihlWWigGG+IKXgPMg13wFNT6NIPGFlq2Ds0cq3W4OipAmxfryQVRl+AUx2lYRWH0gU64eYWMhluKP2xxD/V7ZX/l9114qGSSrOMU0kRvQQypSHfGM2emGJQevU1nC+mczDQULxXu3XxZjk/Zm00vtdDX9OF7ItLLjGp9JFWoYt/ZYJ5XEjoeW3vet9X+JcbmdTtAdqN3mG1vme4s79uxAzf96gIrr8faJEHubYtn7c6D+H6Jo7hmAcrMCS88ZSlqM1JQbnt4nLXI8Kt5Cg/b1j5+brt/2oer9tMvgmCOAw4Xrp5jCxhP0EMMKaOFStrQBhcPcelossHUzuxwNvY7GDI1Kj49LBBnxF4rDbr3sPbxMQtK3Ue6YoCMoBAAbyBdk9AGMkA6xsl7dbMdfkkLu5DJ5eJz8NlVtbSoIXQdj5FbinGwn6uyo5CrTerqXTsjcEsBD8phVIVKYJXsqFbnWl5EXCviLs7eqjCSU/MQB0AhGusljJFqcfZ2kM7uKBXStrjT6jRjKDaYg2uiATdZICzFe3kJUAgf0IN51U72FJikEOy828+HtnmWkFC7zefvNu3lhxWVA+Hhl0s73oNqY93ukAL7P/2pXf7xn9v68bmt/+aHdn6AICjZacWyyvRWC1LxIDH2xRTmaKCi6qgRdvEOTCy4KgZ/TAKHhXpq71qVqdeOs4nwzB1TUBIFeIZSmDut1eu88DvdPIpzcKUQEiabPp/KYYLqdMsZJOcPPveSjWKY/GN+KbKi4gf7ahsVzdrjjJ08PvZ4IIUr5IiMGfVcj+JJzduQGKJMOs9nhWL8TJ3IAIVAuBJGgvqDiJjPhsIKY3s5thVoXNBV8Anw+HNRDjbirg3RiEeKAp/PqSYFlHViWM8GGhe8GROQWADloGQLe5+6hwuMZe8n8lEFDN3p/OtzLB6QK7u/qqdwF8Gh1c8fqRTOv37sD5JZR+Y5Kf1WYel4U+hBDh2J4P5hn5o8KrVxsXKwgQ7njv97ldKiPi83NTGVQT6NSBUkHKBxcILpckfz+NDAvVIZZGwOgF7x+fgZ82GoEG3eMPIQXLafLq8XKyxt4PMHZ5+U2gXMa0IKOId4iPYaO7jthS1z80FZ1awew2BeJE7hY/WWsqPKHFIB4x7Y57h4ezWYLCruyHSw5VsxlWIIuKMYraTuecWzUYYWlV1sztN+3U57NMwBxKDLQRhvZeFBAT6FUMR8Hj5s2+GHVXv5w6qdHlA3w86O7XQfvFznzfftAZf69//cLj//Qotv/cP3VEL0gjIQjPMZNgvSP3sLFM4p4POJD0yrg4JJdKG678UqByxyLOfIXtzGf5XmCGW6VwMaxpeObQUrMq8Vxk+MURh/KvA5hjBJgSWFcOVi1A+nRri2dnmfNgoKDOJYEhSD1vwJhqHpOgAjQTFQwN8xJsfvItOPHkOsG8I2FAvhqRCipWIodUKGpZUZNweI49mpiRP2sBXCwy6o4LGnlLJs9t01ub02TKFdP6vXA75P5fDUzqB3sfXvqugylvAsoyYgplnryGuuwrlOtYfHgCZVNiYZ4yw9NNJjUC9n7y9nreWPlUIJYnu9Mdtw9ZVTUrFhMODPjwowAu9Wjn8K9U67kHn9wsVDKGriEtvuTepzvZlMrjJpanO0c1jQuVjpodQey9qcSkeL7BZ0MsIiheUB76B6DMoH9sTnA1gHhfZcnFWs4P5jYj88fFMLTxaj4Qh7k8rhTqjJgdeSe83nah6h6hNmQFQ0DzspJlvrlMPaJJk3jsUEr0QBcR+OL8AaPW57fEfjib7CPROKNpBSJZlcgM+Jn4VrAHPmRABCH8B8X3rsIWNOEfNIorfK6OD6A6WcEjp6UVxB+e+n+1U7fFi14/eXdvxwaed9eBVQmtEjGplH37U7bHJcD8ru46eAK5gAofUoryUVPoqMzESr6c4K5yGVtCiClKtlXTHPvBZaCW6D0aT13GHQkpSAA41aMJZq6Khfcn7XCotcRCqeNCdSkd3hmBdW1FxGcyB92OkjBj4cNU5RX3LatIrACJ9EVh6uz2cADy+0aygGtG497gKedVwMULQIiXgOTrVZCtyboXoKPS4yNB2qrUlpNXXBSs/APR1giFApyGADpHzE0tTnkDUFOMx1FIAm8WwxTqXdR7yyQ0r57B18997Ea2q4lc+akBCSVTaMbyKOydxNKAd8h55tbT9aedRcIzRyV5XIm5ZLCXJ/bU/Bgu5SK4qVV5xZRjgUrOGCprusRjwLEfh4VvIwlLWRqazODFDXJV67uFvhaqY/3OsG0mLypARXk/vMclwQSqlMO647wwA5iYRfosoyhBSEIoJRymDiVy0kaiZByQ7QPTOTYA5aCvevQeAVyNDcpxiH0zu9230DGYCW4HUGlukj/Hxq4WDGfHRONCRaH2L4jg0VLyMzgPB5ECAikwjJBc7yYg/n4EzK58prnvp6MMbKdVOq4CXIaKUBlmYK6aVtn/BzbuvHUEDo0wvhcvxux40Mj+KEot+7S7s8xDo7MJgMLwpd4bZt8/TQdsj1/7UXxhH6tLB0GiSgRDSQIj/4ZGCvbgjFNCK0zkyr7fXjQK/4pdKrLlX93hd8JWNnSvEk+6j4oKr1ax4iDbTCfBGzmyAtj2mIw01ezk1vosScro5rHD+y6cTFBcPnKZ4vPQbXwFg4Uyhv2xpQpVkQGCiOniVRYVzvEmiB2H0XIOG8l3nvEQCI+oqkzZGyv5Qix9ibUVuFz4diMEHfuq2VoZiGGrITWbCHy+B1e4eabrcZrQarDYnCdcQrb0/MhkJsE1mNVIM4twxTjr8qdisgjL/GxebnmL9baX5lTyEPaT/DQb0GoPdK5sImnCFrRznn5gG66pXgNVqrik0INy3KThdc9ijx/bEal0dyA/lJqVoyBprsiT09cFzoCX25cIRUCsfIslAQqmfxhIXQ+xeMG8wdMXtjHEuf4oXgEFZPS1spaLDOyfnDRVDvbyEBH+exYHfg2MHjrBrtMAZrP44K5CUsJg9PG5CvqyCLPRTAe4R5VCotg/DEa0MpGPtPSwrjUGZJ96B6kVK47YLsVRC5fTy3zWeQ7wnTvSBFEfdgGCIoV0Bzsoa1BfoV1hau2hqJTj+u2u7jrm1A1siMI8NrnboEHi9xbcZVCnX27GanEC7rbliAs2dob6IYHRXvtbKn0CqNa5y5xNQ4zLdpvWQs2CKva5zXFGvsLOAthA0TJd4sSEwCxdtgwCLyXDZ2qkJcuIbXniv1qXSfEzqBMIVSpydndlSsDVQ6H5GJJM+8xMY65bRqXTLA3lPTO9S8WjY6sSowP6UIzGnJFpTOH4hYaAtFobqPUGQRz1zPHewqlOQYiuBGYvzKtAsP3j1ltM5ci2HjxFMIAwqerSBGxvgYfy3B4pqWr97dkxAY5YGf7df3FGTdVEzSQtPNXroZ0oNhCEqqQU2gHstZGflw63oPH2BhwYVb2T9VA4Vd6XDxJDFWaVpDNk5hglBVhm34faR/ARdXsZW+w/uQpRweguoILOC9kUtMJRSE88Z1a3Kvc2FxgcDNVnBWiosZUXgRwV9t7LWKvXxvPA+pJ5SOW3HVWitBVxgusH53bjY/z1/CC0KRjEi6ktSv1hpAkMJDgDK0clGdiZvxBFpVLCErJFNY1OcuGuz1y7ptVmeS1ZGV4HhhYeTm00tbfX5S74dNO9P1l8XJadeG5jRGYA+WHmEB/NwJpmAXPi05PtK+yOI5o0Cx9hcW4WE9mBJtPvthU6TVTEU48eZnO9g5KG14dG7QbiVcrGCnPudaNAuuPreCNZtFFrayZpdn+rPAXQtSZTxmLyMGuHxupd02DDcDsMGWyq+yMt2xEGWTOS6phvWZfWdImntbMcE6L/MY6ljzz0pOBwPpEJ4LH9uGKcrsGVKAh5XhSysnKbGghZd3edp3hZ3po4o/mI/KLKhJDeOUbfd+LTEG3D+MK8usk2QAU/Qj83GIB1UviQWrksMpj96pAf5qpZAUzcIOZf7mIi4aKhY10gCFpdkRTK4jW6wFmx8v1htMVO4famVjiWVBp6XTLYcwxooScvc3piMKVmH1srB0fF9V2Can46kycBrpn1nBbWW2NVYvy0A5+hdCTkGEFbnjtjQkyJ0Ga2jHvD/OLsC4gIWrnB+utotaeltPKR8VBkbgSgG7hMO6gqiFdKz78GNjrQWUDgrixE/jeI/jQeh5nZlRvY9zvq+4BoUjsHq86hRj0yx7HqV0189q4g76BNPNH+ElHNr610e66YbyHLAPxlZkJkVR1HmzaecTKttB8y1c3ZZfmsGj650cPnj+UAqGJGn4QED1fr897qTNVpdqBjTbwGlULbnBCLJwwXs7wENR+BmKVN0LcRTjIhRH91gGhcWY1K0slxoAn4V5hxMWVULdzt5b6dyOroLjGQmf4LwwONjXXN4ICsY439FJj0IWRoEMIyeswHhJmNfogpInoprZzvwCTUQKzXHOI+Glk0sy8wmV4s/bSFUujaSaFYZuOQody9pxOjg8HHtnUhbRmGqn+JS7CQbUDOt/4P1CgVwab/LUUbyLMdgYxWHuNsU3B+aHASq3DMWdim1hpsV5Q/d/uVKoGkp/B+4nxTC7tLZiKlTk9xIH7/n0XTEUBaDGOvmwzbVjeMZmYnU1N7dXdpayuxtZDXhT0YlFkXzu4OzvaXLphtlbMSulaMN5FOIqY5+Rjjjdp+m5rQhdF2As1hYr8G/grGhPCWHsDCwFyTPQlOMqnb98+8Nz0X9m69DCMwUm4D8E/p7GugeOuXP9j72zS/yE3ojuW2R3vfBJBgXOhdjMNgoJ4dKHwFda4Gc18cGmKIVG+NzmGfBSx4JPUCg7nHPVNi+rtjqE4O4oT7csOz9NzF1kjwmuA9mjYB0uA2clpZDBfakqed4Pnsc5LbUqBY/D9+I0a1iFWUvT62Wio54yVGSRJnyqZ5nwhPdBSOVClPi2xZi75HJj76SbO5Gq5eknbN82m2FMZbLl2LVGothU57KwhXCtvFFWXkV8hPHlAS/EREzTUuf8oCwmGWXkanrZMlYQCQ9ied6oql/Nf9znAdQdAYPLSKAS04CUeg4o7Mw4gDLHyHigZy3jY0ATrCSQASnKDqTwy+rWOlBBH0MtYCrohh5pwbNGSPIrJnOAghdhv6/SZOfqFRVaKTA4YP8VvhlOIovLLnhWBXqjloAgIzilEpqZIqVOwQvH1rk5f1gsU64rZZLxg8QRCnTlsRHbE2mWMf3KY5PY4NRxap4Zp5TSyimFa1YmRZBTIYi2mq+Z8RWwETyDEr+gckirv1RBF9d9DFXMlpTvucxNpVzI/WULC8/g2GlA8rmrvsHCHcrTAqjOfdnIIXS65UsSQ30mKrujDoQvSWFEJ7byzNiq8cx4w+5jCH4ogeMLAs9xX4CgELCOLm2hbHIQWW8hQjQPzFlCdOsPHTbgfwLj79lHhR0158PnV5ZSNXpmGCoVhqvHN6H8GRcoMRd7WOp/bg4uKlbUcFjpODjuczszDf9l8Z3WwS2I6JagSA+8rNf5897HKaTc2rV/h4Hj55fYSxYN+EEWWGbv6cTOCKPXDMFWIekSu0mnKeg8xjF3wrneaTGMSRd39f0t61ttYjM1eWvW5HJuUcfzezX9O2Oq5oZyNlPpPJn9DEqbUFstlHcBRXMM3ZLp8spIhRUns5OKDHGvdX5cyMSme0aL3F9fNdA8PADhWfN7V95BF1Cm4OXGw9XLpGblbA3KcZSAnySUSjGGI/vZweoMGoVOaeGLs6GPXSlDHjrXEJRyUBaHycm0oZJozz0WHDRyipzP5zQ0PZABP6zV2LKe4G6eyf+vTYOgLwpruAgRgIP7icUr7iBajbqmLM2a1RL3YzinbGzHOfxYLAxrIaAVcrGCqeBg3UBJubgooQ3BQrBo2TSprIT0mkpfDFlPKfBwbi/6O3HHwxpmH1/1zxi8q6hX2NFaam37tG6nu0s7PEQRmw1Z9mKCnH25MFMsDQjcz4e70jzozIZB6S3wc4eADKxsCSPJQ/XaTmO9WLFpGPW1HJ23Jqw/z6F+227virXmGEzuA0GExqTtLROiw3lLVkrWV9jgWoJ6umy92q/OZa0FuNXK9JerEC59BDq9tiioS4AdioFrV7Q0FJcORrt/ijwxpomzWj5YTHO8VfEVo+5Kp6kYsyd00A2Nr6eFrv0zJLZ0Rb22MViTMrxOnT7rID36wCve2Odsim1Q5uCeelKDcLayF0uNUcoLyRvOj2IiRhRkdGfvc38/NA1p4p39lHtvKQbzVZTCYHGMQef6YGIyclaG4hpODKydpjxiB92qpbq6gW1WfncJ3gicmm8cs1GxNwcIjf0Kv0tBKUyT2lycKFirwAGR145znIoWd1ZV0mIUF67g5/lA67zhPFYwqhx2VpCVgqmQiT8mXFD6z2IML0WAUai4A51SFHW9bE0Ib47KRI0EynNygDrvaYKfAtJQnYOVkGMrGcuAt+D4RM+qsWWVvahr8NAFf4KWkroB18k11SEEZmChgIh1Eqe2eUKleHRYYzX0vfonqO4BKa2b5zNbeOZ1kXb6sCMHD/4GJBD4svm3MFZ5nu4ylmuyCAhj+5rtgBy9yUvKr2ofHBweJBihJnkKeObIasNnDEXacjZ3VIUmbcCkC1bPew3j9jVYlNcwEAuy/ND191L6TxWz9bM2UGxsefLk6aSyJMdTCbJqTFQIoLaBoLWxOXvk1dM3rOn74XmiujfqRKYKZxNi6vkOXFo+39oCXDUCNXPRVPYDXAdYbKTUSbSGUHWJifqZMuayJOBGJZhziOsLQWBlsz7Pj9HzRpaia5LsipXmR/bG3hl8/m2ewgQz1EUVDH75hz5We+JCQTgrR24RKnRrQFObri/icmFrWm2QKzIvLYx4BnLjOCfq8ezNmRtZ91H5kbRhEf13t7XIAZe3UNNOs5rZZeuFjmCAnKQk8DusbvApmSveOdFcM9FxzM1BIohVyPYsrOx9ZYCvWKjm/bfA17wE9a+G63t1n+gJrrAgozWLSmBUeENgOQuCbLFPgcVz7rRUZZ1QqZEJE8pV6bt8u5h/5Tn2OFEJiDNFUeOH0Pz8FIbUZtM2argDJbL77q6dvt+10/2GVc3IPAKEBKgpqcxxVVAt7DesoObf8MqeoVy29CgI08gq5/7CHOz72krmSltzFjT+rxUb8+JFZ1IyhXJd1PVKIYSK2efMKEtCp9DKHWvXc4xWkzV3fQqsFj6eYV/MUFBu4+t9PCiHqqSvFEMRBcXaDgbafu3gw7JiUL9gQzZej3gOUrdhDWPuZHx47zuIKqVYIdKs8i704nHLggd98DxLFr3+4ywjPc98hhVu9v6WkejUca5TQmWmmCg06E7RrynpS3NZ/2YhLbiYTC8fCRw2bhONsCzMtSmFgXhEzUj8/eCjoq2re5U3tCSo9Wt+z70Re8exAUpa+G7+bWszre+pzeOwiB2MLYyVFqy1UjltvzGwxQwKDJcCsWw8CzGzw8otHeiE8d90N3u2Eh9yaW2YKW8uNFX/4MADi3I0ls/GOYo9UGgYu5a2gO/oOWL1jjM4ao1Af27m2cmWgQU+okJ8eGjtu4eSWQFYBpVj5yji8bTY7fZ4lUFF5QqqChS4ZTpi/y9jE4l6hYLkdRFTcV2JFRyVHhSOFJe6d20+BY/S8Yc9yfVYnQrq7YTrQliD4x/eBZcFAnV7V6uW+MIZ1druHx6B5agwD8UyWuAWPqJXtiVYiBEHQVKr4dN71fkEEUWLRT2CTCsOIRXBW+XeD1vDCmlhXw7Wbc1XX/Dyb8Ue5veHPVqHIeqYWQE6vnAoFbusgFcKpyv3XdCVyJTEd8byRLeDyu9qxBnFt8AHAmGhKSrxlEspHmI+RsNyVZJPDBWW7w5THn22+UzMtjzR4PMy9OpH8rpuJZseSOMeHs8leMge7nstEL1cIAmFBsMwrAyGQYnV/vJirP16NBd1Hj15adFPlsarJ+oaOuoIIrXVfXjjtMWirQqlTqjhlOrSLQ7a1kUPUkcHpmljEt+VgLAFgvfN+W5CKjO55kbv3EU9nlApvc9iQ5Sn4uK0grm7U1gdt/shR1vGUqVMehG1/XQGUq2UtQdFCmILIjUswmFa88ot4QVsAZyvB0dRMEt2UkCPdb3btfPnz9FjYjtu7uhf0Dmt2IMaxW1qu5k1JPMKs2AlpAaK5U4Q2D9gj0RrAbGB83NbPx/a7vnY1n+45zhBu907oEVA2wr3rK5srK1wO9CqKJ2Si+8l774qla+K2+rvXTjX/h91Edd2loZcoLAYTBYUlwrcMTB1NXQ2SgrJQcCUmIezbwZo5QsjjuWxxCkMW8yQx5SuasWIuapwDz7AokEHzR0P0353Vbf2czr02cKy8ithzkrdiz9dIVQfSnXut//6PFwoTEcPIprX+I9+zYRovf9Z3zOljtbv1kl9xUNImNn9ZiB7lCmVUBDDB/Bm98nY4HqWaDda47PR+Ojr01xwtMLFxdAZ66TfTTSy+RIrxA+caSgZQKnfGb6Vmn7KJBpcozL5aTFrAyqNj4fxWWY8bMfiE1T66jtMCyXuLErc9AhmXiQrHTFrGg9XtXXnjYeHoSYqdCu7RxCNYJQJg9xmNK4nvzv6K0SFoy2SxJWHYJhiB/wXwgPPqafgZjPxtD5FsuX5MPZrYUSWVy3I7HylwKIUCeEKcggFxXfQdCBgiELADdlmqcSy5aYDyRYkVZhqTrdgC0U+uBhD89n3DU0rX42PXAex+hjeBIL3HcKTh4ZYxOejoDoprAz4Fp4tP0vPBedaLLkU4INp3L2FIgMGr+w169ufwfVfVI0+9coeBJHaSsZa67TWFT50KmXSgC9e9302XBhVoxCl2bOwx7nude9h2JkHyPeg70t4hrcrK5bzGhBtnKLzQaVXfsVv5LktgfJ5bucRKo29Z+/4mY2ez6Xecz7Y8gH/Y5vY12eNUOnCZk/GAfZ8pKU7W77UFYl7f6fMSEJBGyelQI1xaKdcK2uQyEDNtJSX+1WVQsFR86ENgU1PXFkEb7miPmrgZtCo02eqpoYwG7R3+V5VyLKaMwjN9w0pqapZ2CYFmJVC8o2EAGX5ufoQ8Hw49FkGyEoMIfmWXEAGBToJcLJAQpiJ+8Wpk9msHe8/vURVI9tMHiMw7boICOFqUbJ8X3xHJqBTlXY+n8u5nWVROP3WFpoD2lYOznTohHGlAXgWE6pTFQPmpecD6zJwf8KJ1ego01rd1J43bEu6CDUHWPk8dm21PYx0AfD0UHT03X3kd4PN8tNTzCc+9/k5uGQw5r0asKiSdPPpuVMtOGPHdSJkw3wasqhovUM5qI825zyrll/ZLNXSL0bDTRhC3cToVaJpjOHAKqzq+XLnaeOZOsLzB++4wrEFznjXcZl/xlqFFJpDeu7kLdhjSA+jHGYRsMfD3h0Kquu7wZgKS9zEcpwg4eUm3qs8aB74OLdpHKSwEslhhdVqt7SLjTSleQpiCnlTFaQmYJpXG6CREy1OqpIcY/h68UglYjk1v9efsfvT8HBnSRo/aA4EynUVo/qcv2ugeVhomob8vay/wRopgVd/aOnI8/FDvmBa5Ksrqoy6OeP3SqVga6kHeGBJ5mpWsZgUg4UDfmdvB1nEhgJ4yui7EN6FBB03gTwC5Gpn6mnpyDbHSpx7zraL4DcR1g6FAA8CHgIeLpsYPWc3NSsuWsmZ12y+JPVp4LzLncWixGfxIjJxSFXxzPtMUjjDZ6bKxk/t4FZhCMFdkWOueIU8poEBwo/ZBVrV8kUDFVzGuPhsdXoBUzGss9MdBYgbjqC/xP2OzKhrEJZhjn/9HO04cSp9JnO7D6quPZ3bBkqOtMkxbiiytRg7QbPdLs9hbRkdxfMAfbWD/qUATa7tBOEs7ZNumQ43mkJUazrTOUfrPwXs1Zov17RCUJdCwlyZbz9bpguZS0vbcYhH2CKvkJREY7FIr5VdhQn7wEePwZik831FTQKyRhptZu31Q1FxmA0Zc23Jkyk3cH1TRpo0xjRQ8vvjB/OsRbl1sac5yMSfaR5UP5XxDQv2Ig9meLE/XyEPBWYOJolSbU+5oT0meUYDjS19gy+NewGMxe/TCb8xJXXyniNLoC6WSTH8lqNglD3GMOF61rjzRimTnDAG4hbkOO8phCHEC++KWvVFH2JV2CqYE5tDnEVWJKWNZhLH2VJPTLkosBQQpdSd3aeOyQ3jGALhFXOgYPwo3Kt51ezsVvBYwE7eaJ58LKg7YPl38T0oPMBQ+skq08Ryq34tgVJST5SgLQSXWGJ50/auqktsJew5mivDWaxlXixtvEH4iFSP1Ad6PvhiYqvRvAjppYa01qe7tgK1O8bFlo/wplAcCG78UMCRjtjIl39ZIXCOngzRG3pjLxjX+vRZLKayaqEs5E0GjAT4zEHiKU2zCGhu0mRbmwQUDWFZklYydX9ZObqhE4Vk7dFrg0QxCUEMsfbE1Mk+3QHXjYHfWVi+Y6PW2MEVpFReuxXjGzZ2hzvzRwVuPaVXtDM0vlTdDeu7UJJHAyQc0VnvzbEvxBMScdD6XfFF937pzySDuXmOWlld9a0RiwIZCWpPtoLiNSgKuTxVMEjKedbgJ+N8ODklmFydpRgKIGBgxiQxfHjyybH11Qnx3ngzrQhr1GIV+D/DOcpGWdToxU2vEEOf88mlLn/79C5EgoWhHPT0PAphXPCu9J850MUIP6AjJEw4i6jSHRcozYtqdv39+RA2KrxjbwJUWhkGUjzB9Q40mkoQ1NkQtjiy34Lc3ppZhI8TAlJQ+tx7X1AZ7YSZO5BKxbS9VsCsOUD8wzUIAW2dOe6SvqdUwIDDBPdAeBcvIZSqScQmC6m67brHDEbimRhHtZVULWRSHAfEw/4JrIaOgDPeWz3sg4NGTV3WH+7pLZweNu3MhiuxSTfs8qdWp1Awzh5ThklWuduCt5me9pBhhgnqqTrhar0WPiquu+pNV7go3jfGvmZTaiUReDwSFnQqyOkztqLtcMm4mXs2XIVDbgiRuldT0E77uCi2jD+VL9HiRfaXWmBGC1KPbVKIVGyyej07TPjA2ijm6OyR1xhUekkL8FJuLCu9YsE77lS8rMwOG2ITywrXnFChDPBRseAO85E5VhMLQeFA896AcYY4G+sW1MAL/+JrWOeEb0EVs1NmZMTeaBDNiSRfvaJ5kMx+VL45khb1G6vaeHomtLYEd1wd1ZWqFso73Y8MfHmcjEOMxTD9IZTrq4CMsQC2nuwLM32i4tYHxIRTOBBZFmfdhLZ6vLFxoDe1G7Tbi4DyYXFYVF6yItvjNJaq0nd6QHJNKwfOABlwrCHM6HWR9+VjcBsl3FaUL//2Yg9OKVT8puDPTm6y2JSfjY5VzNGHhSJSr+hWN3XRyV3ve+qZWPHDnVegE5+j1n6IAlmxGD5n1TYQlkOhG8ZB6YkYRHTeYoevR8cfQDC4JZtqt7pXbYN7MIEfN5MSAjbxL+jGg27CDH5FsMxrKV+eDKCE+RY+Px8po7sAmpV/KEnFScSPAw+BChFcULORMsAUdYzlovW69d/h3kYoqf/eFbtx+DCwfEM9LuW2lNF2VJXcdd9WyhDLCu0fpgJXqXC11sr1lua5KMDUlZfCrDDcq3/RWhRGIjeiK74rBWGPxm1L9XuiCJMSqbTgc7Me7xNAoysZXCb0gz2n4PsaPF7gXaJS2LT14zYYlX9XpZDu1hjhDg9MimFudL4gz1MflFS0PkP4XfxKvMaURlnPWQK9PnFq9xLkG3SLLTtv0rlhhYQEBV1umsrjrtQzewnVYqs3atpeBVudwZSprK6c9hwwC0jQhbNM5EFc1sEZxM9zQ6mbk/DyiClET1f+C9iD/aiLgHUAWRlOuQjTpS3V2VRmiKEUoVJ5XwiBPbf10y6UU7YGlULj1FdhWFIpPe/cLAVyqoHBiqn7sYjUcG3MtEoFpRQzDoN5PcJbkHLCRkL2FphXYZ2ShA8syKuoX2CF9Kptt+u2QxotxgFvAf2ETOksDNwVrJ1ZU+Pgrd2AA3JdV6uz7I2kSylzU1KMXefCeo/6vOp6dv0Ne1Ooy5iyma64sDxpS3DPa3pqMNZufzBkgYVcCLW0iEnFIIMKPFgQYKzWLYbEYORcz116V2mgBLvte4u04jRj4PgCJZM2rTMjpzEMSr7HlSK+UAaaqbRFKdr6F7LShX+ZtGzw5RipujMegjKEPUC8fmDwoNe0EivQmRAHuMDW7DoVHQm3j/uIq3196uyF12bFUOasr5clF0H/yZhEfW9WDP64CsOS9EsNK2wN3/Q25vHX1LbeM5e4K7OLykYdPOkbltJksVlxWAW585m5lfgR0CbTLYz7oqXkRZ4tRl0hrXFifPiKgk8m9DIFR/IIKUUUrchS8GMcCqJzHBQskdPMcSQhoDBq51zjxGaLTW9G4zGBGBTD01NXLO6WR4MqMrHyWSQNSKUMkPVbCrscSEs8loq3VJSiCOp5HTVcZo40IyfmBCRsVAqntkJAGcNWiiuvyffCy4IycH/oiFG0tj6gf/CxK2MLHiqiyEWnkifZYlViXhML1M5lTV4VJWItu61tKkzzUenabg1Zg8fyEjNPPb1WzYsVaoWz6lqu+8sLuPZeXtpPVZEtehiXV2Cp6jVIMXgNKuPP65RHJayswtLZSVxi8jaq8zPP/ZJHk3JgnIFLfXRXCrMiHjXG2d/OuTEUWIyiTOel8eYmPuM8D53lqG/MeSUjEmvbc8L4Z8wVlYLai2Iv4Xeiv3frtnrat81PXxs+8mLvIx8VgrFAQxbqstYzAarrqokobw2K4cpqqe6VG52k3tXEl5Qrr46reEW9ZrHAlanjcnTnrJsXKIO+abWpstZWPI2aokESUnPQsVQdGwahtRlBwx7A0ikoDFSshvcUzIxmK4JvxDaaXohjG7519M9GUcsJgVcoBbx+3xccvIW6+p1ya4zbOLprEHA+fM6BdWL8+xDgCphHoxFZ0cC7a0c7K6w6h5mlVazV85R/PuzhThmSnhX0j/K33TaUdRfwBtBZzbQDFowOfANeQoAfKcBwJZjJwe4XbX23JmXG+mHf1iDU26KVmwrHyI8UachoPuRKdMJzuS9mj3iCbbiuiiGT5GrajsqWA3Rpzv3V3X3H2Msa8/mib4UzwzpsmlX7HlduwEn6DbDOuP2GfVv30vC9Vw571evZ4Ar48eI6nRcF0/EfZCMNtmTJ2ze8awOMhHA9zhCUNguQ2awYqtE33edlkB3FDbu67wXFoD/j+XYlm+s2Fb/53ccxRkV21zJZtChojdsJECnW8mEXaew0pBA2FHyI723jO+AFW3+/a7ufJAe+WkVz3mRbnuRblno96gSk3L5hfS8phorBzi7cuw8zmGoRuSKZKaJqtelTK2hK4Sy63OQvojkZxWFM/0vLz+MrksG8Sll5LBgJAjRrCrRA9DsFresJfBhHZdBTg3QKGvP5e6bPCk3s19E0/fxoxaBgrmsUEICuFhGUn5oIMWYBxeSmQ4SRCmGYO6z5+xR0UCJ6IgPhV6m0npTDME95nwpCelzZ17p+V42LaEErEO01w7nYRewAwp+QmdJ3ldZLeOnxmX2c1+TVjw3F8SNNFTjtXu0i3c/BvQ0w+QpIZ0wpYcOyhq9wfP2nYOI59zhq3Yle59jR3N6ZcjMtvBWrstYAGURwvm8NFzVVL8TQST/bNYQ0BIiLcEzYZfYQ9Pe1kW4l1Sms4zwyytSTGW7w4PVk3CSe9YX1LhGQZu2CD3kNWLOs5Oc3ROSYnlt9BsVuTEhj9nZWt5VdwsyWSQuf9STU9e1nYCPJP1UO5qnX17L3LMUqw+Cyfgnj7oB4ATwGeU7YCjIQ4S2ARRjxvg9vyeffBB+ZnrkupkW3rM7NnHlQXazxSenxd6G66DFggbkFaM8MiEpFnSUnf9pAS0JbVLtJGmV8P624TvOcmSjOjxaHfL/GQiCnCgdj+qyVMBxzKv0jTC0dsNFVPwePyUIblMQk7NtpcwWnUttEow93j3NT8PAlVYFsQZpwkHoB2PuiFySLRK0GeajDFIdk7hXMIfiN7HmJgbLi5c5VpzK0NzTDGvmcFEi2YLNCMKzmTm44H4JnzkwyhEZ4bNfOEvzk4sG57TFAYYBcD03SH79r28fgdEIPk9yLzmhCEyDVa1DoOKuKAuo49so1k2oK4po9V4RCUhzXLCR7kyW+Y/oNVpI7FbkYVfYQ2DRGgX6MVxlIV/QpdRtV2KjKtOrY3/ru/OHu4hcZO0VVCuxT+3d0D1teA/ai1wChzsIa4FoaerE26iTQLShdyU9FEiSGfT8vjP0K/mqjd1Ano+7vpMevz3aatEq5nq+Z8aEH4Efoqid4jF+LzzPm4WvjGbPvCAwCUY1rGECUaNzsV+34AR0JN+1cFenvk320cFjAGMaoAlgPb5T1S7hrgRMWj2ki8+MlE8CbbRBK0/ndzAOHA3qksVDzl5JXH3iyYCYFShn8dmB0LrH3n5XT/uqHE9avIWuPi5rxhZ71FOeaekCYj74orbQaDVkVevF4LuI9qnBTQjeiptgi/VQFYseo8HVwK5+xXXhnPZ1ERcEx+Jw4pTfTZYxpcGMsKFBtiGyoN9AaGGpwZpM4gHA+VADbsrT3QebTl6AKofLzsgkKbVqeHz+39a9PbfcAuupdOz2AMC8cQCvJ9I5cXSoc3C1STfvtAqgU3nq8yWZ7JXjKS4bj/Jz94wrq+rttIa5Zw6fyxMD7JHI/eDqZHpy4eTXCZqFX99cYfL15pOcz35eKKQ3vTNqhG4qjd0IIt25rngPPKtKkw6CAQQjtHXs+KaP5ATVEKkH5GJ5YWXOZTbUgS0DDZfqjyqMhrDJ5Wzyn3WV7ZAtHhZx60/FSNV2D3E50jtR67aYwHMWJZlYEKgYmmjTGyo53rZ0esFxX7bxEBfI1ejS/Cz+sNy6M0GvFHxkVZN9E1eDv15zHEcGXwOcmPeKAZB3LYI2U91wRbAuQ/XXZpqD3IK7XqBarc43TBezpd0OWxy1LOO8xeqpG68XStNxeQo5jVJQBVyrneXKNI9joa0pgio8oWRzdV3cBAoyKanc+O0exWyoctR80bKJGQ4w7uNCuDgb/JGWFq8WlUHODzlKlvzZQQCvryEyhKGiCBM9Od4SQRPqHgPJxH3UJnz4PODyf7f1dO396bOu//NK2COC+3Lcj6LfvIo1vzdjNWDMxrFU8q/qIXRuQ3u78/MstUmnXwG/ZGFYAyetvenW9JjiFhG1A60pDJ9JxPIexszYvklkycx7rnpgXjt+XFzEoh7Lu6l64Osqc6e+rvgVprF9/nwI9UMhQtFLoST2jlG+eSjGv5EayN4Ex1rWodPHxQtN8XLk1l4V781fH9zoFxjQPtXHccCysB36lxicMmcIALYYjDTFtRTY0klIgNY4VQ8w/FcNu1U6oXYVYuRt6FX9NT2FyC2ehiCO19C1rpLw0uKuelKrJp/OU2EIuveEZYhFi0VTXtlg/styDpkKkXYj6mVnUZ5FlcgX/0PqdFELdA2UUhLgqS2b1XGuhDrtExQoC1MFxgV7bWDYVgrpJzQqnVkgueSSlAMhcQmF99u8NtRyttATF50wN7JZ/nm8S60EwFyZHNDFgxa2Vu6g/SDQoLnhVA2esIL2tWQEXTV9/5CkwM8vKAGlDzuoyRKcxUWgys+W5B+ShSe/uiMGff/mV8NqGbRYf2hpeAxQeNhf78hbaY8Ff+awBm8EbEabdPYYSa6mLvVpF+XzcEdAFcYKKrAjKv0OWVU2lTi8pqMSZMquMlJzjwRCpXny1VMeg6Jcfl4Xz+u9x3YZhuCAjHHOAQMe6I6Ov3nNdEGVlZPYARjJsFOeNNGGuP/UGiXolJb7Mis57cFDk7bZOuJoXK1Df2lT8N398cJtmRVIFWk9xZnytpHFz33mPuI8D4kloGmVvQXrwjJYk+2hNS1qXr9qjuVjkV2Z/3kyfpNeOPq92JUYTYmxMshBfuFI2GlxaXZEHvOhs4PdBkKohiFMPsedL+mhy3WDypVzSMknhr8DUAAnpP5mhJIiKw5N7y48rxmCqDcFvWYksoRFQjOAZeTn0bNhmbDPCQjgsOAwvyfKMknhnzUwYtWNGyPf2LYCXaReVwKvjQZlYna8+vJKudCJA3VlpCdW4OQjHKNoRKsbIFuF9TfGf6zTlroidqsvCPXbxwnUUFDfMI4bXKPw5R6Ee4wYRXKfXxB7YL+3yy6+8L1rXB7XrZHOmsL5yICYOzGFKIMOrcsMYeiy694rpG0b0vFQjyq9ZIWS1fSfuG75vpeqKb9NIs2r9rGQJt+x0zn31Cr02F2CSOtZZYI7u/fi99KjK5+tp8yVna7n5VW1ROzGJek9uiyfI2Asq9Sfa/AxAKXd/UyBHF2BaJi6NcT5WC0jGjJNdCZYO7dTXB4WxOH8la3BWFGYlds/qCqPYKMgmP/YW2FQ8RCHp4cNuQnbd70iIZ20vzXsF00yW9GDwl0mpC71aMlyLFgoF7pm1tK3RK6xUWKIU7vAFP5yskixEY5hcN8owFbQ8iggsywKZm2bMLrMx1bTmSs9Uuv7ypMwrRGsbwso9BoIeojatgdXEuUYb07U/q/s1f5M9G0IHKn6zlZ/57YIhSlyAQ67PCv9xHj57Gux62qnovk3VW72RUGxKN8V58H3UQ8A7wVwmb5A4qKrXNcBrRZAMQrgIYrZrhGAhIUx/HilcV9Gkh414opjt8ulzeAhu2L7dtvX9fTtDYXz8FErg6SEIBBVYp7XqFORsldktcHZrswuPwkIbNFLmA/yVm35SCPp8stBa2Zbv9eY5Wm76TBTR9aXIGAcgNrDqZqLCgnXreXb1eD2khCLVszZtrjdSjbTp/pYO6wxTVfg7pVhxiL3o2dPSd6o016S8KEg6eIImiRPUYis75gXxQAtPVxDbCp9lyQ0FzqN4rUvHZLiOZP+TEeFX0p32WIp3MMdeee9CHgao2gjCuWefHeHhhmeQt6jp+h0J8d7ILkqL7gtc0MFiqQtuYbFmhKezt6WRMlBm0PRddtG8MWsRkOoAACUQHyehGjhGkIoYlm1i6K4RGPD4qhTKvNgi5usOxHYIK5qxg7ROVarW/ByflYJy1SGA2AxGG9YQTE139RyJxZQppjPkp3tPIcRb6BQWOe3mHGKKJ7yF4Gki94ozmIZzSpEoNY4pr1Ku7BjFwjA1UmLOvzJkyFqbrld/ttVYrYukKLDw2hRIrPEiVnr2ADSbxz+KaRWtRZFqJG8BabuEyDhXj60hxc9WqeeYNCdelt3rS+gC3ze7rqE1C6o0GGpq5AzTSEAMAmbywlNYFQoQGk/OsOmFjzG/vRXp+P1qtFUcu4xP1xm6eFWoacnonRX5rf2fQflQ6uYHyi1bV6sgklhvTKnR2jRZout1RI+R1ygEj9mv3GSW/RqLBXo+qneTz6Os03rcvNfpj1oDkW9fxyOiU1x99g7E1sUgCFMQ0oWNuVDpfwmvl02OtHPYafArewpZDHOL0S/HWa32MaIca/3WatK/BZZ538D6Ys2Q1gBHFU09jLNetmCzUgiX+3273IdSgEvGdeQAF7OUFm4+d6YsrWmcSWVdNjQFNqxoFJq5Ry/cP9ctOI6R1oGUgBt4h3nYhYVjD6q7SPZJ32MdebYBlULgHKpOALCJi6rkPZHKG6mtELY4k7vUpTWpLB21LXXjHfIOibgraSmICWO4oezGBT8bCn5vYd3VTni1zaXXgessdnvRkL+kQEXjHGLQjN+o9kIeTVqVOa+lViJ7c1gBy6jIdq7+bP2ZhH+u9W5AEWIkEHyJGhZgyeJ4YiaLvz9YpaKQUICVF2YAslCG5PTVpIWqmOr03oBK5t8vSw9q6b3puQ7vax+opqXH4abP4V/W7pgOptRL2biq2XF+y10AVdcQMBKmHzCgP14L4fqwrmBCi60lxCKhtqX1OXuK9T3FOq7m5cbn5/NXI9UklJBV9hQcCvMjd4bb78aSmhlFNcWsukLlO7luREsxa+ncHNVafEMbTxi0F3S/ehEi8/kGaKtscmwaeAh3+3Z+2MXPLpTCBpwztIyjO1Zv+DJNzJXrWa7HXiKAoUCKp48zqCx4xZuZJe2ipR7cdN1drYyuG7CkcDKTCUFQs8QyUKy+CsMz7ZYHF2jNhS9MrVHAtaFgzdRAzBU8FwUGU2BlHUSnIg8W04hl0H8DLw89k5LlNVlLVJjzI2cwV9CfOqphsye1eT5bfUnFglRkgJPUn4Lzt3cufGkgT6UwNTCSQs2ALeNLWr9Qasxa6zxY8z7JdZb499RC1gLFyQLYvFSk2w4N+F5kGPh7XPG6ZtRJ9Kb12au7srpWYVeMhAj+91arVhpDYLautXpP81q/8vD7L3Gr9YRRfew4HT0smvLqxVz3KtY0nkO2R53WcBXMLqzE83Hyg+oXkobFkkIyq+iZNrpCXp8LgeNhcb5xDHM1GY43PZYlpKMqLfdh7q17UzHgNrUt6SnMbWS/DnxUNfrEd1QVwxLsU632DpYtumNDQcvw+VeOohhuDr1aILmoO3dKdDRDP+JtO99t2xG0ymjiAp4dDWFDLp1dFpmNfYYXTBwL9pq5wwftVDuR15U0T87XUd4CrFAXbeUGWcCJqxVr+u3Sge0CCkXRSAcmG2mNwdZYrJ9ibddGOlHjsGvnw4EV0pnmKuzdAea4x5JJo2tCITD7gbcDPD/y6AfIK+eswo9l3kxRThihp+oxk+wKJolfItiKAj94Y3dxetReiPpjBYZU95fGs2AMpSqFXtnOYD8pGTAWtTY1DXiOMy7uGo5cm66F0b3RpCp/25jg2trdtQuaCIEGGUINBUpuruIbtIIsyy62Ga6tnuR+nuJhShry2aCSw9WFtZXZ8lYa1tx7j9z+U+5+wrfCx/1ZB51rbI4eg4pIlVadbLO4L8Z/vKbk4RZq7fCCVW+UyqnKsC+50Vdu/pbXsPB9s7wun7PIzeF0k0ftglqmpkYWkhmEo8g3FFrGpb6uUrhhqQ9W/+S+19/9fjlF/9OTVBRGmGM3gsy3x9JfH13iReI9Tyorg8OyBSkaNiRy1oNgCtYGIJUQbpnayEVsSud6ziKc62slPdW0CHhoJAYTfDOYKxTumpuER4L4q7eu6PNDy4/wUbfiovpXluZgwSLEQV+6k81JEIc72q8ZldwaP6qD8TeEKq69ldtu7JzVz6plyCC0+8uGYCJKAuVEmGpM3+xFntN8ZKqp0mXLnMTzmD3DsjEJI6lwT99DtXP2R7A1XSz0hLpwqJUnqTuQ8cQ5chwnuu1ZkFn4pkKpyroWGF7K/Npixn2gIVL1rMDWajivyrAaW/AjZVtefah45zl/7Mam9rFLuL8F7GxA1f1UYwr1/cGrXbZur85VXgyjUuuYHt8UI8PCSOZSNQ1ylX1AFvJ8sNZd3xOkjL1KX/JFwdkwwhbGOcsjP0N7Tl+kDReOSabVBmWxTzXHuq9ECKryrwa211Iqhg4h+Zly1DUG+FWUguaiN5euLSoXFIL/XpqQ4agVa0vKcg6+3ThkcaTe1cSl5V0yNnqnr65l+Wll/ICKFsqAhR/7SO1Cy0dYuqgYdaN3cKSLIH3EyZQ2mYyrpnhgABKfxLQjO0gwDHj/UUZrygxZlgwguc8yBYqsf/CfVA9riKFMk+iUVAUf47wijqPrWdabF78rJW2VqTdCCE9l/LCt51NAYc4tJxmd0lnFeMlTunewFQMUEbOQJkKwuo7qfRiScywF1rRjGQmPjM2GuqVroSghjowVxBBcJChyQY5ZNNNBb1GUAovYtP4p/OUdiOaE3ohYL92wKa4cXd5COSsgSq/NqYSqjneyQCHuizmTsLNX5TjM5g25VOCuFP7yEC7bnpgQ3eM6l08UYWnCc/9UQ81vlb7f+ZyKB2Aamny2CwrIJ/PesSeYEKgMLndetNdQFaznrawRFiuiYNNpwfL4zKkUStnJH/D0xviBjwHa9ngG2PmV4zUvYV7jE0qVzAyJrtQPTd7wfN5UDPi5CEJSxiTWjFr+fr06hbz47L18qdYc4aUMPi8K/SLg3wMhaTzhdso04IKriqJMYEIyouQWx0wKL/7E+OJ3FRANDIyTK27SPGQD4CezT8Rc6jQ7fl/wEAJotoQ9D7ZmnIlkGg6Oe3oetpITmhKEA+GEVFIIeaST4gdC22k0Gm9Nm3SvgmRNVRNw58YHZKKUUN+nFnGy4jpVUh5GQI2CrMUL1P9dKob0HHQriZ9zsyNQcXhObDBkvwCJNX69PP+16S0cy4CyE/lh9c6sgExy15ZglQXBkE5KWR8Vzxf2SyrzJByUcmd85u6qcX22W+R0y3jwxTSHtWVJDLcoVK2zXldi5Tr3bu5rdOAKGrySYZNdeQtXu3PyBBYm6gp5cOP5YY5rrMVpwEk9I54rxw84D6VfB28xBH+s+24ERHYYeddFwW8Dt5UbsjGgeIcVxNc80qPt6akJKSUSYA9tQRlYmUterJIYEd5CsLxHLPP3SkmdtOdvmp4FPLPetJtxvPu79W+dp/dZUGBrxkczgFZw3Txn9Vq843z9LjAGOKzARCyvZy4/6goqvYC8EgW+qgBi/j82/bZYvKYmSNruagHPmdDeNGWzo2sc4gj7yNpg0PlJ7SrR59WelRt8lzmwsOc9sNlPFNfF+z0jKgUs4iMs7IoFmhTcL6D1FfREy9UCTL2Qrbdzxy08AwdWPeemOsB7WQ1sxtbRpc7Oe1RIiGNETUM2OqmMnHqObLyz6VY5hyCrO631as3atU9XQs+URXXasJUyRUZDQkl5j9XCH3PmUXR0Pm9YrZqvZXtHr9WyB8ocuBHTEGS+WsOjwRRGft59iWX0+Afndhbs/RvjC0sb2lKvFk9agRvmsUGRRJJSDPKc6RWwf7liQrq3bHbvtZRepn6QCp0GR7/5q/u59L97X+y/8rgFhV8pBn+85GVrjm4RkjKucCpKgYoB8iY8hXdSH/0VhHgVs33zs/pPWh9X5EflXAVP+9KxDFq1Qzldy5fv1MCfp1hCof+EMZ8cOCYi8zWN8VuAUyAqZ53ru1IiuLIyMiFw8rTICNkcZSEqEyYVgojy2LS8z+eKg9NiNjVyWsaFI8bWJzYQirQI/+yDfgLXw7nRe8ApfbW6G1TYuL6t8KRctlCJ1wkZudCPrT6laKCIUNuA4Bfw8V0oa2ZETIHXqogNO5XF0f+xYtA6yWWUhHWFhVSB1bDe1QxHBYirnfmmoJBdEQpPMArzuPUksJLygpYpzqf6B3qCc2C8W+NBxIZyUtcuxGAz1qTCROPhQVeg4kCyXkbDH46DcRoTzUkhlNTMSuvSOwkq5iJjJBsXOo2zwo6XG3u6IpNVed06Jud5QEDqC+nhKp6gDyUlBT9T6Tl6kDyKSPW6s+MsQJWeGtAroL1StMY1r+p+GowuLqx9X1p5lmVtXgmQv+JYiufUiw8uYEWPLBeXMtwMFWPvgtQSSgHd1wAjOfX38r8AS+rSMbhc15QWeeTLw3bvb1YL6E3FMHoUXZBNA5tgA1dPmpceWpZGOJttqbCrtEikQrBFrdhBQEOuMJ6YVrHhc7FJOFTLgfnooszGgJwpkTBSuMMdkij36E0hAdibsfS+tpmjj/MCW/cGgsKAN1GuFxbZxO+vnPi4pBqZmJ4DOL2hHQhEkOih0A0prHegysB9rQPFcG/lVA41PdhtXAt84GeUUFAfUwqHYT7KRvFRAtEBHyGpQM9EsE7g+hDImiOzzDrwXgPInhe3HqUMq0HJohyyn4bGzPGMz9DV7kPzIQssznPEOVYv0YpxIN2b90F6MkorNvzn0ud+0WHr9MmrBpoFsrK9fO/TPuzCfGnv9dMONBb1UvkBC2xT6/R5CA+4t5/ljMHwmOEm/Ig4MWBZJwHIo3Sl9uxdLU9oCy+hfb1jSU5N1/R0xMeKoex14ter3GPNjgnyTm39AsUASrJQMvAcfh+lkG7MG+8Nq+HKXOgGQ4Wkynm8uPoDmS78VuC5jqleNyewv+fSe24mdON6PrYNMpCwCZE+ebq09fOZk4zJBovoJX+EuevhkDcJAtdkdnxW8gokPFbT4k4G0nzA4etdW6EuunJaad/RtYVfp/HAdQSlIcAMyxbeAhQXiuWgfGoqKa0NbWwG000IWBum+5mpsMrC2UFcdMwSncb58yN7FqCegRz/Cs6HQjAZ3tRwh/GKwkSb3l6HTpLnxl5hCqryjDMuUP6m9Y6p3fTr8/nBqoSFvopuai54A4fWEMgsQrespSDcC3qJ4IZS5a3Xnp9HKoKi4GLyOvHhvD8coOf5N22DjZ3PqRw3DNmIRURf6Y7XD5tjlE3DPqxvRD3GdQWw76EoBKfczh/V66NiqAFqKSBTqxsSqX27lUXF7nqyjplerPXTMwP1rGzI8ZmW52aYyvxgQ1xOx5d4CV6Tb8mkNG7e8Lh8/fQShpOM7/N3JZEwfTqUwuYZP+e2eY5gHgyK340l9VWtUG+2bub8O/+YvjstovScumZ8fVjVopzHawOkLDhfrPRNCBz8pa0eX1iott2s21kVpeunU1s/HdjJi1bwi/4F/EAIJTqfmQ2UQlUWCSxPWpFejBCeLpgy/kHmTzWWsQs54F5hubsozP18e9Da81+s6WxgHsJgfbpTFzYUtZ2DhsL3bmu2zOfACIqhUKlJgdkzKjhvEsKpIIx8Teh+tgsvgplbjNyrOU71uli0193lSNNNSVwe5Xyv5THnGihytwiosC16/QQ9MjwrcTpRXJEdNjLMWEtSFQ1+VyAviPYUQIdCgSPHf1Vslp5BEWamLdGEEsKzAmYfBK2Z9EYUa9e8RPabvIViJ+R2Kj2uE+bE9zlmWM2dtmPxSKNk2IDyLkdlWyZ3iuUUQ++tI+WBe7CUtYu2mkxNDS85+4rYQFEqMTw+KgXXGWFvyBvu61fUMV5fySgsyMXU2rMByWNJuU3eaYW+Z2H/qvHqubz1TAw42sO67R0mFOaaqwOaLp3a9uncNk8hT9ZPyiL8+krh8ld+tL84zEO1FoYweVWXX3IsTeB1xhCVhN1R//sMpRCpiStUASNX/OkQSkGwCDNXsGihDPa7TGcND8F58xB+ChT3CxbLMawXYsfkRUK8wQU5JX3UVc5kHAXBnAJrVDjT3M6LS8Ia1jHoO4gvQ6ERxjHHUun4VT04ewp8QemT6nhFxcpUTmXTOACoUnpmCj3ct/NPP7f2+NjWe4wdzVLiWYZV3RWDmSwHT85581eP1cHn6T69XCxomHXohkTqeVxw9drQhlADvsX4ifiv8DEFna0Io4+3ICcXMMobZFBemWR8juB7ismUko20UwfbU4FaOZnevFKbZytLCXjUeTDmo+rzKiiUpz40XOJ8aA3Vlq9LR4VrEt4ogq9eq3ppixv5PUc1GH2NVqqcK1RbA8eCR8nKi70mA4m2jVJ7k3SycIERU+8evWuJwuNdljMXr6X+R79/z8tN/GnB+xhQi9emZoED69Z3qhFe6xWAeDyd2/ZRyRKP7OH7O9Bc2OWaMd/8TEkpe3OBLGnUhcKXgsMvj6tqbAddy3erhVlcR38nMTtZalw3L4e2fgTmrmboUgiAl3geW8MgsgNhnC3/QZAW91UYJoXlYKm4ViIonkHUlqmdOceok9iHQoBgJ/2zgsTeyM5QIvYbApDnT+vI2L8UVGFqvWob6f7HbgdJARvkdZfLQXUdIppDkBaCEEoStBcWfGw2j1jCrscXDvdZ7GboqNJSRHxtCtreXIcLm7F+L3Ci/nF/lb0K+1pKym9mWkUnNRfz8XuEL+wdRbvWyMjqWDeD7IeAkVab57TaLi36PMT8a21sJPxZbyIqb80Tx4Fq+oc9iyfhGSDzyJkjpkLmuJ/dS7xmEXUOnMxe81tQeracB2jX2ysMkVg33Tu9KbmyJ4LOM2zRBbi4PibHA9pc++TrFY+EClftb52N5PnEwbqTXfCFZYZR9chkXKy7Usygvz1UxgKRJVazDd+SXxXCmdCQ/scr86BU4CV7twr5K2xv4VzV27BnpcLMzeOxbT/vmZq6+vjUfh+ai3oTVeDOR4WBl95ehCZT60zXLHDKrRMYTx+UiidXWHLmnk+QBF9zBWQJCIJ+2KyTJpezQtgh733XVmiqDqsy+WU6HEWrzZvQmHkVwHqdMQl/NottZK3byhyER7Qn5OGiNkNJqLpFR7LZ4rOQxybCl8EamhWs5bnaFZ1alkYGjzOgBH3ULCkswn3QNV8Q0GRg1Bv2lBANLXCcYyZs8+Oq437V7Z7eu9yIP/gb2blqwn5lrZPXCUFLwIclh5+/iwOKytCBTQufrI9Ax74jYzbOpeeUAqErPYajeZGqwvGaOZnwg2rqu2073+/a6X7TTnspBRsPla4cwp+JAwUO8b1XC9uGlpl9DSHNhxUzUnZdST5YyeX33G9lry5Z1Ldkgz1RfagXkxbIijEMY+RqgMU+JKVRkten43fyvisMmhXItZETveLwFNoGmUpOL54SWgbo8NZa/CuC0O+Ni/IqjtncVjTJBKs6Ixq2T8e2ezy3MwPOv1eg+Xq04/jKTd6CwPoXp8OL6PKF7+VHFryMq0EuuHPOgZ4HrHTQbJZi4UrivDsqBHDUkBqhCnvFIbJStQaXOJwxO8TNxtMDqOljFkAZvI4evCPcpsWvClZaimxV6Tz+bn1FH2Y3OSl9HQSFOPslK4Y1frrz8irSodPmc7Wu0wBzum0dk44goJfslpYe1EQdMCiGyeNLA3a24op3YXoRW/G2Hkk9Hv0o+te61elALBX082rKiTdnvcZvFlUHJ3FO0mafyA3FmgZ7IviPs8kYEwIsqTiG5pe0Gndg5t22E37u5CWgql5xidSBBXqnx8LObw5iLwjn6oltFH/ieSLG4fkJLqb++Z6uWRfZtKWmPbbw0iv7sRiVg3Wes2a3VXOPjDlDqXqO3G/uiSKFYOWg88fpJwTCWWXRyDhuTUbSaqbu/l2Pd2RVxpujZ5Kval3YZUtDEzIFNUiguoencGrnzaqtoQj/U/RTGN+brNPZuigfuz6q9X4j5e3NAdw64tyja5ujKS54sbDMB+T+B4WamRvpXtYd6bXLZHOdR7pqDjex0AJ5yL13q0u24bTgF21C9RTSErLVWMeq4FJ0hwshFHTBCt6mEHWOf8xxT5WEYIDHEtQUEGzcUMjwcFc1BbbpHbF3szwTE8m5fSJ/1GjdFrMtOwwhs4quH4MFiyEk2pAuaqzzmPdTU0AVf1GqcFI3sFiptCMlFjNh4YbRILBZZxHeADOJxHHFZ0FvrjzDRDusfARDERqKBASeHrCfKSyUjHDZ9IwZPq/7bTt+2LXTgzwEZhwp/omgK+ZbtBdJsYHvPZ/b5fNTXydW8pOBE8FXrC3HijQFFIqGc2wASbniexWGqco6LekFS/l1a7DPX/HokybFQr4kETB+xeww8V55H9irVx+LAUqrMaYaHLeh4vdA++FrcL2ur4e/hErM9zOvp/lel6bgMs3Fe7yGWYnkvii3a2SCqalHZiFx6RAK/j0J8TjAfGr931nZDx7l8k33EvMlgE0nU0HLdV/XOrq510PZwPMtDAubMyYrszSaSbqCAgFRMG5CIcC6M3la5dvvzSf6PCVGKl4kufSEHCiAhT87SA0WztWqnZOPxmMw9bLiEPY0DHG04ilQqpSNPD23TF2tbUed+fP5KTD2gs+mVelN6EXN+1L/hi0gJDW1d9eycu+DxT8A0RWKKH8vdsiaLOBSNxI9pUuFdlJjaB7V6+Fqc+U8SLAjBgJFl7UmITiotFPZTptShIH2FgM2e5FwBdGdPD21WiXkhvWALDcQMD5s2uE7KAXVzTie7tKNzYpp0shAYgMhdgjcRWwJcRumPpcsMBvIrLAGzYP4DixInaXjqYbiWbkrXkANV4qhT8Z1v+Nh302vpKCsmYRFSOhF0k+UQjvTwiQPGO5VvE0J8eGZOduqrO+8RFHeqcBVMOeytUglNlGeIcElxGJByA8iZ0HmLJ2inOfNbK3B4yrnNttr/b73GY22qFFZP4tu5k1l/ZtoLjysmppW8rbrh25Z6JNL+fZAZ6jgluJYuvz8vfmzl3EuuZkhYK1tZ4VyKWyqqNAN8ryoLDXFdXnoieNL2BvaOOi6zhQArCCsOatwSbqmxa80R2L6dJs7tJUV1MZdXYTHpuWATCxRJoFap8XQHBv3tGj4w4DqoSuMCkW5rzRxWEFIjGco88X8/8yEsVLQw3HjHlukDopaUQo3Hxb60nOrC9+8UCLv82uRERUkdxSImdxSob5eNcu5Vqoj4QRWOxfqESm4gNY6JBfV33oWjLcgh77g+1SWUgjOfIExYQhJtBjwEI73QcLIFopawqhKDTZd1FGAqBFtTqG89hHrUvqlCyR77U8U3cFUQHe53KdOyaU3F2OlIcC5KDUrNRZm4ZXrZ/4Zt1rQYFQL1vUJqhYfvjIqBxbB1foTG1DqbpcKoVj/JKiUdz3T+k+Lp+x57DPsu/CeeP6zmGuXupjeOmdVOl8CPdWP3jSIp2FXHJGvuwf8rCjEq0beMxlL70RZvk5M4eZ71Sy/tqrqrNyEdoqbdNvbMAY8IpsDY+qkjIYMqvxQD4B2+uPijoqrHVZXNli39WFuHFuvxtxdBIaAK857QFvLUqxFIVHqFkrAOvHngwQ0BVO4z9xwzItXBpIEexfOOF+5Dg4veG98VyizzNjxkqBwYHEbCt1wXQs0fkWwRdJ7BMdTGgq2kDHHwuCjaZCyujjuQhtt0r25+Mg9HdSAPGOAS30j7C3RQyhCXrBApu1CCEjxxL27faU9iw7LhGII6zHmSFWwWTRWLHIoDkJqGhO9PK2zJKVTnAH3pk57qPYmfbc8GzZdA8/R3j111a50tWonzge8wHU7H9DjY6tYzp3IBwGv4O/eRztGWiAhr2NDXXyW4eHGekIwN4Rr99TUAKd6eot7ffTCrwRQeY3UKK9ZyNaG9rjFnkpvgfugQJ/2rOE5WTnb2FySF1YcthBqf3E1j2pp7LxDwKeiWIDrFu3QbiRn4af9Fc/xjMAk8BD8YWnQ2JhjIakbJWneyj5hTdBB9Dr/ySua60OWNTE+96IchNhcvbf0khft4nhuBZdtnUgLD1o5dUg8nLqIk465WIL1GhulXSaRV6FrMOOoBUcSsQXkEEpIVbRaAPYSzP+erTovhSb6VHBu4PxqHRmpoWrHWFhK7QZnKqVYHsOKqP0WNEcWFPZsLoI3ME5n3JifnnMgz0cB8pgvt/yURadq4fQUBmhIyteWrVMD7cqrBiOU8KTNfRpTgpuAUEy30X3MrRiVRqseFEydxZcNHwj2qoSGafG5X4ax9aFJkgL1rnHwy/gbgt4QhOdc7JV5P0pFhcKHN9KD85F+Srp2F0VnEaMECAv/NuLj2gVJHqudQyi2gwQlsk/kPRkKSi/A90ga82APRbU0nmFkH7ldV4kBveq53X7r5mcneHmUK2OSQbKnOnUb92bv2+u2ZmARBlu4hp+xC9a8Z4piaF5XXguvKQbDV29BSsNXnG5ePpfjvFHpnEqyTEtRCFFYqOc7QMaurZEH/dU9hcHKv/GZGiCuD/jqc3+FAqoWz3uwOH62PAFjrIYyKk+Mv0ZhWnB3C2ril6o49YZBHQPcXXKYl82Ew3AElYTcOnOuqC5h6JubuCesoOipyrdOyvVvCgB7bsxVNG+stBJKxbMtszldkdTBytcH3uwxqBDvcn5RRs5LFvzkpqnwgGmNGewOIJwQlBum+LweAy37GnOAkgtKBk49rk8YyI3Wy/PPBS/GUcJm8rr4XQTslfGDz5XrOA7jjKqMScwxD/H4M3OFNQdTOl+xuHuwXrQlcNmrted0UBIlFkZP/Iu6FwpuGVE4BaZ5V/omFApx/h+woOjPGfcg5BLQIY0c/3cd5H5eB4bYfK0h8JoU1D09OT3ArPibp6BDRF/CmZy9mGcIqQjEQSanhSXCSTa9Ck91BW/Yc8rU7Yhl5R6uWHyuP2XXtbrfejHjpfRAeY/Gq7HM9IZmQ3S402rp99dirc0nrwyp5WqGhUtAPeMLhrmL4UTP8Z00qb/BU6j50At5r0VYX1n3cwBofLO4EHqppqrd8lAGRfUG5ucPUqBOAsbuG6t/O5NlfhVjYStLwSiqJqaVh8NFQ1Ig3JSbinvKZTYEwYyd0zLWX+Ej/K1skWxOXigpenc3392CV5SKehSmoRiCSjobrCizg+dHEJMV0IdoqMOYg62TqfGRoBhuVr4moV+D7Jy3gL2SktuPzVYbxvSMqmCnFlaYxoR9HT4KyCgfUsAICNozbVTeGBS5eidw8zmgKA6miD2MaAP/VqZWekPOoLLicPUsDlez43XAbnXOBaWlp6JMrrilcxQagVOLsay+TC2LkSC02jnqDK8A6w7xotbOh218l/PRs6A4bi4XapFUDm70w5NqfZJXqXq/3rsi3rvC6Ad4Y6pXePMIr2dZiaQ26vHKBOu0p0T6RsW7gcDD/ZbMKxagyXjx9z1ee//as1zPafQVT+GMa1ggv2LBpsC+vp8hLrJ0mwuvESrNaZhRl/nSKnyrvGcFMoqZxhpxNfuJKc6/X0zBF39nNPt9blW8mXGDYVG8o5DELtZrimEYRGWiLDuRG11pgOlGOmhcBI0DpD53wh0SYoZb4KI7oRYpddm1TZZ1QmrTPfK2lT7qGIbw1Ko8gswPQqfXBwxWarYQ9G06wKsUVmDICeEGxpybBAJWnz+jUArf8SJMgdAtFQd3ow+gFKfYWLuVpuBzcW8y3dgZPVJEyWNDD6bDORk3cGW1Fa8ZWzHuzJrC5/R9CEQ+DccHIjgfEFGxOky3TczZfRFK3+O6aEVRHrh3dHDjnB9ewjozhQLGC+XKuS7PQ4Ho6K0Lj1PTV2SaFQSE93l7yY6AUAzrfXBKRQOe4tV67VDRBpcQT+qkheKhVboPM+f2GEH3nKabf92Kvnq7pLC+ZsDl2rJCKt5Cygg1SWIyhZrnlOdBxUCmW8OBZVC8Z8e95CUUXrSm9Gmm587HEA/tt7V4K5fXFMMSrnXlLL0i6/RweO3CsSUEKfJAuLiSB4swMGhmfpcmO4Q7CnzwqmKYBfoXXW06UyaPXU9m0djLiuHGACYL3dZhbJC+wU33kMLQE67vuuqZHy+8R8P1C6STfD+VvvfVm5+Er28lsW8FwOGtQOBlmlq1NspQbE0ATjCZHU6Y/YoFHzmgF/WlIn7rqagjrCPrpVwyrP1C+yGXPuCacXiRVmg6g/AyYEGnpWrIJetFpgK1Mh8ZgEyCPneMWwlKKhueSEFibrnSjDmnYtDm7u0fu5XdPUjFdvD957X6VCBVVM8L98QlpA1MwdTXECpON4dL24CiJhMconsWaQpK8x2yp24vosFYcHO8Nubn77klnDCRJlqhuJixLjbv9TkIurBULd0G7LyO7z1yQ0ZSQCr1s4otOHMva1P8zLonfTnDG/WgqpEprxPebPYtXogfvut4Rbg5Zse3Z/f91tGf+zBPt2QEa2jwcadL4b6xTh1fKFDxO434L4splAmL1C+9MWjOPviu6Lw4vnTC80zdr77cimH0TZ3vXd3D6JJlK0S/XFsRuplJxbzdgYuWlFNQPendcicTaB2mPQm7ccbTba1ceSxdWfVG42VT1riD1/rAlrowgxXC1XmThK6m8vHDyhOH5Q1aAV9PsE9akunRKMthvriFNQ5bK7PCtMBnhbXiNcoqgncSMU4F9/2e6kkSvrRCGGRHZ0Pln5WBtDRFulo3nnvDFnleeyOliLB6FqgdMKmgFeIQO0EFslJQDV8ggO/neYpOWWC03O4iQM6sKZyT/Xa9jtSPgrKy0F4wM2WGIQssV+eGUBYUVAS0fd9uzJRzWePvbzgKvVJ6yZCbDJRXjwpHzQZlea8mGpCVFnupFO3NeOA8dq8lxpd6B784vlBIDQGQW2/Xgt7pWL0h5/jWwr7xwRiJ1krwoYRiWE1x0ytj9Wu24+wvjBNR31/yLkuw7Oax9D29loGtCUK5wt/e+3Cnzk5pXZkt1Yrh4s8iYOM8/YLf+RzEijUvc6AxF7F6H2eq6BScrw/R1ryEX09x7aX82VbQBTwFUhpmYWmeNNYIBo/PLj0epe/xESDrBdd1cVj1AIjRFxK2UiS0NJah8Mm0EbToXeAXbURpZDPraoKQCFXNy6QIBKYOK/3TsQoWJSkO43z+9BIHaXbtkXndJoFiKV4jO6yClEUoBcQYcZUwAtxjO1yHoMxQRTEaOz2d2vYxMmPWLyty5/GyoG6HglBqbjR8Ep+TPZp8poPVVBRm2XduQCNMfUi/Zmom1rmC7G8dgzB8r+D/giNRgHqtHjiPIL1TrwtRnr8bxnP5uxsCka6MwLXhmN9h/L50ztKUkamaDv9+ZfTmGz5L0dLDfo66hPDspRhE9rfYiOprKIVs95eu2NKHqqtjS2sBzh9/6Yu3sicOr3d3qmY8vHrwO5NrVxZVf0FB4KoQHJTzvWTufemMVe6Zp00LS0qjWMjBhRRB0Sg2E2wgPJBXUgppEH+VjBEvfs6tGVaNIyp10+Nwo5bq5l/N9cJrqbgmpe5gNnLZz4HvhwycGG2dQ45q2OE81bOpFrfnVvflgJ87yG2tbFHT4d4PyjDxplYgMRXPrIDw+1YFhoR/ZAnjCghyyzqnYDDcVOIcg3dQ53PySIINNeYi+h6AF0vb6rgPi1yW7Zk4eKlQr1lPUArkqQnvbAtYiIV8VgKofEZMQTi70FL+6zjW8Hinuch7wP5RdTB++L1SG8O0VO+B2/ZfnvdqK75HkSxBKa+5uAsdG+0teH+kR18G6mBzZRiQ5x2eJvZYkARmRiIOK9KbkM3sOr0tbCvn1M1P20Bxb3G+Npyl/3NlEfmew1AIxVDQjCKTv3qgmeMZ3J2FC8md9U0NE67vXc1pUSjdIygf6NjHF7lz/fmVTKDc7Cr4qEKlKL2eC9+t9WT8xKdYmeq6BVFAMH+6FLVV6Ej0zAxuOn3SdrjTOa0k8J/Ki59GQxFY2dC+NrEvwisF2vKDHMjR6tzlddTKEOlv6A3goiMvyrIQcx6ZfXQcr1EWeMIJVh41AySbIEFwihxQPEzMVsr79hD7s6vtLvs9qtsbaUj00knFf/GAw1p2K9G0OAukNysEP5vqAdbiMAryyCtl/wtQoShjhoSASZNeGhux9kBK4RMK01w5HddZK4kA9wE6DCsH/HA4SGYQtxKLm6Y5GMbNuVcWEtauZAVjIVjHeNaZnmpGVaVTD9pQRp+ct2UBOinZJRyqQl0+/y3oZLiGDChkH7GQsqcl57UUn+qFauur55fFhV4/l+ni8566us9qQCwf3Qv1ed6QYUMyRwx4cXqvFKZuztm4NNgKVLqcWfwVs4/egoFy0PGFV+ksFt2kolgG78KrWJ97TZvXMa4mjqX6IK+swJJLD2FUz4kf86Q4Hx7sg8SVRRldrdaSL5wcRcO9143rjBrh/rIszVvCO/VmtUciIYtS/R6ILwLzyrrr7/XG7TcOxixMuwHYKhSEu77lPa4x1rLZiNeWfOlioUXOfVEI8AAqFbiNiNp3gvO9hEsv3Fx5ptHKMuIKiSIAApMS52y5zwQEeK3Grh7h0jwNSiGq2TlGGwS4JugoQCAIz9AZZOW8JH/DwUK/Y2tPEUNZoX2iOIkyXsUA8Iq02qS5uMecqUBOyo/Pxb0ePB8DpNQTHkwemFj3esxCstHSQy7aE7i/nO7Yh1E8ef14xj9vGJB1PvPtCSZZ/LysYgecq0KvSt2V3e5uF/hh1tRkhT+gS2S5XW7A0reONxRCX5L1/quSW7hGyiPfaKmCsOwdkJuFySnpxJle/LXho6uLLrmSNTDkI4nD3jvBHZKva3kIPKUp/d7jjcnI62CBafrVg3mgiSjWBb0H5H/7HsWvP2QfWXCo/H6xrsPDU7FUKiR7F0Pgvgj6oh9lzssDqwru2nK++nltHnNPTrDSbDnbg6DswP0iA0pUxDl/FssaP5oFmbk0qcv9MQdH5UW58MsFXvOQq+BT9kxWqfLZhNV9Xq3bGu0b1RQpvRhTHLDaWIJO6bc358ecM05NFnkeFZHWA/8mK6q68jETC+ctz64YH7YO+a9iELn+lOK8Rk3CfXhR0WtBbKrwhpw9lvDdBIHWfWO8Pb0tp3pOMFyJd2F+rmsWipH418YU5phX/XdpLdvj1PyzgE/rKOnTzYG12ZcWtcquQsMn9lfHPM97ZvW+W3ojyDzEC/qXbsiwJQuuKOVB876+Nn0fmUDiZ/17KIWQ7yQTuB7IzaN6AL/pgpPC+YLzVE29cPKht6kP/I7MEDe+MVTktE/k7cNdNSbOLBcJOGxOPy9CQKWq0FbffAtXLn6tXyjjtCAbPBxdrEI1ZaPPU9H7PnjB3ciImCg8MrNlacw4hNuDYIxRUXtUqfAUhEeLU3UZM29NBvO10aPMwbQdUhKENpx+1zfGGCAuhTzl9ZBZkbqZwlNWY/aWZvOhki7MAG8xlatR4rPaSyiKIZSRBJObwLhiWDTeHevWs1VG2+UoEYJ51PpLWMTEhIKe1js044ERIsjKngkTAdSVzUNWK9WeXFDerutEv3veeyC+rMVshDPDav3EjvmFgH6jv/qSRf7aF6qBZgoRsePyGbNZdo8J8lRZge8xqZkVDQGlQpuapOXSWiapu+U1DsjDkjIoMnDp/uq+9uf6ya6VyK3x3Zy798vMdyuFvhfeefL6sF/VuG9pr/rFL1cqy5q6vJDQRl9k3IwmoFNvZPYR4IYTAVzCQC6YMQe+4JsMiJaWnLL2s+NZzYaoFtiVV9n7+xJqMad8mZbYu9e4+5uewuC216lesiwXPMFBKNe4jDYsiqhc+VuKy3AfzA5ylWm9dKVsUM6kU1WvKrZ9D4Q2arMZpRtDiWfHMnt1vdtah0LtWeBcKljDW0l5MFlqtfWplT77NtAl6UvMQjdhGSfTC+4zR5W6+pliwX3ASSqoADDvTz0sVvf7tjoi+O92nwG5uTlSL1gcqU1qxhXnR8H9wWsYHkbP8mJkEAVZNeOpL9Fxnzt+9KZxOgmHL5Ev+rxjf4NRVJo3ZbW+x+P4mzP52K9CWTqruJHs0vguhKOv4dfSwt/zfd3UFMvoL5cvXXsPS8pVMNurUNxv9xRunPCWe3f1mVsad/l7VxlGVxPyToti9YoWrl4y/vTmpEupRbPdRUtJNEoRVUVUPOq82QawMJxqIwZzam04bvcVFn/JUFq656pQ3Y5TPPwMEBoq8LWUqTJ4VreeWZ2TodPUpKQclxkUwpKVU55JXcD1sWcjGsNsVqSVXFCCym4vA+nuIGf0Sefh2DWfM55sWIdYvnltAmbhZTaoBMZz3bfL83M0B2LqaoGxfN5p41nQsFiIWWG9SrgHu5XEYGs1p8DU4JMxkpXimsdMRa5uf/SA5gGv5uWurQ97xYbcv1nnrzUcpfo7mFBd8zJl7+Co6dV1TVbKF5EORqP7hYWQW7N6uq8dX2joLQk+emhO3ihkhvO6HeIrGm8q6ppR2FTpbIX5PoE61rTcuq8C2+YWnc89KefJQe+3sLr9psdcPcR3Kqv3K4UUJFoYruStk/BaUGg4142g0y3MeDjVgnK59YURWbh6b8imsMXkYiM1TKFCQEMdtsqMLBEvFFpsLpASZMBTeyMJE05807TMaJVXoZ3qEtd/61xBIUAxsZevMp5SqIQ1zNO4l7C5Ua7mpGT6UICUuSmbJwNUHk+FTwYrpny5Kl+ORwRxSSu+8FCcseU5TaFVs5a6gorOXFODdgnWWdBaUDO70pABlcuac8j+2qiaJgusnvWc2pnrYww+A6bg8GylqkJ4iOk4mJu3rv3j7Ccq3IL71gPzZ5hLFn40TVmx0htEeuQ8qkqV9ygWvez814kRY24iDTdouD1HxyH2t7idqGwDDowtL49Lv0dyQIa2QqAORsQbRmW9ztIA5j0xvKWuf/jd+2CAOyuLAIyNQnZnZcqvlww4pvBAYbzV17h7Fh2uXRboV/f5pYdk7aB0l8477FsneNSN/ru243xN233peatZeVsrvKaDlz/dTx/zJaGntweByECzNo9jBKA4JnwkweUOZfg9eyrUOIKYUwvO3CueZYGQwHPq4nFlxXiAyNvvsEv28wU2nzUQ7paFc0Re/NCJqlgJMcxu0YQ1PlsmlZ7AAmv2IG4tbL3n7lUZEAQ+DkzcbKJmChVNg2muqTyLcB2uaZqVGsdRSqc3AxvtBIa8Wolr6Hxua5wbz9DnEixIhcTvHEKgkPjK67FAFC6aK3xLaTnjvlAMJpgrhWYJgub43cjIr5mCu0J6pWd0HjWrCP+y1SKC26eYg/m5uWq6KvKanoh3TEOdAVhj8bOC0pqvwVuMe6af4D9V+Zf8+NVX8BLya9csCp0unhwvZY26j3hAfFHVvnT5Qpve8A+SBtRo6y0IaQYi8sUbYnGGmN4FmdXviE3N0O+S0e3zVo/9d1MKwwgKhveqZp++9obxsFiglueYTnZ5A87w9Wy51Amyqzmsr5I/PmTHdAGVuGzNcMEBAa3qVPdH7XUOlc2zU3GPG3WYhC4EN8LgIcT2UArA4wMmWCWfvoWPPYi5tmOc7F4gMwWe65GadHrtlitaIC83DRose3kynBN5B+bfcU8T0mzjnoZKaWH2tAQ7Q2zOP3P4ewEgDwUXOS8U/LhuVDcns637VSj9d5WtM9XEyEdJPU2+ItWgXJroIhikLMFxegEOQivQaZptEPRpXWS/ZXo9ihtw3pEiq/iWDRaNzdTRUBRrwUVURsdugFgZUODDGzsqf92eA8aG9elCvup16d4jwB0V1zHftgONEjhb6x1C83qxvFMY3vCm6yL0a87k8udde4FsOPJOmZyyKO05gQIH1yR+qQSBr2QZeSizzFv6bFUg+e9Cv5HFoyqapNnsMnHwzIoM+cLjr6hTKJDSbGV+yQO9cbxeufzWjS4JuHpJl7+X4J8tZ7fWHCxdVyYX/iJkgDDFsBfEpLCBWw8XXwHrJJK7NT+mgx7uv/cXzmwZVeiiTy/SEenZgviKQrh0glu6dd3/TTrzVO5vQ4AD75UndvYgPHb+gLV0LA5Mr4CKQR4D/rD7b4u0puUSww8+/Z5iaUpk1zSIXlvQSFsdYizs0LbrzxwHxna3z/685NBBFbUpNQQ7ZPKB2obGuKOPNrUZ+Yl6s53wrDp3Py1UCV3j+mb5zHWoRIKBUZaQ5ksvlqTSKEYKICQKt2hRmRTaKTAuff5FPY3OZQ5ws93oofeUjh4U8g7d1xkWs6vHOVyTOAKGclvSW8et2oQ3FMKrkPANWVO9hUpa6CSHl0M7v0QdiD32RYNstZq8BSnKLCIt664I3VEf8KEWBeDPvRKEztenex9g1OLBFsUQkPsEt9YTF2Pt961TWPaZbh9VMdyM0N+6saVrFwH0LoujCi9ZO5kZooYqCYjOX+2WyFD1Wu8jA3uIOxwDq1bHo3T9cw4spd0esAg+DzN4Sa/nr0A/VVIH1FCiJCm045WBsXJJqVbUYI4X+FGX73t/XFkn6UmUJjT2tirHjiEQN4mpUERVxPNaI3eU2nbWxW5MPL9rK1ufYSeyu8zgCsGvpu+IL+B5la5xTkGO8wRf1QjJWWHZeJDH4mLFOocuzjN77RrN50Vnzb9NqS5PoSpZtkJV5x1nockAoDI7SCx4rKWLmy19tk91AFjzEreA1OsgVQK1SLaQxffNi8TsKtMn4N7U3c/U79nkPh/eG0bbgkKY4SDP25d4EvXcuC/3PWE6M555PFv3eY4ss5lBtDyzCExEJtoqmvIM8Nw8/gpV8xQzLPtOWbkIbdX37BXUGoayy291b/uC4zf3aB7pC94qBc8vjUL3lqKogsa/J3RT3rt1zDimrWB9l64zsX2l+plojnu8WxCsSWATep0vO331bmaxCHuWAwUCg5eHTmvBqmjTPzszJQaW7TFrTIUPNqCFTJWTq0shKtqDqHh1plPpOT1Y7QtzM1lY/d9CD1IWdA+gTh5fjnnKUMrn2VtWJstpUQqZIURYpyj5QcZUAyBakK4uwP8VE0gW12msthy9bpRAwGyy+7u2apG5w+cuCImtNO0J8nIBHWQlOtdMr4p1QWI2E7KgBFeUvTvALw5ee45KsV/GN2oGW51Pe11WPGw03z3UDHAztXIihlM8KmnQ8Xl4Oi+T0ve17M0mnATFY48d6cDq0meW2moAcN7l8VYZmfv3lX37Hqx7IY6w+Bm3PFUiCKfSFO5KIY44ivpwF6VQmV5XyjBcoVrfXc6Wrjes0ek+hq+84Sncmo/58xWWqvZxcj5Nikt79F18cX+VUpg2d178PQ93Vgj+/bVBzwrhSzyD+TVvAAQ3k3XUWR5qxOKFom5ZBKytfU09kW6+FIHHSetNGS3JdeQ5cpvGQiznvPThnuT+o5NW8i+dSqxCDd0lgDOeIMURC11ez+xWSo7XzKKAS6egYFr1kAdL+euT0q5CToJuwEAzflMWLukdwluo2Vv55KY5SZ3JXgvIyCod1kwv4XHjs8aCBXnxeW6UWVbjRWwmBG/i1M6HJ8E66OK260WH+g4zlIxHu27Fgob3WlJa4YW4/7bvx4R46o3MuFBWXhdoa/BK9Iy8Zn2NfP6qvrZnJWV3FbyuDYEkBAk71XRqeiNWqjqdYa+NMtY4r8EyGzQZVVjNbuUXxg7aFzgdnu8ZRhpo4HumHZ8HjZCpjgPKPoe/UhwMOxDzBa8DhZnFW5ivOxhgdZ/MgnpSDEuv1XufT3P1fuE+0rniat1bGBGC39lTuB7kG0L+PQrj9z4qdk/OG2PexmmdE9+9iayY9KarDKBpwctltwCHImHhkbKCbMUW4R/PURbnAv0F14GDZsB9t8dcrES7S+pkBjO14DP+kdb9vMJSM3ROodqHd44z1J835ndQvMP5YqFGHyRvlOjJzK5htujm/VFc/OwxbYI8NovZDoIur5fZMmhluQ0BfBCuXIW7qpl51T3Od2yXF82hgtur3T45ghLiyewewYUD0lWL4UqqYsrJXmNBmGEJWrPHlXM5CVkGel1fYIpzPbS03MsazfNpipxIkWO14OwKLOSKxufzqO9FzG+xzBfWRg+E/lYo4x1a4abX61Oorzprg2rSRIV/R4+nZTZWwFBRqV9iEPM16vVTWHv48+dc91Kue0sxXJY8kml66uf8mb8COvoKKakLyoCf+WsVQn2o+Z9r1+jWcWXV6j+uTmXbSQdAzVWkH+ejs6fprq2zuEUfcf44PgfrHYKj5MoTagBvkvKmWcsAC0WxhqyyVOvGLA5asMQJC3C9HCKDpNQPcNw+YG2qAXxAGvZmZHHWxh3+MYFbWtLTmqwxk+HfG8qhbpZ8RtPD8jXq+gAc9KzqcaxGC1ALLbGkVogthFFkC7GQyySEhN3rpta1YOlZCMiLw0+kYu6CTM5pv1QQnQ8pny0GX7KFEnenA+h+uV4kZU7qfFTIxUKoChJ/pzZvKhQNPgfXXX1GSeRWPdjyzAE3mvbdWUxMcY7am0Eh+bx5/sjVzwrfVj8vL5e4+9KeLWO4JRteO+bPplV8+xzRG8TPTIcyw7oSLTHBeu4FGG9Fe8kw87IBdz3GCdEZ1kT+55VzLN27/rMoS4vRsKgY3x3R+CtSUpfuaUlYvBdS8vevHvaIrUXg/72Lat6csWD4jhkTRRkx9D+wBY2Nx01TYTJrdGGMSXct78LWuoKScR1gyoYKwusIiBZZItdWTZ83V+IKF4VVLEFO15hWXh+yKRGyGxVhCgdAFzYXfzot9/CYqoOR1tSoGFLIVsw/BV40+RgtmIVnLWyeqZGCO8iqZSHioKsLACvHErwwUEGI3C7mw6mqggVrS0b9zWs8P/ce0AwCo+ZBipb9nQEp7XqAXILaef1pWbP1qaGyarH2OfZz520PndGWAq76j7h8vJ4CrilKwuR1zKzZlu/a4lcaJcc5ygwKOjw/FEKi/uaWNV6fvav/6nOrnojZb4cvyyOmY1S81UXreeG4tS/e+93BI4i+64Ts3PDoSl7Bo6+9qhGc13MzJo/EAHsMS9fMcU7T8CUSORVCPUl5b3ppeH1+oaAUX2KY/3b4aD6WLvqlHkIKXyFjV6n2ruf3pioL9xV9kp/1g3OaXpKyzd2aCkxUXfgaEK5WSzKnSiHA+vV1YIE615vdxLyJxkY9teFxKAMF+pzB46ItCAtYuXb9PWzXRJQ0TmYwMcuiuAH1fnLxdTy5T54gmGznWFpN1vmtFmmd46o0tNl6h6+APDJLR1kw2ZOhbgozXgrD7y01xSzqamSk6a6cU18bJtVWi4ISBijAcQjFFpC2itcBLyGQTW9NaaRkf40+x74vzi/OOXh745ohiDKso6X8eN0zCQgVxLbHySp6eyrihRIVt9GPnC+vFwquDtsN+wKKBD0kHI+ht1Mp3Wf4qr7lcRehmrGR+UIe1ztgoHfJk3ecRkoqi1St3Nx1r05HGjzlvOsO+8HjiKkNL4F70s/5NeU0y553aYXiQVaDYUBiXrleEYnLH3irMvtrK4WvGS9Iy7NOfH9yUb+g14bOatdaodJYuI2n4ZxOnlY1sWAjkeGlpThr20lR1A5pTGsEtCHyNyoEBrGVXsjAoDyepdRWHGmt9CyQYZzu55CCXAJjVmDyZgacUxtmaIdZoaE+fSOjanm9TOpoDZXPmW+okwVGvrgVA70eFXChFzGzaI4l0Jpjk6fAjBEwge5COSGt9Pm5nR+f29qZZEUAZp59nQ9ZzYQCcFjxU4FHoxrGGvAV0V1TMfI+A+5j5g1iDlZ+yb9UjAitiahbWV0rg0pOZ4oEpzDiJWVlOYUyCh/lI2ueB/iQRXL63QysOYlTJhaud3fH5j9cj5pvpuTSGy39PHLMpY1oNZiGI9IhLYTHxjtfGmy+lRb+JcqlwkfKzqpGxUDRU8a4qvxJiqvAi2TzoWC57X2nxyHHv7EnrxrrDJ9bgNKWYNcZmr8pZus9vDEXv1/2kf+9gS//Fvxw6RxpkUyu+eBRvDLGwQpYsCCr5UoLULAEmqhDOAwBzL7AM4BpoQvX2tk/Es7kTTIuTqs+skRcwEShmAZ1YfZMC7R0FFtyF9OaLgvhiq+oKgh9ZoYwBot+jiO8Evxa+nteF5pnKgIoBP4UrB8cUMeA0QghIbPKBVQ1qEcqCVUo18QAnJspv9vWXsRiayjHzYE0lkjtVUHXwDvVFWAy0QpuygwejqWsHbGV1pvuS151ClwT5h9yDUGxYksqJBoU1ZRfZzO5PiIvwFhRgaoGD7ZkwGX8UmOpFj8MFXpBoEpRuqyXeG0CRSW6ulZqdY3N67G2wiz2Sgr54XuvCPhBWL+2+JY+UrKPqrVtrizXlSxAURWibG5HOlDCn/rfTt+9GriVfL2X6TPdVbkNJVYB9qZCmAKCxaCy8fclovi3VzTfUghXI3vnUddIcc9GT82Bhdnne+XyNL46FGGBO/Lul0XvTVE9BMEHle4gBUe5VjJouodyFsaJLMweABeZmuLkIimr6MpCn9JK8x4XfEZbqrW4KDfH8rz3/tQdMlrOlJiuvfD8+6MqFhdTM0FCJ3ZUQ9OEQ3rAPbphGX9fYOx0hzPloefiSOrqmmVTUyVjZEPx2Tx2jZXzlYR802cWobc4Z8jEssEtnKxQkj9JAtzKmckAke6YV1McgQqBijBSoqOwzoHosman5+m01+4RXkrwPOIQyRsFRa0HwoQIxFrsbQCOk0BlDxG8h7HbUMn7cne9eY39DvJh8Ozfc45xHfKoJIr8iFLL/VxmQYzvuaBxg5ig6GSW5n4+6pxcwVW3xn9btvZMxrqnO85e9XB4OgVbeiei8xuyj97BkT7jkG95DmmtFwuonOr68/rPqxN761LXAabOoCh+/AzwlkIi0SBAaKWn4LHMVsFrrmMR+GkdVqWQFMoenfPaM9JeoIByrVqo5doH4PY3N86Uwz57Czm15vwv6aB2MIZbvOExmntHXgJaSSaMI7ZYOORR9Bd1CzyVU2slCIJ4DkpD9QAOvHrduDiNwWXBgAj6c98uUIzUDnn1nikg7PXZgxQUJEgnuqIpjbZa/TMmXC3Puonrui2fi49EPQVhIweLlVQQRWOqWTFcqfhKF2blWYi8cXwe1WApdQvAzeFB4LofPwlmU9HXZDz13tgsvVGPkVCKCSH1BTRe/z2C/ErGlH1Sv/8mtj97rDL0xC+VdUKZMmwadq87e2jB5XUh+eB2TPm+grivbqbstXJ/w7BrEP+1c91QDqkwfTZB7DaIfb336YQvVAqDsHjlmB/Ue7T7FSxR8cjx/eR1SSV4A+PEZ00Ml4s5zjBc16X/FCIRUBpgm+yXXLqnzRPMU8vKnhvI15HbEq8ZT9UyTwoJP8z63shUOQizocrUVmitjM1B6pJ1HFVB9LG7H0BugqTjqOMavbbct9Xr03kvpRFRKCtswqi5oAWL+bH1Vj2zFLgShOA5diynKkXCHyVLx0K0Mn8aRURm2QwhFuMluJJMrldiTa5Qt8Wcqb2AheBl8Ia6xwaF5z4QjgvkdGkebZlbqdvzkZfguQG23S6HkvUmymc/Gz9LW4W1ktxHTT/Fy9k+VJlXUAzbbTt//qz06668Eo4radyhrF3g56B+XVjt/DAAAQAASURBVBtXEuv9iiEX4uQxv6UY5ve9rmsdRjq73kca5hpxA3FS+XreR0mU6djC+4O37xLIN+XG0klW5fZLPGphmn+/Hs3v1DJlpOWFN9TUjUXSPfSiIN511OsNEupaYzsOQAEky0y0tLQOXZFM6620R5wtal/CgWyfm7GDa9ywC2X9zo+UphiJN/ah5/e8CFypSWWExS6OpUEq29soqVzpsSh2sXQ/FKRFKC5mzFSvrbgPM8ySQv1mekR/31avPLNMsRVmzn+Zd6/LV5jPljkUh54XWlMmgZ1datUjRF/mTqUc3D/6A+muWsfJF6QK9a6Qyzq69EKupEEBRMj7Wt/C2MprpcjMgd2KhdPAAdunITNAg+oRXmGVwQgowfDiifbPta4YACXtkQhxx6wnEjqWrC+u67u7TjhXSOdYZEnuKCmqNH6sIF557m8dV4Ks7OvXlEv12NISkHekMYWyFW+Up+tSoCHPq+XZHI9kfGcBQloSdTfg1n5LRZq/ZyoG9MVx11ogUYznpX37exavXWFcMZprnLv+++r5X8nl/tKFNQT0K1/6BI84cMQGHHoP6YD496gmJw74XQo269x4n5fxg+CH8cbOamhfp8AI3kDVe+Qay409WQapRACRaKzGSVFABLIyF6yV3LuOVhevy9evVqMEG0v6OY5C41EzZmTkD3OpIYZClYI1zCH4Bb0UVhRkOu+pcDlZ8dBbw/NQKqYqw1Pg13oF8gD1DBpv5GCmxTnjGQ79CnB/6mLX+0QbQlFMhWR1IjNM6u6Argw3REpwX2gBO0Knh7cQ0x8Nb6wkqpq+gjCvDKBuMOQesxGQmL49wtIxzuvOC2rG+qwYIcidNbVV9tW+U4sHi6vSMB0Xq4Frz7mI+WJ+/J3CUroq3P/eh+85bkGw1QKbjc8lWLjGCUwXnwWSQSUzeOqXBTxf+ztoQrxfYi3DO5xRjMX78LOt++1d0/CaIvEVTYTnOfb5L6/Ik9+F5mJsmj5SXc8xgvzP60f5/GTsTi98iXaoimV6IGlZFmtUQpaCCDnwsuJ5q4m9SzHMKa0W2iIO69Zj6fSUnyuWW+KJiYeNfWUr/i+rIMoaVM9AwXeOFMzSn/c6ftJuXFPnKmOmxeTPeLMX63LEqv9/7f3ZkhtJsi2IOoAYyKzctXefPlek/6D//5vuc0vL2VWVZEzAFVVdS22purkDCJLVV6RpksyIAHywUYel0xL7IZcahnkmvDM7wUNIkUdjjDBcOvGxQjGW/wjZZJOw0CXTPzOfVRr1kYeIVey0XjXtLdT6rOqaMw3VgkJTGClOWCJTDJIMcnuzyGfYEOjB5AQlPJRSEsOBD4MrbBGqrzqzJvHYkprrOsX86n6h4BFpsnP/0faVTAKMoTgYdC0Q3lHQZPwq5gZ6OAa/t7HauGWPM16DAXRBZA1iGgF2Iz5AoFLvwh1J2VTYEGSnQJLcZ1uEjkRYva/KxE+EwwK/LrhHck3hviyraja77SVcC03rgcpQLrcxAx1/eQ5zw4kzjr9evbCWX+yS2jq2qi882wC32BoGZjT+rZ7VVZFJoxqlXFIORhZc9+jYMBxGKDvKGdr/QZAcPjLiw2fPYgdS+uNh694JXapuTEG73atH8XZNhpYXE/ccKj1d5mibCGK1IZ3kvAsx/dANPQh8XNoksxyHQBXWdUibHmDGnEb2jAyYMtwetY29vOSbxx0kUSsGXcBc6ObY5JrMLSYpcPtDVqpLTYJbhsZpIbQaj+H2Di2yxDTXYEJCGwZDaBpZQo9Jtyreuz6kw4kg55Rur0rYHZoc/vYlgR6fi71e3KE19oNanCfYQ24v8yzyGJAh4VulOoPQkr7w3ibQ+HwLg6ITRo4tpfk9itSlfTKScU6iH2rL4vac0IEGpamLas2KoGnOB0NdFKXofT/MxiZC52Qc97Y1Q2g2rz5WEQI/i9L91OC1VRY+ZQjXJIMZp1dl4NMjlEX2P7sRlYXM8bv9wpz31jKTpSVRQ/1jwROjYIwSI3mPUwu6oXYJXytztb75rzzgbeD0AiFs1Tc9E5V54q9RinG9FvW+8p0ngINkT+LE72jAJePrWgJPSoFpYAwFszl8t2Ix79nHlDrpi09M2oh4Go3Dw8fnBbWHXTo3zYF9TBhP1jbLl0qqCv8KLq8kXDoGT8iHEpOIq3C7g80LiR7fkAFe8oD8nlCLjc1wazg8FOKsUd9lIpPBR5oFWQNWAWNfnCFoqpMxIB+zRSyb0dgq9RlTFaP9xcuVYl+/Py6Xc+x9L94ERwC77mh2BstLlS7O9k7ZS6pZ2fgIF8LoPiWKVyEMmYe2zzLOpHy4RWeGJ156zsFWxbTYuefl3C8k8qo1aEEn7WOTWZk0cSUActyroWs97Op1WN7V67lPxyuMFEJGmpb8VZd/Y0Rzedc1409z9eJnk8s+3TYxOIlNoETARYTRMRsx4cyoCqhCKrTlu7q2k3sCBKYwDe5jYQzKKCi5soaCMkZmsSRBIONzNR81mR1WQlDctKnBt0ETHDeTrY07Bt7PPqYtRAnbwFr9exYW4vvoIkotjC6V9MdHrQPPQ5SBXSOPPZnhZQlG44nP2CHXJCT+wX4xLcFsBypNm/ayIPMs+ybzTsbAdOrGWALGa/ATU2uTWNLoSGLvHiyo0pbpjGXr6GktXlxxVWDXSOxGzYOaQvZXbFmqwRFi8jgExCP4vEUkfTDcqATokBYKHfkTTofl/EioKxIFnmx/fnsVuF3WnO+XvZmVyoo2JJrQbhs7b4s+rnmAGuQUrcC8eV9Yv9n2W+6aAYVxPRIyWiR55gQK3xR6haGVj2+3IUwHrM/hzwLNT5joWT0wb88Y9wmX1BsumHHunMg+kK4lUA3SR4gEWB54T781WA33kwrj8NdgNRgGmXRMpVFKDmU1cORdFRX3ydJ/HQ/SdVBVJKMoDGY+jvKP0rJLa8CJN7TZNBDrgXU8nlDYwKUTAuCD6Hmk86fDyc3KcpqH5awHkgZ28wQySO7tbcAbTCLor5ZDKnn9QyOA7cSIlr3fvcLAPO29hFUIASERYXHJ9P4Aa0+8OToeWh20Evvac1YBLmqM34uuuIQJwcLnX+ISaJR0KK7CeKkVlnKN3JOQblPibE0NzMngJ/tFa4fTBdOJoXjbQRqOSHKD+sCs02UYx/njvJxebbxvo9gTJekSfyH2Dc3Su4J7JvuaayMCV/tTntFQgNmzATWVdWZEOTOl2i0ajMr7luYluDo3HeoS5xRZygEx3UI3b9QQNi7b+q4Uxrqj3akpiNUHxqxJT+JntzH0VczvOxdtEkbh4hO46pZeK065UtMldUGq98DmzyZpQUpNLx/xsuBz2B838jIRGSKbd9Ykjdbc6F3z6NMys8ngXxj1MD8r6UI2thRiiZq/tcDKUJ0FYpGUDCw7ueY8oj1wDHTJpecS/e+pnRm8IXULYpx8T9Vmcp34mfjyJ4wE5h5eWb1m9WT/KXPPCl0QTFg5bYLZKwTobqKWF8kzl8qcM4LdPaegMVD7IZZfUraLd9pKAqXGatcOeG3FoBmIBYYQ46GXEIzKJJKILKeXFRMBMvWKaQx2DhxCAnMosSN8X9E4QcgcQTKtTPc/HBGmGoPQDLm+5+fS3FarNV0hFlrvZDCGKCda4dVRoU8e5Iid7B0GbTb0qklerTOf5Ah7dODuZ91/y4/DR1wsSqP3NN1Q/vf62XFg+OUGQ9iBohTP7/h+SjeC/bMSJg9vIZwJQUS/FBpI4k4fepdYgWd6SCSrrM0GKWrhbCOpwWzDR55TVDSOZAS4lonaGKxEXJpZIVOjsHuY80dSP+cc93GAiKsBEkVYUhpDcrjD85OUQI14DpfqRc0nLJOHmsSGhnTrgd1jWgejrbmmHq3aCLnM08gRJBASYCP/h4hyZwoGQfWAQECKmW7jo9mMxN0xgr0AdWUg2xiTuxH6vPP8tDQLCmUkI0dcBZ0CdP/7NbTrAG5TRGNGIOwWFDqKkq4xfT4XFixHjRYQ4GAKggokIz/I/htp2Yfgh/oEU61f1mlGECn9z87GtA2bQjhPwLHEt+o4yyMws0FCF1/EFrk8JT7r9+pXN6Ardbg/iSEUzemKwPljTGFNlFeLdG8rUi3VzTaxpLqpOrMvW9je5B0FPpIHq2RGN1BuAM/DHgZeBrik/zdtDilhyEBkI8WBGgbGCEqK64emIPdrHylE0lOKr9aDw2dmBC8PrRB3XJtV1g6oQgWmOHzfEWms0Y+Z1M3sLAa96PJsQIKZHmHkK/K5RfnMdOWl5E83WuurDxr4b19EiT72usruxvoW6RkwJwF9GCzFPFaYTy1cw/KWDIjDfZ6fyRGQw3J8tjTaCKDLJHfCVDo8oGsIQu9MwwQLr6mNcfq6QJvxPQUin6kqxC6he0ntW4giNieIOkUGucmZpLcN9z9dbTVhHPeOl0UlLASt0xkll7pnd6U0PggqtVVOTAZNcqIyqIq3XLFBZiPnvgEpkOcNF2F61WEwYHap2SvxvMg7GfPBLkz72mxTriJucWHSkFnHdzyMppdvGJyTPmndgRto5KeZgq9NJdo0ghXu3zsAN8XrxFugjtV3PC/w7LhHKylBP6J55KFQSZ3w2DiEnsSNftlO5I3o+EkedgSdJsebITmRwDJQyN6pJRqToihmX+E177J78kTMw0Wl0zzcI8UD012MQLmmjSQhk41YbBVSipFlHz0dA0ovpgFL8dP4I4kdsXzOMX3r6YWTRBGBdx4nIgVrqP7rXBBGcmYd7q2e7yiD2wY8480TuA1pexiwMe+cYpeKjSkclrPVIT5dlvP7g2PQ3kXL5Mp6xOxP/lszxJH1dlTdS2nUunkaWVhp0/Hkda/mkgtvJwoVtu7wvKrrF+sREfZ4PwruJFSFMzcgU9RYzoJP4Y11OFj97/fl+HZejh/mnqrEXfa1ph0p2qiMf8suljYw+fLWI5ybrRG61XWNBql2h2C9UnRmZScg4xLkwdOqyPN6v8o7G2HPV10h9DMpfovo713Tx5bDuD1w7T6mkBOwXswVbHTLgOLCXSNJIWpFOr23Td5zadkpNY8O/7YDnUQONgWN+XK+ANjF89JETpToLzBcF+aqlhJYK6CqzT4P6SvUctEI1HDul0pNh5IriERDpBem4LhM/LjZx3bIIyKV2g+7pwdKxeUaAJaunKXUoVzn84sNTMK+t458HjyEzkw94aVRpbAMrvPvsr+YV3pIYYwBlRzdJfP8ELUePp7NW+c0NB3XYuqcdULhy5EM38YOocncUqmteLCXODcQujuFx1umUaGIjjQpQytljYbQ3hi1zdKgqz3EdYWNIYs10UXTXJJsbOYu/PaxHN/MhdUYk6ZFaXYihSPSLtNV/vZ+ZcTFGWmy3sq49XGz/VDQg8lz0m4wsvAmke5xIOXew7hGBbXJu4aHWhv+TW2DIfAn9+w1RrD17E+0H6uncGtTtewuvEwp1Bamp5dvPbuILKP4DFP/qiStV4o0T+YQcGkc8lGSknvT8HG4Kr4zBbEwU1zvvzl0wvFN+l1sH+g3C9Ybg+gutkwNTW8fkWovkbA/+os0BJmCA9Ojwlj2WaWoFfzWplYJYiZmm4wpp5QujCSgBrMwMrbh+FKVbrzrtBwM80eCOg+AM+O17gNABBm3QQafmiPTHsAd0zQFYwpOKyOjq0dcu52oxX5oLige0qzlqy7DdASwsaISmDJHehSJljYWhIsDGMOk3STGoVkxHbh7anE9FNZUzcKjtwMu9FgFpGBxQvn2vhxf7B9iFrQsqme4RS3sN63PoXCaMH+368Dd2GN26M/KxUOgsNtZNgTIwhiuED4yBiWYoslE3WZhqliTEKTEuJ104DLWYVVQqKaqWQurXSPq/dRrb2iFOZKB7GhZk7lMx4gb0ZX7mcJMC+jqSZksnQgSumvPj+vjVf1hG/f032d9lL5lABJtdZQsVRIyiY4eGe5NEjWETfKjhLm8D+Kce9Ne4AcvAn/siMUei00Z60qiTWqskk0/CzIHdtiYItpLigbcEvmBAO2oJiBjj9KShKLwGpPSHdoKmCzrGJPwFamE/5scVM67wnTAn8eBFIJi0nN6fdkBPS6H11OkFuleOHAX1Y7k+B8fl7PXXLZ6zWAK6rrJw0/3YqaCzuJHwfDduGpKlNu8TZo3n/3wvsl0OLQLSBLF2k8S+xFUFvlx4o9wYBja31wzo+Q5hIgBc5rUGsGFud7U4NhEY2RtYZdjGUuRleIoNQPasz3x/c1tKcycmnaFR0uGZ4WMjGG8BoMkRJkJ8oQYcx4M5mR0fo5bkdGWUG5KV7Deu+BDYwyzVuC+ASnG7w32ueBMpoOGaAuTd8cSqj1S+iRdXK036WKp4bDxovLdBk3U807mgXvJw39tkZ3d1rDxm1TBPcagN34CPprdArzQv7QNbAnS7DqkSA5JXvpjROgBn/kBY3SkpDHm5nU4wD5DIJbk8skDnpuHlkDtaxc1uuTM+Ir4l1DY2fLad60s3COHYZlpKFAMHl5A4dcvsA+IyerAiAIzVXvJIFk8XphBGu28/yDcaX8Io2xRmZXgqoTFwwU3VC8laWv2+gq4BsReiLcTMHhYZY0Hxp5gTE4EuXVhZI0UFyJocN/QSElpM+tpMw04uu51fekfD+bciu/4Z+LVkzmYMJfqypv5jJjKHfNRl2INbYUAEDBZJnIcCxf/f3lbjt9iH5jGxDU62z6z82H/PA4hvL6SSUIaja4ApnIvKcQstCyxmRWYyfJysvZawZw2vl8/J4m17KEN3VwuuhSYdAQPTiA1yUi6fo4IT6pR61lCkNkugtKZxY0S/2fb511SlUBdw7XuhZsELhl/ry6YcMatCeufxbWRa/8cKYHp7mh50ilNA6ahZ4oXiPFrmOUThdTfQYCgmhs+HIYtFFD35GyWVA0ZPEEkUxrs6mAxXgnRQAoKrwXgKRxAyHPKTHP5KGkgIhMqcOuU4CMLqUMITPUtdoAixSZDVwlI1ny19uI+So6ekm4QRO+XJ+/TMP85ROWX6zpTrVfmYwTebAuvb1I6EdKw509CGhDve49NMRtC0Gyrtsih0j2zHNi03QDmEm+cA6uUKWP0W8xjK2wTEZwn8yHzFzUaFgm6Q+K7s6Wh0Cy9sWeYxj3mIMZbUqD0bQ9tiJHO4QU2hItMI/L9dTmeQkM6P6NvrjWdlhMzy74NoYiMPhiuCn4jUj+izydpL8hIwCinDUfkJnf3KT3gvypY4VTJC+xvrdwnMGx6EQIqhnaZCf+mfRtCYnakCGtyWYnn2B3gbTLxXRD9r4hTuNqxCV++BVfrjEGeMYRJYUxbbXO9IDGn9AqvF2KuNFpm5bBIGBYQTGgMhjWHghCGQXPpc0jEjZ5wvTTmcDYjoOTBdwlvssCK4aPv6QHEXEB2+IyJPT1KPh1ABC7NvC/LCwLFfFz0+NH8PKwqBrWenjDp437DuhTGUW+gtEiX05xjrgex/vzXseC61pvqPy5wDx9jtgYhMXWzMgVfXhBtqTqWAW5eVOeyHDOuZFmOb5GwLwR8gb4UOmIJUGsOKba5YSxGQpW8B+vECSURdeOzRtkHmT1ozWbud6xvZovNNO5Nq6EnGftm64x6EtFvzAMC9ZwRmsbwxWo4w+Zj+//xslweTy6QOCxqfWGeI0e1UAWOh/NwDQ7J/409sSnQfYLIKYRSGII8lh1RO85lzF26q6pWv8L3+a/1fTr0CWOYQd9b49lknHp22md87Uyz/7czBWtb0rtuiP796h5Onvx9zaZRv1z/eZi9S7SSwtBCCnLPlCccEmyUoxnunKgiOMnqA3h++ePwcrAaAijjOYpzbMzD6MCQVjyIDNW47BsmemNeHzAzBgW5dvN2zHTPgxAyQI1FekyyVBycQXxtrjpsNety6b6805oYtp1AoUKYa2LpLdWhOpu3XKS2jik6jndC2/E5MoKFw8pgMGd6JaZk9DMkPWgGLsXja1tLMgSNcfC0FSNJGt2jLfq9RkhTCrYVkX2mc0dIz/eQSPuEM/mMx9AyCCNFPY3Q8mgjKcXiCdUxwaE/h3YOYVgeTCcaYgbGIcsrtRLGcJjB2bRlZ1hw92UgWwYZSgGa2f5O4k9CKrDQjF6sBI545mcyG+g+imUXLT0hywVdaI4EcEhxg770KVx8b/W2jL7fzeT2nn25Tgd7Nt+fHKfwiVXYhXWuXANOnzVH46L6vaqM+qz2XTF4Uqr2snpGgOjqKR4XfBUPy+Nx+UimgD7B1dBUbSci5qXi5w+5ccYQsl5uHWE3Rg0p07tJ1VzTERuGTnwXQXWEVxw6cSIoGTbB5DTLp3/shkQaloeBucBZmqbC547ManR/bVcYjMahHcYQcFxw1WTQlHu0aFZXPqRLRinFbWl/4eGjKon/CljEITVj0CXtASRpStT05CGTAHFkISAGsnGNw44UqdejAA/gQoW52rb1PjqTBnwjczXyFRmGb8kBqVWpXcY8eiAglNThjdi4Vmp1KqDd2tw71CjMtcFd2VvMTUL4tK3AHkHN85LnzBgWazpb0j54dXWtUzSHMIJP1rHiiXlGEmjKbQ1o6ybmIK7Xk76kJsdXLvAWO9QI+7T1KK1JRt+EGBr4N2S+dbuBYnM+V9qLDrXRx+zj7TD+fXEK+SKRqm9pXdjLzxozmEn7yRg2UKeZ2skD4lIPDGGz+0AQXeoyt0Z3pdNJF02BGSTdfxtblF5IBiMZU3CXe3rYGFZtqrUd/l55a2fykHEywvOl0hlxeMd1wRBQmyAMl2ao/Wi5jHQ+2lxRutSkccIMQwIG1KXawrWm7wLRzDmFs4kfFyOw1l/Hw+l7v7OvEgtu3kwqmav9xD+j1HqORHzmtYV5i9iDSATnjNSTpVE7gKZg8JExerO9sL6DRsAmnn9ppUThccPJZP/JoFkOlF5STEpIIz8w/9zHTqABPb5KvqdMZijR3pxDZryFETpiFJq7apvfDHjzjKpMuQFTLTPIJhMSRgQBIhJIWp9u3CP+rpbGvFzHHxsEjW7hncF0OrJ6vmq8DIYFUzuNfFd0kR5G9bh2Wl1tOoaoiFa7s3Hur9HSAsU1Qfjm9svgo8L6b7ulS3dbWOMmjJQsYdIXSZin1xcYpMNDQkQo1dvi6/XKWGzNLeLVGINk2PYDZ+jIclzO5tltmwaeNAdzQ3UJsLkMtuGtRwS4Qd1U2XHfsK3ATxk7iaf0T6XBMh9gtlqOUjlvwibCDMqc7Kw9yzfCwyWkrLZGWa4UwWGbidJGltWozRDXjtxI4lY4k77VC8kOuVWAo1ssBQ6LXPao5VFQxhmCBXS9WFAX7BWJozcniwwIJCM3v/iJBuVz12tyMNW1MPIUWOzZFvtigguqzL0bTEZ7xIZdSvYANWTX2pj+Y8I4CiVAnQGfExcy6I1l1w37wxiTvWhAnlFLQfYL103dVrO/V4jclmStz96kD20/d2bI80LN0QWkh7DR2FcmhGWpXsyVa+Ed3pYDmM/nWg6GNarp3QEfFS3k39NuZgr31vSp5TknKk29ePx+I/fLpUgu3Vd7504wBH+rVdbiQYEaySLxfa/l+3AGwtMlIPHNNLVptGI3N9RdJbSsxaBNMzZyE/Mw2t8sVsOf/pyRTjquBTEl9o1kf5k6wpv4oDcmTbe8Eba/s1n5TkivBe6UNAuVwGLfyHP9K6bJ8JoFrB9Noz0wfYWteqI/GGot+pk5pIp0DQnagvl8bPb7+4cHcx1eXpeL/3tBAj7UT2CNZnp4cV1nWlUST9XGkFqaDEHhPdov+FkykhMI1bkRRXGt9SBGFEpiwBvqebvdg320d75YzAETLapL8CUY4pt5uKFanjVPBYLssdw3OcDQuBKmJRF0pw2BcLGFV1jgvVLvjLZ21IDamcLHfGcyMRbcCsZ5QCJEh8n4uTGEFwgW7uyhqtDQrjagDOnOBnw1a7meXetpQtq1eSs0+PL/ZJxCYwyXH7U/TPBHNVgJsRm3dKyN93WIg8FTId17o/FMFsSlRxFKOceBPwsGLZWeMjVBT0Gxx/in9Had1iJcWpksDQZF1DTO9NQkNpmieUiy3vdHIZ6czx6ly3nKf+q2KnNdVOjD9Y2PwzjScijcVcefRIswjUp3/Ie1HAwBnjZuPGVNY0kGp/vCiSYNrNEvYwrGEJbvL8vy/bszBsfo3R3YoB+pryFpRliKM3H8Nqag5aIx9NQfsr7hxUnXWzAEM/Zmnic8h3NNKOdjCA3eD8/oSkgt3uHPs7mhG6+PgzmXloBTjTFY4Jp53SGDrAc4UrtkjqnI5xGBkJqIkN95ptjL6EdqwbIGP9JKiIJIchTgSgAlmAQTd/nayXl5MGN6JG4MSG0wg/P799gDUySiDWRL8ON3hx2ap7YXGcMUwt1jLCqJ3c4TPhPRrERxog5mfzoURAJ+RRWaTVKLYE9pZtOLbWaF75M5om8jN9CQ6PN7fR7+aRI4ZwZWchFFSmiUTO8N5uPR9BN79hN+7gPDnNnlSRy0P/TaGVXN3OAJt0UPbMvynVKjN392V71hgC2Jz9CnNEQrU03i1RdiT5WXX5C8L+MBVrfx4FLag3bh98JlkAw8+9fsI6xW5ik1EEjWGEvk2EfWW1/LD88F5JqB/bMUGraWD4/L4elpwCfeL6y37nknJHDLpWaXAkz7l9oK4SOVPPE8J8AoM/pukjqgtywMJLAe3p8xEVgnBvSlhkM3VKYJ4a3E0024eH1fjlbO89G0CKZBkVxg2FcUWFgWNgUP/3L0Kfar2fho8BaNdEvi1Y/Lkan75XItwE2hS9HouNcjgy/sdY9id6JTx9sDtEgbY4amz/tZ3ruuETEyLdfEdVsM4ae0LRjulwSv5UuvqEKzHqWhKMxZ1zhZpuZNNW0HS9zrM29l1kiR0iIxnNb8HdqABTcxL5rzlDerSnX2nx6z8GreHmFsTHdA+0d/dhoiNQ13m4/8CRjDzXwz/3PmibfB+HvegpCZhGvSXAa3QWrmnBHXZXI9C2J7HwFWESAH6Yjv0qympR/+oJE+YTXfG39gETMnjYpOeShgbO/ar2oJDh/BrVJrGSQjyY0z+p5xBmAIViSHzL6vAefMoZWH5fAcWoJDPswM7EKx1DVg/9xzhfeLhtjXfAYzAfrxB6QWYR5uiMJmaglrqmkUvgxG7/QYcQ3u9DCMqf46EygsItwEGid2uB35pA6vD8vRNAVb48wcHE4PJQajrJ0uNzx5DENyxiAZXwemut47ZSiIp1nRENE4LlO1YVtATKEW1yFJ4eXxISAy84qzTcHSpoDuImgREGIh5nsMrBvKsd9Vw1b0oo17rnXs7KPeEt44/DqmkLlZCrfbfmFRgfRzhWfoexx/4EVt4fhdVxNnEzLr0wrKUgktOyvXD9/08EQ5DluZxSm8npfTi8EMFtUMyMEkLBIcJ9JGdIMxUO0/GNPpHkI5BzTS6kFoEkmR8COGYUS4Gk9jfeIGDbHMqDFC86pBKczDQQyo2PSZnpzaAT172IeyWWmhXS/Dakl4PZhe5uMvJU51veKm0BLo/z60mZQ3VFrmHNGlUJY2hHvT4EKLs3kI9+IRr+CStUJ/9liXJDVgEELpDCJLxsCEg5BjO+y21XgdJe+0gw3bQoGPCJM5A2eN6FaAh15T1BagObBok8OOqXWBiXjqEIuJMZx9jCP89VslPGV6gIzSCAwinc4CdE/yKRbhco+OzDT4H22ADmNupZjWKWpNOzyrwY60q5EmrciOSIzl98k1M9o5hYz67WvmkY/V6ztEOpwmf01CvGlhnZ0FTZWpPKb+vQpjv0kBmOBre3aFhD90cuW+AtGEFOWE3oyOphW8nZfzGVk0z5fl+HJejt/hoQLNwJkCiYKp5m78BcHmQVUDZ2LxyhBl4eWwlQAlfl9+2j+pKW3ST26wSP63OoSOf78vByO27s/fk+A1D6ZbiNpeY96ofD+jgsXwVxgfcgT52KBduON88wAqa47nm7tpwnHSB0Bt5sMfsR0ieZMRUMOz3z02BDgzJXN/PktijjKU8QgQP5zCZLjURlNybPsg+wrMkAbjdDuVQEOfS6bdZjQy1u+IhIlgAmknICPxPF6hQbr3F74aZxianAk1j2/L4QlkgvBWzrcaPe2/43KxTUaHAc0CDCZHRXxAyty4W/TjBm7QlIP6nQgL+Zi11Bwfy1ooM+7P6qiQdmL17vHOfJQiJ1eE6TUMvzMJ0bP27nsC6z5VT0EgnDtbIUZlohve1w+wbOjxjK493DjwchAni9I5vGPLb8vh5XE5fPlYjq8IWALccKTLol1DqMhTW0MyIkMg1k+PEvXx3t3RnShPchOZH7QdcrmWfu6JIzvCYS63LZ0E6y14VTUSMDDwwjhHP6curvOJzutyc89uobE5NYeBNYenEeIHHI+NqmquUWbOoWbsXepecgKcODeuM+ZtkInPi9kQukYFOw2eF7EhrFYHg2QuD2Au/s7Xw3iduXO8O5C0EYFe5qvPt79CjMgkVJmeHMZzFQIc9rNEjOY2GTCit2R44L20JxA6sw9diHkVehnpuT0l+aupCWGHyFw/cFt1LdiYCyLsfQ48niOSRAZjEqkYgW6ZwkNlos7cZ/Ri6TfpnpO90y4fZwaMlNHs1ug1VexoC6Zr7EV/g0Cxcenk3OpaJjNQQWAm0B12AndF05y0EbE8o4PqkPPvTnOxtYD8bsbxsI48OEX623yPqLFbksFsYhz6GHh1YT4pOWsNBZSvNO+Tx4fl+N0iTOF9Ae8iZwrfX+PgMAuqFqEndMTAJxfkkTSvEHaZDP662Zp0yTEzopdeN9AUclNobQcw91CfTYupkhsrxRWXwxkjKH8PSXhIvnwlNnU+Z+wHFg7KSmOyxqM+BCALz7oJxtpVa9V+ZL9Fze2RsdUJ8hmaQr5fSoTiGX69p4gIb6ZSsatETjOCfb1ow/AcxMqZWrqadkN/2/tFa+CkyB7g+pAhOFMwuwPSYXjaDax5uiWPnE+eriLzIiHQkq7MMjjOQ0TARzR6OFuE1uZQnOWVMo8susyakMLCR8IQPXwB+ZI8VTuFmEw5UYc4PeCiyayvX1/OezIaWRNDFpdk2tjOI4EiGB9tg7GeZNhYkx28v7hub5zZ0sr6Y/OymuHOOCOCXBgDn8Xr72x3RjRfIdhbN04YQkoofHaZpPVkl/2BwzKl/9ONVN+dwvlMS6Ek6EZIk2Tfl+Wvb77hPVpZ8ui4y973V8BEsBt8GLHBGD8Go3ACt8ozRMIpDFUlGjfO9RQdok7PiDQNiZn5UmtGq8Ezw4sHU+ZGpiS4yq6sGtZkfvucJ2HDZ+6XL9AP+2YHrmzmRgiTuNIrivUwEI0M6TlTlfs6wH7j0h0wdk0XwuRyTtQQkKQQAOeA3jpsJGoUJfNQCmPV/PhlP1JUV48qrC3qblSGw+d1TUrW2zUFRLe7O6yVEUWAnhIGahSAjjwNvC6fR5gjkNPd6koPxpx4N6WmhL3TkjNakkbTpsSPP9OCON0AvBScuAoR03YFppwRxw63tDka7r9aEAgpYbBnDmQC1CLfLJU+Yn8yEzJzdTGLauvbbGi3kM1eUbEhQgPJWNM4QpL5jQh597bP1WjeYgxlwwoj0Sb3bqfDvQKprF+yfn+5SjHFwamjZkA7aMgtFJ44SEFtP//65kzBVW96q7i7YkS6ZgZM4KaJz8NDxrvAvDGZVkLG6r9KSmhdYEj6jn13CUAPr8AKNo7UapQA+9DNkAZPkF57WN+5Z1u4ustFI5NnB6QAIs39wbgKzsdK0QOTzaA9SHD+CtQyQBrxlJTpf27vdVdUMGZ2hx5I7/beSGue74IUaQZ7lVs4Jw4VqlYhuHkyCJ0GHVPXDEjwvR2ruyMlF+aGynxNUosBsQiEtCIRHupCJ6yA9WO9ca8l0ZgPYjAuHy8jMBKxFOktB5glpGekDbHvn5+Xy9fnZCqpYVp9C6blyC3RvG2GfqoTtvp9veX07KuWXfel7fU0ipMx8Iz5fMCzCIF+CxJJ0qXcghbtjLvmk4WaKNjYapuL7Zzu7IIeKgBjYKzJshontYAtuEqmKgVSsWHd2z4NH5XovPoFfl59gqzpHZ1PER8E4dq7cLg05cM0Ipe++IhojHUIXNWbHQLTGMQgOYq4DCnbpVI/vOMV8boRqDRcTAU+kr4NjxZ8CQOiuwwmYaxSrW9MeIW4ax3d6uw+XRPOh+dwBy6qKpPAFbnG19Zna//lePS5ItFj3iJ9AOE9vZ2JEKVv8JBhDhpfV8InbnCVWAbj6ZTw9JAooyJkRcJJbUsjXkmIOS/UqKgJqJaZVcJUo6O3VVRhS+M2mZ1vZTKKFhzo64OkbIQnCyNBCgrW1YBAkHtczwYIo9efYLZPaCweyct/dDawR2iNDTCGhFisH56xl66clhGYAXymeRhzikp6Oe8tCVlK2olEcDF0qba0hqbO5DZGX73/gwGMbMHMOcWiSDbmYRM88JyQKVhEOwMXTfiwe3kOPyOM69qlPLEhbK8gjY1n6a2wtVWh+/IL0lwoAZfNVDq21dLQoVDGlU42rWPQFUxQ/hBX1nnPB6yiG1zVK8FbswgJDz6L4mDjDk8SNLpq4hA5NuveMbQtNDuGSsQBlq/7qgvNlA7mhmmSman+hoH7uyEdMv4BWoIdUGNsjGJ1xpCElf+TU6SMdlxU6ysoE+L6lANcGey4Tx5ZPh8Mr3zPxc6Mm5D4cVEYdXGf4umseHax8pGPpXpZ5PdhcjzVtLTPfd/Q4Gw5cWR+aKugi6LeL/0fRHnU046ph8DAWt0cF7WQwmDGXJcSoGKDqftqZDId57TBTSx/mrEy0fmwFwBO4XowN1jmOQLBca01NADPcota21FeFanKlfiK/b/uHzUYT85wzmn5oI5nC9fXRZXzTS0wfgLSdXsMqy6+Fzddj2Q3AcRzIiGJoW+Hkfm3zHMedRVAWj8aQ68wep+n5ca5kc9VHknmcC1L4Y/WaPZ+div/FQwwF2/C9e6Fie5pWkjeIQHBFNNwJOkGsHkiHB9SRhrgZBxp7FY8mAZtpjaGai8qfHnObFzcQylFjvdFrQTL9PkQZ8qiXK0Bu/UxIA2DGRLDyIp0B/QJT0gBm9MNpM0ziOtEo5waKlUQuElDVa4gn3F8qY3gme7dEevkEchu9xBGzN8JB8B2kkZX8jipKBdwD15NPFy1NDJAeutgTS9Y08Ns35vWxpKTqS2MeRtzCbuGrRUlfnq6pJYwhCzvm2qKXDMt7EMbjMKJnENqDZraBEIds5zSfpPR+YRDBVbJNSZhV60I9Z3je2FAKTwMCC7ZnroS9zNQPkKnSrnQRlxXTTTJts1KX7pwiGSVWabUrv8wmBHQJiFO5KeKwkd0Emjvyb+JRKgAuCbgg2nIEIp2qfOhIxVb7M5UoCeRmkTP2M9mCnk4fINATaxfTjrZiXmzku9dXyT6mRtaefmEwAJeKAZewQT1EGRUKF0kpYZwBulQstdNKIQ2+0+GsdXfDakAc1jc5fQWx8ARVOTEjQY9FNRBLV03JBJjRn77Or+UHCgtkgG1zhSD+I07isSDUsqg+OuxtsMU6wVYC1hwuJ8KM8K6mXE4GQKlY0lUxiC90KgG4Ykyq5hLrA/hp4SwJP1BSPSIKicTC1xCqWodf9+O3GeMggbDIWTkNhZnFELMO7HISm8R8xKu0W3etUYGP8PeJJ8PuxO6LvUiSh4qbQVCE+jIHDBU+qZm1REFT4jHPqzdRetE9c8m0vbOrfW8iQY7OeepGfk+QWVEzT91oOAR+zBtgYSHZ13Xbpbfq0ZaPdn6XLexl69Vy91qTRPOd994fu9lCoUJNAQift7hnbQFPe1AS0XbzA/LFfW5sgkyE2iHcOhOyGsRdGYpBTYjiEtFJonITTV/wiyYhlpSb28MohyGFTZoaSleBh7K+QpMOHK3DEmxqrP2nIS/zPc/CfhgDoNGq8veFabQN3YfgycclFz+U7UbaSrUt16lZd4HrY65nUY21JZplFvStYeeWsH+AXLjlUhhnfNJxswiKx6UNZhnegxRW+vzExeN76hF2md8JjZ0PMuIk/2M/igDITF2CdYzdyJXj2LklsHUtSO7tu1xPatIkOc5nkzTeENqFre90H7F2s8ggBb97n0zI3uzaziDAbOwx3syPvFmywJZLe6gwyp7aEHO5Qxb4brq/iPXw54q8wQN1J3vCEEKrLToc2A0z9QgjcDmuPKuvDVPkp4xjHkWzFuFatE8FSK7BTApQtbyqXaXS2oKdysCIQumjOEqLISW6mm7R5/VN87Wo+V6Fmpfu4HyGqQH6MTPNzVUR4HJRgU0HT8MiF55S6QqRJpGzpdQnx2bpDSgzLSoW+qB1BKWcwrsYDo+LkXXkaOleJXku2RsVIkhSef+a2Nyrw3OXcFH+7pJv2e7sBwGpKZgdS720ZoeXGtkbPloBh3R5ZYF7qXWMu+lfSUFg+FCapk8Ez6KAA2OeGiGjrsh6IiJ3rjpZtJdIXLddViYslfJY/nGwZz9b6acwJxddjTtzPVk7zSDseD6Jf6lQJcCe1ryRtuLiNaP+T4tl6fwgPHAPrelRaGZg9WTJoP0gDXzPGLq9uHLH9rHeeX+mwF+RQgZDh9D0LtsM4RZGpUVFDWRkjN+RyvbxefmgZdEH2t7oODJGh++v1A7W2lQ6W9jTGRgvGZToNIzM9lXOpYtvrjZur3y9va5NBeUYhDVl4FQSoym/VT1Ugn9rZ2fsMpNvkOVcUSBrqJxFR5K9VcWWDM4UsJm/nuMJyXvV+CsML4xcCwiPIFlw4Uv9zcSnWVf2jz3MWtdhNxE6jFDAyOggaLhyBok/A3mEM9Fzd5Y1VLBbTtboxJCYWj5lRBmmVPvg0ruhR+VdLjjYKXdJxghbUNFA6RExfVSd9wpQiGSTuPzoT2ETSOmfOzTAcdNiJXyA/6ukAq1Ud8qJjEOpjK62PYr5o+1JhLOANwRZ59Y85bgNBhWlCRFwkXWiKZSRskfLtm2d3P9fQ9blP9rxHdYxliuF+1qkjoeXBgCTNMKhbk6o5xlN72LCLZW9t/wPorxKh3AGc2aKEvMlB+HKDAUmQHQpdVZ2jN082m38YSxRhMBbHbv7PG3aBO/xtAsKn1yuLaCK8le3P/gNTQG0UaxNSGHtbfRVnZVSnkFS0wJaqlEzL5zYy1SG6imI4nR3I3wyb5g5tHIg2QpBfwal+CDIbhRyg83IR7D/kMC82tNzfe0AMxCmgMSiafZVJw4iCYkm14lmSgqExrBSN0tkIgWaeH4U0TC4WiuiDkfkzXJcmUKU2j/kwBgHrk3OvFvMJ2+j32K7JSEYuQel07xXAs48shyqblA4S3rNzBNhroHC3cg9MMMpZ25cplUAs9x9fmiHSRsJAmnMVV7wiOD7XiCIDWO8jFPtKGYaySigjX6m/ANS6H25ULm3TG+eJ/ZNfwjg4ncgM9YHYnk9WDMt+XyEh55pbAUzgjLnBYN0OcYwZI5vvVWqnO7dk+9uan2ppruShp39T++KdctYbPjPutaAt+Re0LPq/0Q4zw/l1Y9kzjGwh0m9YM3xtnHpEJZeekNjOaHmIIWK4H6W98ng+YhSRtKH6xsKn3HrY1EdItgFcLUCBwmteDHvWas52qB5wcZhvuBowRjPg7J2sx1zQ6s+zPDQ0Q2nB0qy6dkOfEP5yh0EsxB8HBluDklsUEHUWxYZTIE82dHNDZU+8zpUjbGgKhWc+OHpKXKvjb/q4nPjo9qahOiP6pxtfwzifcnBlFSFZR+CRHyNfIUCyOtRzHQ0XMmNb8qNdaxCKTkDEY3snQvn98FFMXR+TxkEFXBQ6TDTAnS7CN+q1cEE4w7n4H4Ada+9rEp9FbXZORgkjHyklLOVNOpR5yECzUOKQnswqyymN80Wre9FF7an8S6b7pnEMTCsIpgggbBoNi5aGg+ay0I+VyZfDJ0uT+FlxvOTenPLdrBHeL/ikn84oR4ZTOrlOPv3dIamhqtz1xxtP3JnPKUa2PWDZJjUU4vRd/1gfA68hq9LwhuWV4jp34jpCEwS8nKLGoCTFs5eKbSBixieWCapLIioK4Ct/wtnD9lEvSu+QgthkE5nlqbHjd6SBpTyXHDgLsZVLNaGJmIPu88KDTAiztmQF0jH05WV9NFTYIqzCGnKQKlEvc3zNsz1TYPER5cVkgT/L7M+Wys+X6V6mdEps1Bh2+Ir3cJW2MeesBe5jgSD6mJdpjEzQg2Kr/Vs4U/aKfB+1hOuZznlHzxP48GZ0W1UU9hCDtMFU4tgdqYjn1AlJ3v3tZupAvCABMt4FoVRgxGqp5Es4ceGu3wz2fXtn266v7k800IaMOjplx3Dbb6fPtcRDOl3szJoxxpH/q59szNlhJZnQlKBO5TLpjlehGaulek1RHxmlgtq0tZ858Y8xskff/cglkGnu/MwyOI0Qe6OFpADyAiJguLTJJSHhOMKcfj3wmxE3U43ePYKFUivYNrJ5RsmOjMNBRoOlHPge6fVar37xkM1quYqURz62HmMwH1qQcboUB3sWTyuaRSsu7jYeOZ/t3IlsriQJkKozxDIUDxGFtBYyLcbGKS0o/etdRa9+eiPKLBm/QIi+j6CJjSeIwCg6rWBGk9K/7185RzBgcDBPK5t5AzS4HaVDNOm4xQMFsnZOINwUnSiDN7as+9pEyVwXoRk16DT/UMOzSrtpJ2nle/itcOIZkUfGSvUwtlpt3ynEudN4UNJ7CmaifTMfT579fknMr8Tuhc2TcpgN/KFe5TzW6PaKYYUVSjIWmvilL3Q5eEpFvwOxFv/s7Z9B6dNE5O565NGkvCIMVXSCTSJ13C371Wa2gE4S2DRyH0PZncAVoDDZ4aaKXeNOkTLYFyLr0jORgk5qGhWmR0/FR8P1NxGKGQufWi6Sw8w/gFwEeeqoMug2R0XUvgo9L9rsE0oQrJ/rpxo3VNJrVLClWHMGiS8JV9o+vc1twavcSYAkKD0rju+DnciIVgqY0mr985bCptatMxzQhLef5k3iXQLqK4MS8PI/toelql/SfqHfdo40zXsNVf99zil2CmuDdSWFCzhOupZv0tRwsCCuEWJZyZNPDKPCAU66a9tIIIxt/XMxo0hkw6wLgP1KIo2RYuSofiZ03dL3Mh/c8qfgXe7UOp94/r+aiZQMKLD9sMsTHeMm8pVP8q+Egn1zsBxjDkaSEmEx/kLi3VL/toRxdmY9oQ6lbfc1Iy4yQT2NFIaekMjHiYio4b3f3RGN7DcniKjWF+2udv36LOgieWY71Z7bouOjcFDNCZOycCiRg4FNoKxq0LKekHmA47iYROlRH+jF7uhFW0iXzuanZbf9s4fkRVLTReKWh8mbYEN4Jq15RhbUn19qdi2Gro5d+qHfJxwaRXhYumHd/5yDusHGEjyDIFnrH/h9AHTZPras3WHHUcyBB8rzFoTXFwPY9wGa1dbhKzrGs4ItCOg71IxqLV2+hxSK0W74tU5JwDfMb+5VgBnV1NSaNzJfOb837PJhxzzHlKD82MDRkCYcC33XkBbrfqTjt9TxNWdBPvQpIdPWmQW79BIV/vo8D1cGuOS8Ub8BPtTqaAjJOzg1EI433qynhO426brWkqybwn0oTSNW4GFFrxqMxieDScNIqGBDwDlzr7/dEI+uOyvD8vB2RNjGChtTtkHCpzPx2S4Oj6yM4YUZQmqQxjcLi1wrWV1xtMZdWvsgIYfPQ5Da4d2DgQ/CS4cUrDh1G6kkVhpvMv2lScaYVzLrLn1zBI/kw1vWLLIRc21ZoM2qAMEPfinSQ2Fc6zJpOL97TxiIo/CFSDDzk3vQpeka5Fu51taTIE9b4qjKHNTZNgYn7DVhQpSgAXcc94dDqMy7nPJBkbDbhFC4MXnPQl3wivIl1Lb1ouVqrOObOm9gLiNmw3YBr22m4PkzVmfEaUUSWxwjUFr99mziXN/gZ9HbR0MKFid0miKvAM6QA9qQpascQ+81ijjeSf2Z/Rt8OefYDCQEFQJoPqKT54iV6mNGc1dzIjKoBfg+jvho/gV31Uv3BKCnJdKA+Hz0c4737f1DYhNlOGsPpb1EAvtAJYJQ1j4SbqEvwZUIbdZgfPJHR3CXx0/+zLt29hX6DBDTEKPh+UtLoHSZFUuWFRPQvQhruqJjGFIdo1hMhG6UZuRi1jw1nRmAEDsAwkkFi+h5vHva2q90/pIlw5rcKZGX0j8ddkOXQhhHCmOx5bwjYzDapqJcEYcI3GWfQ881TBO9YrzKQUuBENJbPJHnrWWkJ07P8YV9CUFQYAYUwZgouVIipOVbI6iZnuXILynKBZIRzLY8WARFzH8pkuUCDwMjUB/KNNoMMUaocqpJRVAiMAk6nHvWWeIKaHgWbm+9vycKHmxMqtGq/wvWdzbYw/CGJxI98TaItyKDtuk6Y0AkjIVaFrmXumx/fxumYWFeaGvQ8QY7rSb0DbKd9sM65tOP3WNuOGyhj22n0awx1MAcQKtV0LXruSHK8Y7PKhN8xKWQTBezVr5S1jphTPe5gTPstPgsnR3c5+Mn881WcEDXnwlBFmS04HjcG9e+j/nxItDH+cG7VjaL8ZRcuSjXRN5Ls1cpcFVWjE9gMtKStoMzGDYp3srD0AjAlwgZxOMvKsAQGPpy0pbcXgEIREDQnMdxjTIQUpgc9iQuKKKkwrlDV4Q+E9WqKwMITpmpdfxvOYLK9Huuth1XKevdFbCH1UHapoDNMJk/lOCRZ9sbUlEX6Utc4+WaxDJJfL2giIVVBDdNhYZNyEpfDKQBsE7qDUnHEd0JIJEdN+Yf15QwS/+VvQ2cT7qNHvHFvE4lymePiNBzenEsZnBuphv47HKCUSjyfVAEiTsP8TKnsSA/4H4lys77y2bSNsAWy9dkZUC9aL72m3Mo0tWCs1FP3sZ8NHVNGZb0ZVfG8jnfXQllwUqhPF6/dUqJUNIv7nMoaqn+OGKjUWtbQ1HgD03S/VQ+9qceT7j5zwXi5sfO/QEtT8j/Nyfn0N2wIhHz0YrEfLQ5YwVXPZKxiqFv7BJs75GeUUEz5SiQbq/ZAku/0HxWj4Ks99Yx5TI/VFud7hrBalPJlLjmEV9KYHD1k1SZDTDx8R3iM9QqSecIM+g7BMm0riCC8u4t+9tkBP6lYOh1Rp0/QYDFCiC67eu9JoZPzKiIv3G/5Xzke9OV4j75L4gDQgk+gLdBaMCMTxAQ4RGKJ584YmeVqWV5vDt+pVV8YjxLHsTbA3F1zeAF/GvfSI8saKgj50mwNxoEjIUudxpqBcoVr6/YQx6LoOooNrKKj0mIGsdMfym2I38f1zcQg36idMYocUVUjtoO0RfV7SPpzKQgcnhF/nieMmY55dn+/a1mDuQmzu0hQsXwpK+WkittxEDjlop5lOoeU8mXZu8vmGFLHFGGf3lDwwk2vLXOmmAEZfIAztAOMQXFtAojIX4jRXEOcHqjgNeH0T9nlRqcT+5ymXRUQhtJB5geywyqYpBVyYyTO+c8L6FLWmIx1B4PdeY7oE7KxmfUMSrzBUej8JbJaZPVH20l1dNfEdir37tSzggj3O+gjE15WZBLYddZbzXUEacZBQoU3HwClM98+aJHEVk7FiCgIVFWnQ9kvPbNvWcTqnyohhH1Dm0IkpUlowLYMR/7MxBgg1nt+Ndgnfm3B9hu2rFppB6oml5SviuvKn721qrLZWlojPhCA4N1iCRs/pxUBM1vrYEMw2GcLWPOnX6m66M7/pRCDSfwphYmB2Bw9kySVMdAHhz3gYESqoFc6E1nsYwmbbEL76OPtzdE5K1+7UTj7DFOgix8OZRidfrGb0zQ0th2hLS7gJRpp9IDl08nlyWFeSkbpsCfSlUBhdyqhJcKPocwk/eFSyJcsatRRGXhp6MgjxzOyS4rFE4/J8kPF+hxbM3sDKayZBE6pQqGWke45DL3NirxS3x8h6iRQJ9tw+f1wbncsy/Vxr5LtxV09GDLdylRk1yue11BKrZyMhntPbi9tSPM8OY0i8xsWHCIoXmSsUXXfmoEFyXMMtYiTec7NDN6SHyiSUqc/GMXtf0gkVBDQ1Qzd4Tx7jcKbZukxAiI+OFgT5UGtMONDi+wdlJ+0fC+K4MAHb0+psdkGoxdzYOjt8BK+x83swjUR7xN1X96hq9VutS7b6tyITe41zrPvMBVfmdYLbrX2rWtSZJWtna3pD33+EIXTtMf+cnMnNd98Ip/8UpmC4tdVwfTJJpOau90YjqxreEJ05G2sO4maGAI2kL0qDMOLHkLBGaoRmAOXNHyBanNB+4Bm+L1Jdeg9dWlZUx4MZdHSqcBBhpFnf6XUzia70Q51S3KhqdbD8S5w7w0Rtk1ulKM+1b2U4sQ65Rx/q4epSMCW7ns9/Zmil+o7Ebs4UjcEknH7ZgHOKOFUHigyV1VgXRMgLBz0/hZZK+OiEAC2D7gzfJhP3yGmTqEcdY9eIyId9uu06LXYz8vJE/Yk2PytNqRrXY69Msn4y2V1jDg43ZqqLiX1JCaDXARbRRyCqIO4Cf1r2z4x+lqygDMT09OoKmzVik8Zk5Ml6l3Tlklajesv5IqMeOfJt0S1a+sya2GvmU/fAGgaeXSvX6L31YSJ5yzqRIUCDzZxanQEf1elAPp8xhdn25vv9v1sYQhcABF76nMB/o83mh5iC1WN9WM5PD8sJZenWr0T+H9oR5PN0lcwHrrG+a4OYf72hmvMdhArE+FmgSGDYuSlo6NtaCD08LF/o0hMOXUYcI4d/yQOTqkMynUEvWU5SoCIwQfdy4sZxCTAYDlNfe5oH+pV7OVAbT8Pamfd/a1CZmlqNwO13WYjMe2V/+/Bqv3ld+2D+MwmweP5kpG7k/LkY7GWGVwojgDDdnfMtbBBjD8EVUhhSJoKLRY/sn/arrZ3fJ5CX2kfYv6L9NhtDSvioP0CGMFSPMR/cAoQZJUFfEW6KBihCZ2LMY10IM4WnWWVoyeCKzYJpttfSdBTvgSGfGh0ZCv52Jss9gAh919IcjjHpG67rXpwIaTsymE3gnLIFdYCyf24RGvu1TYhLckABDi7jWT1OtaMLhFvamSq0cGVfY93ERfc6Q9gg+txDRUto9JI3Zxd5/RAcfjlTMC3h/HxaTo8MnpJaxIVrc/Igreah+ERHU5pqG2bvel9LSR2gUaDsqxp8sYHCliAY4p4mw02HHPmZ6oLGuCTwgGgs/kEhiEXPRzfEyUZ2Ii9+4a4VGEWDVG3vQMwFU3eH5qFShgQ8uX+71B3gupUMpBMJa8bA6SufZ6VDKXKIIIFmTIJHPLFmNxhilkptBkLEijj0YZIuNBs1GSTMJ5I05yeIZmTwTCgr/fLFE8lTJYedIRguoBI96CS8/hqZE8J1zlDA7Ivky7kbeyuypWruIjDYY89oOrKZ5vN4PQ2Wub7DiBoecYhUVobD+IasKGb71jQDRtEzVQgyptJVlsyEJUadLsS+vpjDhX1GIy2YykjpD3jVtApuTM1PpYIc+6A/87L2/arJ/pO0MUk0OS/0tKIQ6HxL3MG7YKja3OrdcxpxNTAyBYY7GN/O46oQwviZwy9kCpaO2AxbT5b+YSTnGnmDRsc3KwspDHE3c5gLGPMLJSCopQYohUJAID2rqV2PQJaU9rTfpTW11b6XA+ZBR6wXbMzAmkso5gs+MTTzn0qVqTrGeMI33NwALTbCkuiNkHzf5GYsBqELwMEMvkOFpetdMMgRwcpSnlVDGPOYfU1NS+YgxxD/K+6d/JzMkAZp65/NvUBvo6ymzd3jIHQcn2Z5zaLzLIY0cGJ3/yVs6YwEmHnm9LE5gLum20AgGMBPvWiAxROIBnLCK5wPJfhqBxAJMxlDg4nkLGjg3ogzgUZmPz0rsWw9aGYmqB0MMgpuFM4gdk5fXpfL9xfPyJtGf+5tvNvsEa4d2Vn2+JfHZbEi9dC4WIiIFf00iI41BLxvJ9a/NuHEmCuCQuU4jv0ecxeXw7NNB+ZzxXnbkAGvEIAQbNq5d6QCe8P3wFucI+5ZXwOJ5l6YpXZNpIorNAc4YyAzYWrd2+vfX20k/o22TqCunRC8H9AUHo/L+cmCak7L0TaKu6bKZpN+xsTsqAazr1SyWnWAfvxcGGWNE2lCD7kSPM0RROkeeGnmChLDnxOXtJ/sjEcgBCckYAqRBgCSsM1ZPl/u80f3z8Roz0bVF4n3vCsu3UAdpuQFwhJ7U7ipXzdC+QexVT/4wUji7/6ZaIFalMd+c0+YFnnL21JDCGmSEbLEu8taiFRbkrIhOja0BSOKqDrm80sbjnpkDeNhCACnUUXsYAyasAb2BVMpM2APxMUZO6vasa/nmUa10y7zouoJZwFmzAynZHzuhBB2DvKKcIyINNlHZxihuhytrObruxN3FnRyO6BLx+9hm8Jcco9yTHQ1DYaJ9zySIRjj4PhR8If/+5AcXCfsr/cQFnNN/Du4UEMad+GA0f/TdiMyMLnPBUIltjwj9l6bG6ssR405teZgCBdoPmVNuXYz2tSF3UXPy+E+o3K55IY9tU9MG6M4/II4hfeP5fz4uHw8H1xbODIHj9UEKNKuEOsUpCTYJMch2LoS7VuMI7lYAk+V+ybcm1xe3e+QNqLASxM1u0jJXEz2PzMuztxXtS8Dt88U0nJ9Gm63hkyth/i2eH4xbUWW7yT2q1hyjm+Upkw8fNY2+V/X9PBLvseMdniE2pBOpsGg9i8/0+SAaRhtuXk4phWkx3mnzQbYN6Xxxkwy3YJH1yKPkMPe0Cy5PnqQqN0QuksbkmgUBS5jUkNJclg6L2Nw3J1JFNVvPoQV7orRxrymncSYADUm+00YghPH5+eI0vVKaaiapll33Ugd+b1c67f5M43ThvP0hAh6s+U8uCDo3z+0oCgvwwkvPdRgsEhn13JQfZBrHUwkCG8wuX5A+1ZriTW3DMv9zKa21gJG7d2YC58fwoa0exjjuMBFlS6tsg/6md1tVwnwlnCJ/93MELauHVphOS8/2yXVtISPp0PAR/CwuXzYxEqude2TpoPlIfE/K+ywTZgqLDUbWNb6FYY4tQUI3pqFyY0hmAET4fuZ96Xwl/qsYG8tIpJE2+fCIqFNUqKUJHl2TixcMtTZLKqSG1nmjO6XSTgtJYZuApkTft+9pQDppP3BJE8JhuIapMY7JnY83t1fV4uD/1fGngZ9VgTDwTo8IO2C2Ckib9RgChlpvDSiTsLP+BFbPoNOLKLcCCFtG2TW7G7WChatqDAc+x89dcT21NfC5yEIS/daCddO8YfnXiv/JtPHOaOQoMFU7lIbQWkRVY9bWgUw9wg8WywCPLK+W1r31yBs5sb79UtqXue/vjlsl4IHg+gTZj0FA9GUDzgfZ0RWu/srNAXfEvRSQuR+MN7QsnwqjEZwuOgHvbuGmzf34Z7LcGcIs+nk+qmmij3n7rORxiM8z+zsw3XXtT7CewxOHeiBrmexWekatn6E4HMzVZcxqYbeaOTWTbPXbIEtl5/NFGxuwRTOFunnjOG0HGxDGhHskaStc2HRv2OiCkMYf2/aeFK3nqhxvJE4sm58elLZBje1VyGSokaqZILnqWeR1z6wAic45PD9z5gHfaaorM5iXPuHtxLU32QKRSLVeWUdCEJ1dp9tZJaZHBs28xEx8pbeYbnhwuDqdWrL2klhGqbiUGahBE9VdvnplzNNhREM9ezwz1hmU92G1aNizLUJJoZDex03h9FgdFfIKgkLJHBPWYD04YARAj5s7pYlXbhoO7l3aLCVSGpmI81a0d3Armuve4q/grHk81BXAvvDv7dxI4AxA9tSSzIcH27Ktm9MS0iBB8kTPfAPAltGLcv5UOEBMFIk54tcW2ZLcC3BNYUwTgfvtYdAovaUMQEP2f7PWglSR5r99tlJmEz3oZy7cr6nHEDOIgUNaKlqR3Dozbz33kNDsD5Z7rLnpxGZjT3Cxx5MI+uQc9oZG7PfYgh7sNH040bcCw0T2qYC49YrtrSYXeby6TQXy3J+MIYApuCGJ/uHRFrl7eFZPfrd4Zd7mw6mYe0rBaJhih2ygjH2aAdGjIdUfwPiRQBb3j/ROjSoDe547hrqhk1jDoPwExbInCp0HU3Jg4sMDyD1mtJkaHye3puHApi+PUNLDOrEJxGQuIQkEmAMhtX3A+HSVFNk1ODm8wrPrpSUqP639VMITrW6dNFUW9RY66hDTGPyZTm8fjgRnMJ11FL7ODj/PiZ8R0ydhYUacwqtcBCGSL0R1c2cwdhPIzqEg3q1uk2oAdCnuqaaJMvsuiYkOFOw/kKSt58KMYqx3hvrcvt8SkDWTKDhHi8I1dhz4Q02cm5loJz9xJ4PhyjYPd5NQKQcEbWoy/u53+x5djYw15nhtcxL22O5l7bnckCl1Kbgbk7IyObvy/Ny+PLFYTUWGQpbnHrNHUU4gIcUIUEGjup5Yt/0Z9mPOqbZ90rgN4i9fj/7UgW8Pk8iUN/a7tMUjCk8LM4Yojh9SFfB8UV171rgnsF584U6uHjgCIhR6WtHrSRRysMDCMKJgUT3cjEphXVC1qUZlRz4Hmsucdiz6X6Kww5DXwaZCawxMD87iK2y2iptxsjE2gPhRhQ1r4txGyHN1MOZ50eCkQitOPEBpMX3IaAso6S7tqIaEMeUifGEIDQJq6ciHskUU3fOeY41H15jgaMvy/LtJfBvzlMy15m0KT+bJlmIoFY36w2Qnxt5PTjOCI71HJHVXGMXoNU5YTDgVcrkdGU+LpdXwFLmWvxOpmqGX8PsYStRwyj3KPND5f5rOCA05LBhxPp5hTa4tK7cVTthdNfV0BDsp9EAX9/LMV7te696nRUNlsKEnLGw6bD6H+p345oYOc47+y/rVcbFz2A0TwEPNeR9TQw2Oh6X49/+thz++Lpcvj4vZ0/1EjaZwwsYL8Z9UBgR6x4QlMzjrKVWPtnLBYJuMPpV9KRrCf2dIvxuMYacs+UnMwVbPDCFkBrghYEgmHLMQeygRE4611UvYRq7EoFcLo+qHd24GAubQTmqIq60im0YLLJ5zCRxaBiHIPrpCpgJ4YJoePQtctUPQmaqNohEOZjhPx6HkAZUlp6UTnEsxOjtOVmWMg5mRKHKHGU9BkRo2/NI1GReIgv3JC+QzkMxwGn/2/zzcGlKlNl8k/GSoCEnkruy0g31xVwuLfeRpZpmOdOmQZbncJsJrCbwnGoMgZsrVGY4NGI/mAZhrPoonEQm7ISIMBTiFyAVF1dH8l4SYESlp+HTvs94CqmJ3AkMYlKG3cQ34xAcCEkxtsjhN2p4o/xmMSylBsefjKDGu0yAsXxPjjlKag5d57T1wGaXz5IcTCIdV2eUHU2rMDEpQKV1o6nF2WVmcP/6Zbn88WW5fH3yeKuhAZ+Xw2uk/kjYkU0Fkj3b0F4j3Z5K+zcIyqphdI1qpnkow1HI8g5t4Y5ynMEUQmpoqhokytVrVwoCiULjpsrhuppVDIOMtNzhegVpEulUn8HU3xvX5madPU8+YGBTyb9EFRMHkuH/zhCYuz2NfS2bqhNoBpfFuzywLf3sDRsdyetiasiOxcXTE50N/DsOifGK8IM3GMLzKWnhFvad851rG+6fQ3to8wV1Wplc2DBYGrNqCu6q6H2B7cQTyTGqGD76gE1GAsGxFpkd1T1pJHkhtK3Rv0lCQ01hwTGQcDMuoRS0kdgWLxKjGlNj4A4FQaJ324cxbkig/mx4FWF/0W4UNiW82+NQEDeCPZICxsmKMUkyStmTgxAOwcehTPfFDzdTpnzPKHgyWjfWm98+IGB6cBFW7BATIqeZXsOVGPNeKtmBGZPCOAuBXPCMemAnnysTnxG0rCUeBvJhF8L4eC7dk+pxuVialC+Py/k5bCTsljGEhLK82+dhP/KhzBAD8UhcEX39u3+JB+if9cFlfJPJqhM0A2CmkO0vgo9casxNAHfDglc2AsAOSs9HsW5KlJNOJ2NogT44oClF7elCSXyQ9Csle+0WpdBRnpMHoRaKaR4H2SdIwtiAOY7yj5kkDXOFZxO8H5Lx0E3U8Wo7rPDxZqUq9+xCkBmZivYlc04NiZ4S7CieLpsOWKv5618OD1JLAl9bf50o0T1TcvRI9HKB5XT8RhBdYdFqW5BiQdBzETw/E5iYM5Eh6rN6nc+Z2X90b9FGQ2Ldo7QpfGTxedGmcgOAcXDfFjV/otbrOEuT3DnwvPHYHUfjgEXT9z2D4OjWCrgETgIXC5wwxm+0nIzf5sAJtXn7kekTugTMYnuKaVVIkLUeiDHOrNiHeQIUd3gBQ4A3nsKs8Y9zKYeRPgcekT3OrDN6zr3vaYPWwIh1rzAaO58nf/uv/XuhEfhp8+XZYN0gbmMTjyFk5U2m4tcNDyqDwCLFibpsy/75CEgwPPbmS1720Uxq148LfU8IpT3vGhOYta69CMTLdxTh5WczhWUwhDODs9Qgs3phhRrGR0KkuGh5naaAvoEDThvcK5lJNF3JKpJRarQySGolwQvc0TdIGiMbVJEZT/kZjcMowsPkZPBwCanODhQhqLP03YiobfKIZM5sqrmJ6A/f4S5xcYzBDqkX3ztMYb+SOTkBHXUPAhVoa6sBW9HZyhzK3DmeMklPAiJmXlJe4Q4CQzJq3Ew82KVcM04SMgN8ZlGpYLoevMbvyDQylbfabVQTZd8mrUNlfY92ZigarscxEG4Twmprn7Yki9nwynas8R1jJkN3zxzMA4s+DduHuTSfw/U059k8eVgLQALU1O5EOxX2sdsVmC9LbSAJKbHgjjEXM/AfI2KaUeIz4T3PC7Ym3a75XUc/pnO/hwKAIdB91qsgPi6Hh2AKxTNLBdKm5br8QW1AGR7X8xwBjNSqVh0s52LLLjBTISZjL4L1T2xNKbmn3cEU4NeM6PgwPIlxs3SIwVPsXe/t7HO+BP/rkhkOfRoeb4LLxHNEr3EJxvy84TXiBBPSuOKqKvFzhpklFVUTR5fVE6h+7kntUsKh69xhrJ0SITARhw3Q/4CdIB0agUAG1ssbjV8SIUrvJE3TjfiGDJCz5xiTEeghSxNSk7HLJOZgLO34vdR6dvTGpEL1chob3qVKYLhD4o9U1ys0wdcCTMEYohM0pPeAFsFkZpHCIoy+q5iEdBm+bDCFVj+77JtqU8j9QMcBQltwexxan7i6Mv7F76NLLCJn3dVU/Oppq0g3TU3tIa66kGD9sTY/CkeBKIdbqjJ1VOzjYfE5g62BUAivTfdjrJXHg1hfI21KpNWogt6at2LivEATmQTTvXfpFVK73t1LdnLuARk5XGQeRGAKUZ4WkdXqbszxMsXL28dy9JTj8L6yGBfGL7idj3VIqNEoHRA4a3S00LF0Gy/0cIrxNOjoltY0AH18VbP0C9GOJ8GCP4Mp5MNpV1APGUqiWfNWBiI3xzzuqDIzNb1AFINBZIfw+8qoXSAOXs6NYmo6JczAEplfvZSTVGIPAu6SmxoshfmU+ra6YP4jNpGW/9wcf0IfSDvMufUiKqayiRE7NzEOocNVSIZmeC/81UOSGym203sHxICE1mEsg0JoRJR+er4aDkXH0g9LkaIwP0g37gSfSfHOyNNEgkrtDXmAmCvJNQOLnocRMRvVfUqouubqSihrmOsmkd6rOfTxi+2p5+EHQ0hYrGgNw0jLs3C4PIygRVYt872CtBvJENTxYWDb9fAPAp5lIzPNR5TKDFtFQFYHO+US3MccUa4pbDVoblaZb3kzzP7gBNWec2ZAC6btsKFFMRVLak0uTYpwlYK4qAdYj5EwnNq8aQgPI87AjMdmJ2ClOdobIyB7UE7Gf9g4voe2ExrqZVle3paD5Yiy+A773j572LKzicCnMGNued1bzUtPulMFk62mTht67YRudrRipmbdqY3cYVPAu90LYZQCZIDKgADFJ3nVVJ2rH93StiDdUrOXD4V3TEYfq8QHaTOkPDACSBSJz4NAJLGiKuiSm/+v4neQwkPIo0FyytZFg4k+DqlQoRox3gaVjtrQLIeqRG42UemvLikk2B07JNAO6LKXhtnMI0+31f5oefbqX18bUaHdvGAquQkQkuYafvg+M6sASNhIPFDtfRhr5TrCLVp0aPiUU3MYTCGFkkReghk6EzxgXvzLtXE7EqaNDKSZXJEwGkkcI4Rt0ALNZOI+k9ppL6F3kTU4b2jNX3cOgPQfCSgp4Ihk7zBTMBfumegTxSQEmSnjpSbl13FvCJxC3N/6+Pa+HE/H5QznAAdpyMjyWonjQPR3akaAJ+kG2zXkJP6F+HUngdMotmQM4cvzcnmmloC+QBjKRxyMgSH1/PeXOI4wqPtaWA4kYwrfvyPzMPY+u5Lv34Qikv6U/X6V6F8jeEpXbmk7Fyswc/gVTIHwETwOIvXuqJsbh675aC+T/u4RtOm7h0Qee6QR41wYEob2LiU0qlI7jDQksVUxeB4gGqupyiNXfIHO/PvhrhhzISUak6OPMqX+OtEIcu54DzKrOsrhFe9ow1G4yqlQSzXdfpKQcI2Mief84x0pmIy0BzNoUMipzKesSf+pthhPTQ3G41K2QVWItLX5QAqOInVhLwW2TqZSIZ0wcAaenAyDBCoZCJhIPptJ5kZepGB0kZdHM5USW8+fhKVI0Nu+OXu8gRWDf4I2wn2DokHO3KBpW0YA1siepC/3tOxMhOhBX6N4U8JVnnRx5MIqjLOfNd9zuF6Nyf28uGeXG7uiyt1ruMpGAj5EVztTgnsrXVvd5bprekj5LprZECKbdqf94E+HTBtD+PLkTIFpN8KbqpXahPbl9jMj/tCquE9cYzaGAdjYoakjOpTmmKaddboFw3iNYlatuRHjXV4hWvVMK1irHfW5HcejNq8a/c9kCikI0SXN4SMQV93I6Yst97ZAq6oaTdpq4ht2TcLfGMOm4bAzJa20JBjcwHNbgjsVZFQTYkZUa3S/U5fbHI4QYUj+7mpKIgbpMf2sPY/SSOSVRsbyDFUxyYx7LQj87kQX/VYp0w/okC7jc4mmntmLpuu1Vpt7Ku6QFm0MofVcwj0n6glwTD53qP9b1G05LLqX5LpSGpawTqbHFqaIZ3rlNf8N9iV9nBNSyYyr0eiaT0eSpyWx83gK1EYmhATGkGmdEf+RNaJ5XqhRaFRuwoNhZ7GKc6khMChRx8/MrmYo1RTWG4XrA8KqQkvOl3uiHV26Lk4HFjNhWguVYesXPeQITRKOdDjLGId93vZwo5hj/qmRVjuCM1lnBsEQrOCX0yCPuUBiPib+c5scHCnOH8vZtIHXXkeEWojs+UXgxLLHd87BFtEXrfRqy6kQYW0lTON/U01kg56unvszmQKlU2sMZLGN4emSRarkRMjiDzfSPsJyFPcbDkn8GFBB0ZxSQo1+pBreGejUuGhjIgMYBkv3vPByjmJRg8GUgV2cEy2Oo+8cRjOk/9AALjfsGUN4yFKbY36kfiwfKlBV4uhFS+DfnDYxLqYnUMW/R3bV2bxfX5p+g0I05afGtixr6TDrGzD/U8IoYfgNyY8XN6mSda+ZAvsyw/sHkwsfejKE3FFQ+ATbJwNg/qRkEJD2k2Hj/abN2E+TQBmTAtdZj8fQrmOtPPmd5/ZnDerB4L0LDlVBu4I3EiPVCzFP5w4stQpqOd/Y39RQSyr3BrVC+/XKanqMbEzuWos+svATXabtfSbVe2wOCkC92jMQB8J5Rf/9T9FW4ryM/e8ZW5/BEL6s4w0MlgyBjDUz6Lpt+8iezbkDdARtPxPo0Ug/OlCb0rX8exyM4lyx0qBbu4XcbTGHa8Rdv7oZevohTQERsdwz9CbRnDFq0Ei8nWqwePHwx9b41GinTRnAAOXbNc34mYd72SkYg6+ESGVQlEMarGRWpYiMbmafc4xN2uEhywWGyyE9TgjDNV/6rPOM57t3BOYzvYtSKpe5I/XUe2CsG3Mv8FapYibjATG+mTFw+TtDUG1J7DCpdrt3lI0fRJSUkzEHfIwzTBgVmdwP3MwiyblueS+hk+IYUDW2gM6Q+8qlaGMAFHIwKMazFMMySnryuZ4lmppyZMv1axkD4BoM5oBYu0qkfA4JFhPPuWTOsSBXD724qNmlTUP3I7rPaF8V2lRCp/0FZ6l4lME2FoFwIwFFuNUyUMyiyy07K/4Zs/z6nFmUXYOw6HNzofU9DcOw0IJyDKmlkyEALrKcRZaiwpgCGUJWgfP8U1FLIuolvA5IkufBoTkIXtYvMnHsw2KDugCSU/ox2+4dOlXBbJMhqNaq109uIP2sidh6J+bv+QGucDNTOJuUY2qgai/McGlBIQyhFwklXcuYm6YQyDaI6SRu2B54lqYcXVR2jaNgc7dGEFblwsqI0gg6iEHi8A1aCjNCk65KP8UtEu9RYuAeFQIdZZ+oMahUzfKM3jdUWVMjcn9/9kEZkhCO3IxFrRlRsVMjW2MSbe3y1wljiPVpFa2IFztWbukH8Lm5zNJoyOhfxnjgbKVB035mfwYjiayx6h4t3fa5ggSZ9Z1J8FCASXDz8S5qqfYrPFvcJhH7KpgfxgUIxghqSS3Peym1gjHGmbL5YEoKBLe5MZvaIQy2TrTgNmyPVqaP8bmXHImwafUaU6QCgo4tE74JEQPmztKeB8t1c4L2zLUyo61pR/YR6pR4YxCeX4O03th3w2iuUzPsZLQjGGNIhvDlIVJ5u3HZ7B1hSzD3UovMTk8i9p/nTLPGcq5xxqPkrdb4vrQ9v97HNYZn4iU0bRSyLtcZQr6rPq/IxeUSsdXo2pZ3L79AUzCfZUCc/pnUec0Q+kZgAw7GBui2Be2wSnDlq1s5nhCOElQnM0TsnetBqZTwRCeqjBdwjB8HVMfBSFoxzqXxjt8X/F/GxDB9P7Qgii5RggCo3YNEjDEISAGd+ALHJxIf579M8WzWNG+VMI2hNWFtDnubSyKir+zxXCoeVniVLA+PFXay+eDhVu0JhsVg0OZiSKxfGB+J645dhIFWfrkL/qZRQWtwuLCl8tYJIzzHVNy2N6gJJVQnGk565YhkSubiTH3k7CG0oYZ+GpHD88nuHpHYDskY7Ak30/Rmc+8xk9ZBDCU1uMEphZDoHhWtg5PJlCKMkPa5ezDGiRoJZkdwO8pHuIz6vmbCxdjvh5fH5fzyMjQ3L9DV3pfpSnA2EKC2mAuqpaqwGvEemawusTCaq92HXScEZZqBxTVo1LMGN14jN4cNKb7wgCtawma7/WLmhooUHpP+JZLxY+12TeFoEZCQjPPgRbpmr9/qFcxew2NB0w6gw8EbdibgnsEUSVUmpBi8m2eQRrbS4MqcLMTaO+FO2IAb7VI3Lw3C+Q4Y2IQp5MZLFi7GMzKEkn4gYILMapoLILAOw/h1MtxLquXKx0+tmV0IQJl2lUH0GlFdt8QUJYB9gXbWNeCNSMOexY4gfUUVr/4cEFEa9eF5lHNPSMqJ7Mg0W9KoK6zn6YwYeQwvLPeSQdAfPZIoZCg0ypgLlEfNHFNZ31jgh5ImXQOrUJoU9ilnjvl8BIeSKdi9bsi1ymESsOZ9NJtEDC60O2Dl9jybV9RFcELtzwcsRK2m7BekJsE28PG4NgQ7StrazB0U19HzyCP2yUyldOeHwT0PHoXNPEw1gr2usWlKFqXsDMaZQTAEZwbm9cg0VG7AjshsD0T1IkISZU+BFTYJal8pQE2E1MOMripicVXi2dntQ40WyOnmu9v9o5PVHX9+zT3M53ZN4eEYNdjtX6EVSFtrC22b7tWCpiCJQQ0kjmZug8VL5J42g0Z0okWly9zq3ABp1OqumzpvI81AvoeHwovh6OeSrlixWWLOWT9Z8Nq8NOwHhSGkVCUbdbWGK/xjeMiIaj863i9vDG9M23wp6DpbahtsJydLVZ0P3FWJZQ2MgLhQYYwRwWnFRCKM2Oldioh4nUE44c2U8hP2ZGoL+T7co0yRkqsT5qiFEelE3iMPFXFzBvJRyHHIKKJovUiOBdcllFA10qEV2XewwdE10gP5UFxH0qyEvY6BhahgZtd+/75czuYNZN0aEn+WvtTz4ETVsoIaY4CEbeP/4NwTzqNvf8s069taoDMapUm8+S7xysoh0+Zor3AJPbRhh5EAuY1oc/W8ImwUGs7F/42qb5mY0fODwZ7AvGKtvjorKzIlRmr1Dc6d05bl6jbe2NyTj/QzRS9ue/iqlDEQqLt1gp8ep2Dc2xO2MWBFJETmM7eNaUY6pU1yUDJKlO0nqDrZUgsQg7dSvJUmgZsSR92YaRg0k1mk1A0DH4mNqfXpw470ARqUZvMTLiFwsRuEsKSaECimYoMq2SukQw8m6Y9gOL1i2yiDKLyqe3SVfyTCwkGL/YV95f2Eort/vExs2jFALLFuWfuYOD6CB4PgxDuCGNtdzNlk2qoRJXOdrBG6I+2AbhLNryWCQGZQRXeZCwppr/lPA8so9Biddxdj59OUkEVYcMJqwlJ4w/h3WclOpFXuzSxsY0wNa2MagUnqr8+5dqmJ9FrROAtOVJ+pKYiUnHtZlqFre3npgEYdtjK0AGd+wMUbMRE8k9z/FmjmDCTSkoxthDk9VvdTJ+SmITyehh2BU+/HUhwKOH/GgOx3YyxmoEayPBc8yIhU69a67Kt2acK2CJ0q7e9BR5sM4TYiXTWE+tzVxz3+45PtLk1hqGwsziEgcnoiIdoWncRJwd8bEMTVl885eb3G/icqXmfxHVYaomhWHUsVqNwrRmeeAQ98kjTKknNItYP05GiH1pmnqccs6sIuEg5hUj8NoNKAoIJX2z3wz3ZMWofc7CrEIzOCdgjdkVaAFVFmc64HRDSplWYwmNegxRNGN2tk3J5byupNq0spCJMThgoTHs42n4hP6LXCc41XmAC+asxK02tnokRogIRFdP/4tQw6g3Fb01tTMn2zyGxUVjvZ4bUMtZxXgTbdIAwmZHY6l74fYk7c0+ZrOD0gGGxU6lMtGPNokAkZgnrcZXT2cLGN/aA1RnAx4hhY2GmV20mEEP+7nAPW1JC59WuwB8s6QrsC5JWwkZ8VZE1IhgCBwaE3nrEoQZrCETyXHN7mfLrGRxrUhZ85g7iAeYZ/RCP+K4bQGUDf3zt7f7ZnmSAxhesrrav9Mzr4UzUFm3TLNPDBf8yhIni5G5BYBWsQnBDqQjJywxiloxmk0Qeh8MueciES0vhdmIUEDsW5iffE1zucSqUlPCqUBJlwbqoMOBPpj2pxEjx4GlF6oX3DJUqV4IV4kzD1Grv+E8nN0uC9IanpVDHitU9heuyvJmBjetT9tSoTqXts4lP6mL6Rtd+Sf4haUVe/U2JmIFPrfte4UmaZQAcieUe0s80VU1pYudVgDiW1gXu7ST3trE1MpjBiTzy5oVf/i9KtNSX1GEc4cWCzIQ25z4bXNo56zCnAJDGXiGhjKEZUH+HPn7AZCKmUhQ04VLIT5LKwgmAjopq6vHvJYf8zvidrZRehTALmMu045o5eQkz1DQeWUqaYXlAewTz6H3AsrjOoyoLb1BbE/ouGwfoJmVZGm2rB5aumKcwI7+qjpkb8gCS/ftcY35SS3ckYbmcKp+NyNKbwZv8uy/H1w/95ecTcXCE1DVijHTjf3J14yGJd6fyATtaNhL7AJfnlIDbJEAivdEt+vEnGUV4uPuOtQhMljUxXTZ/15hrLLKeaytiJ/gTf1J/yudaU8FfRVTGD0Cabb8UYkAu/jw/Djxrbchiu7iv1v1atkOvaxJ2rG5Vjh1HUpEDN/so+ydqWww+iXyLX5fOrLdNf2FMF3iOcldcFg4jRAZOn/SUJIQ2whq1H1HPYqeyZ6nZb3+/agRvdw6jtw/t4GnCbGbi5F9UobrCRwS7P+GlJER17ZzqIkeXVs4N6ckIp90n5p88XBI8odWnpLKRWAvocdbRBEzQlCaG63AohLWa0Pj2unCGEduN2BA3E5BomQxChpMViZAwV96DkYkpHDWdsEzfciwoJsq/K+sy0g+29tGII1+4jfdp4/ar9RCj+dvjIFu7jspxeL8vp5bwcv79ngQ6r3pT1YV1TIDGYpBhwbFQk0pQCNxo20ZohCGcpGkLR7fAOuWdPpcoNsZH9M6GkkedofEwpBGNmHhW45yn+m0nSUrrSlMpIh5zPBrNpBmvudeBMifn6E7fSU5QdZgcxCvsMYqkTQV98YMhJcAUbUg1rlndFvYPqQuD7SR85TEf1sDd4uD1NxgFeJo15Fo2yaRQzhtB/Tn8nNQpoMVySGXS21Kp5HuU7JDVPMc15IySV8wZiTIbnzKK6cOaUwoMnatkcluOz2KhsnTOzL9bctAOL+jUM3v+BML+PjKEeUAd//nA3Na3lIQs1rdYq991hHcynGpP9jndcTmZMtr0+DNSZrM+dOhCgR63Z3WcHU8j6CNCmRuK9YFYOHXEd1B1ekALXqBwuC0HNmRVrpKuWtKr/ro0aQdM2lMboWVsR5/sYQmyZ2xjGbA//DNZwh6ZwWI7vH64lGFM4fLe0s68j0yPLJiqhIEHQA8bP9cB+dih5AvuGmEiOJJ4p+dJHWwgvr5v6p/OUQhvYgJhC6hqZMp0hOEzE/Daq7kt0q+bnl8XW/umhjFeumUe5Ngl41xzIaWFcH4OoTDqfx/nV6/R5ZSL2RRuFh3QMuU9w+AlPgADGfdCUfJ7hfsg9UDQsnZux96Zwkc5fn0tuE/+MnjkISuN6J5Ec3k8DTZNkkYtCfJxKgX/oVVXiBEa6iliCsLWEIgt4620EvXlXnhHx64Q24ojowsssog5heTK4mMOIepcz2zT5rAVCYuu1mZHxVc8ytF63m2RK/XW8jvcFAhO979ILz7SElPpjjlz+Mg8jaguZ0A/Mk265h0n/cZ2PnfYT2lSKu/iWsLlsJJPbgI5yw0zajzCEGyGgLFn8yfvvtCkcPSf56eWynL6jjJ9FKL4jdbC/WK4vh1KIRdbvHR+tq3MpoZ30pWsXRYGQiGa/1H6OgDJm2UzYKDfr8BSqGQ+5mMROcQ+8jFKC1z6Z1wQC06Jk4MOaYXF8kq/FGewWdOTYK9M4gBBxvJyERvAi0Agv64yBDGHFAGZryOfvbPh8cLu+f0yYJQupSJRuSvxIV23fe1Qy560RUUQ8J6TUkxEKkx7FmW5hCPqnEIg+HtRStpKT3GO5p7w/sB91BojpUcVGU1a704D2w4k+5t40EK+xLXtY7AquHQAyolE+p4TPdoIYCQlzHKrBJlykUm5zpeQYNY8YBELX5CAU+SeaY8n3chD/CFpknE4IUOmBhnlJDSFdg8dYnCn5ZVHnewiDZCZRpW+Vm6lkt41/GcezNAGWxD/n+QbjbX49gbL748stO8/duG/r9XWX384Q7vQ+Ongo+cP3s9sSPM+JF3LBS1NiE8gIqnbGNaTEFGUYp8FsKu3J0BKFyH/9oQ1GUjwTqZpHPlyJPs0D3O0aKjEw6R/ywvsYDNMFtqqupzCW6ab3nO+q3vrhBCGj1GbzmVh5M86mAR+iEt8nU5YKAG0ZkBiimhRSffNg8wYSeTeyYb45r8XgyGycTLMxZmhIiU0LmW1ErNtQ1iRtiGPRYUT1pgZ13RfaMqmb1CRYHUSObUMhLcx366KJIMKXMAyHBWRoV2J6lAdERytjyHTpgI98LCyJGlJ7GGpH1DW9b5iqosCbbmQNH/6oiAgNwbqk8AvgExqZAxpEzASjp3usDn+Xc+Faihv025lJ5kOJHMZ0JifEO4+PcDs1DzxCRWYYbskSQ7rnXpE1lCh8oRDje97rGRjAEDzqGoKXai4JN4s2dClS5uhAxp9UwWPOJBpakZ3lOWxvkHGX09MHru/Va9rfIbvKxp+dnx+vpxCRlR7VbJPNZFlUmlOFV6mz/c1N7ONrBMrxyCE+JZHK1zcptIhZuAaTXdw/HdeGdsJ0CCSe7Jt4DK3qEnPsUmUuDtooIxnBTthUJ6RsgH+0R3KWTKfViyM2KqCQ6QJLKmUQ5Jl6OAKHxgENQyaCS1xjGs+okItK6V1jm+Tj390n/b48xfLuroEMN0bPUGqfMaVzJn5juuiW+ZSlObu2OIMntw7GBqQ0pPU9uxf2XP7OQjaS8A7aTtBHRl/TA4+SNAvREOaAVM+UFCDC7nXTBRll+I1AB/6OHD+EjrS8K6uyFYI8/lXoCARUBIg0UmuCRURBL6cIssvqeNYHu5WBmxKrU0vSkhloNlUVPnrSSxEkuWeKhqDV8iBAqPedIgEXCcCU9S1zcBWKmTCE7SvL8zpVmz6h73X+nNBMgHW/CD4SjeCyeWhEihWG4NIqg404gDKYSpC2iF4lZG1idNMqbg0p+2JpLN03G4Y5ldyUYeVn7bm0Efj+YepsZL/0oDzN/R6bPdJ/QCWGkJ8SsPuuI80vjYXan5yH2WJOJJnZfOjBDV1ciHaXkkkFtpZ/5O4vr+1dmTERZTx9/VJSw3X03nIozeASwlCaxlpKYWrf2YcuyW016U+9rGsPy41EQNaLe8ShlGH0Lvm1tJ/GKJxwPQx3USOido1ricDGzeMPRWsiChqBfUzNwQ4784gxHN/MSxD1iNUpxGEppIIgTMfxuwEdgWZ6tnKSJ8RJ6xFgPNQ6PHLbyoTCsBxJIFE7upW/TSix7xVCZOL+OzRUCnhkgNAKJH132TPa77KUh/FT93LaGft9pDkyLzvbpPxs71e6twsnrfGh6++8o93MFFzagO90hJxbSgtCJ3aFLCJD43NhW+pqbsiEcOL+rh0UynMTQ1AIhcY7pspFumWvxdr8/enJsRq0PkvSDnhRc3iaeJUt2UjuVodcL+5Wh83PAixM4OWGPkvzi/40hkRYbsXgpxt6tkNE2yFU0GCMUbpxMAQGxqUEo9IjtbDen6LhbjAx2RernDMKO9DwTkZphl3RKrPMY9EY1KvlyinosMgsolUZ2bT1ed86vEzPHHagfJoWRcp+wDZiRPsxitaEcRUatMcJmYPHaxC8jMq1s2mS8NAoPMAUXmURBW3eRshSijiJklKCxt40DIMpJyw3YjFG4CQpU2NwYGB+nxmcXfAHzOqatLhjq4YA+HAYg6PWBdeLKdYjFbgJedQQRBO1sSZEZueLzADapDIEJf6bS3gY5DkFzq1LdvYdv+/8R//gOLVP5eIbqbtvJaAGeo8yz5+mKRiXfTgtH0+H5fR0iqhDx8Il7XAyAc0KKW6aKX3HgfHqUFT/Vgyh/y06bRvpSGegKiWNb3I9o2Sllq0/hUE7SiQAv+TcQmJzKMq7A+nbi4qIwY5Bae5FMbBeF9JdEiZDYJpfFv7gqtHjaGSM5PvmWOhqOlqed8AuYAwFtmtrM4vYKKuhfv99mQ6TvzumyWLuM03B+xLj9mvciBy2DUakhpRpN1mNY4tkZoAf5ao7dr7CI33vFe1m75Fd2dctWqXHmDZKnQJvci+SaBFOtH1h+1WJorl/W9Car6PWe4ikcH4lahj4812bOLtTiN2ntQYSG/dzIkzXtVi4zLJMpWgRA5KQqVE4xwg9A8Oo7eU5w9lY1XeAQVmhHhu/EHA/S5nOHQbsnNL4K8t+KixLozoTV2rTc5KEf9L0LFUxf3b15L7Jx11LmN7/CTF/1omU6w4/WVN4Oy/nx+Py8Xxc3r+eloevT8P67xtVkltpIJY15JEJSYQGWRZX4ZlkVao9wnRNMlaG0PFV+RyJxEaGSErQ6o0ju54MQVVWQECe5IuP4aFA4E0wEbwEhUDicFqxcBQegcfMbMFSOiFBZT4elVp46DpDbM/LpH75THhjaZrnPvbJNGuKjVX2lZyI5AryvJr6uxBJ1vIl/cgykQF5OdzgkNyo2eFR2axJcDCMXOwP+k51OBiDEE2pM6/xc2VUz99ichR2zm8mimxZL2iemgHWbT8QXrIwz8NbCCvqkfYGQud5fJgmAnNNqInStn0KhxDfc7bf7CfdqVcaKN5Nd2Bi8BjEpjDAQYpQ42k5vGIcCDvrrsDLSfd1QEUY8ytScJv2bEIT8zuZJm+eXHyXaeY00OsZIFwk8Fvuo74oWwzh0KRr/tJ5QdEQNiT72cf9HZ9tpZ9qm0V6nPx62BBFV/1ZTOHDmcL7Fws+Oy0ffz473Osvc6mAaWvFNUxU3Kxglj7nkheFroRp42mSlw5GiZeKcdiYpTgKJyu5vA5IcguN/VwxX1U1064gXjmUXnktr8F1kWEzpCBP7/v9dbl8+44D+jLq+BqTygjYQTgG/EAVXN1mIUHZl6jjXOoHJEw3mFJCSFTpce9cgBiwzEo95vQwoGt6e1tDSvVwNc1l4fqnpwrw6CxmbxcNF18a+n28LR++bpX0euP6aZ8KCtm1mcoM8qs+eP0bCRVjOzVmXGiH5ipqGptDkhiPQZwOkUhKEtpbbNyMFk7ihrdwfukZRIZAQcQN3A3yUcHD7wvoLiKdmSJer4NdKjZ9YwzhNt3PXmjiagfA3tQ0Lo46mOaM/iZDsnmBlmBzYsIDU9ZzwTXWJ2EvKYWqrQhOdXt0RrFM1nN8sUPY71BY9ZYVye6afb+haaMDQdgg/pefblMwTeG0fDxbENtx+fhyWg5vD8vp/TGwwwy0Eh8yQhcOs5wjytE5ft2UIfU55YDXqBDlmwYygY8Ky2jeUB0HTcl6S32caB/W3CMJNoZ+LYbjhMuwYcN0oSFEFKpBTVadSvKukGh7MM7od+DoIx4k8ehitJRU0fQmaQJ7MAadD+CP7rIKiZXankrQCqGUzTmXhkrjnGtiOY0noAbBzQ0ohR5d3jcZU7haovC9MQkySnrtlGA8OUzlpzA80Qzit1v3XJsDfVaB/dq20j2XeyVKpiZiZcTNCDplOxJozyL6gCJMiJjv2jHWLdNKuwQedQxWbr65PqxBEO8mUwiCfrqBCI4yokyTcTiBDrgwKJlcJQYhv3e4Cl5Rri1AWHI74Ee82UviCuSZfe81tCf2A+2n0Iei/Uy0B2/lb1Ebk5Es9zVd/wajrjIL7z/oJ8FLP2RTiAyVVlvjw5Cjp+NyeQqoJEL0bSG1yzUOwT2QPLNjczymhG8BOX4hcsJMYaShCuW9YAYFT+bazSqiMeAs51S1BcF4R8fb4hGbYxEVaDx6DVzivIYP4QBjBt9f4vmom5sF2E0yZB6bVJ+WyhAo5SlzUvtC5laSrJmoYRyEbsA2LO1YNImEOQZRHSU59XCIbai3TjsSnoHTAaNJSURhx3FjPd6T19GLLbPIAmLj+BI/NwzbJMSmDUAbinoQ0h/ZSpVACgP80dbnTGGmIs1x7mH74n4mHn+IPZHYvkGTrpkb8TV73NCK4wwGbDPmHpAjaw3kVxAuGBthcAyJKzF9+x7Yf9qoVEjgHpDSt25P474w4c/TcAh8k3tMmfNIk54ux36tpr2vc5tQqPydzEXjEPicmxnCGk2on13RDjg3Vy4Z4+8X9Oev9/TGw5b/Z5iCSbNWQtfOsTGHh8NyPh2Xk0rPUvw6Og45PTMSShHzzP8jmUSTKMw6INKtGt8K5CITS9U3E38xyEkkREm2tbkRKDGrpqGERlVPSF3uHQLC66q+4aPfvw/NiMY2+5MSnAcCGiGQV3eGQC2hbGJhjJnaOWAKz+TKtRMcI0hSg/rwM/L76HgkZca9ggnnLtV7HPrARYYGoem8NQ15znn8nixLJWONgC24ECEKpDdJYsRLGoPAuzdb18rFESHnttw/jNQ5dSxc5GPCAzn/WcVPoCDfE+KJdTguR88JZO7VticgiCHldEkPIeONOTWbnn1vKSNEmxIIJ3JvBWznBJ7aCAepaVdcKJKEdizByT1o58CEHsuiWYJDVTukkDHxKtTsp0yWJ4Q3ILUQOMr+4h4bi9S0gL6uk+8Ocu90I+zRjMvG1Tdo1Te8KvvM1jXhrecgF9RPTnMxqbhGyTAlWnBqVYN4HevUlvTVsoFT4kYK6dUB2xt04/QpoQqWTSmV14MBbU4TIwtZ1SyfKYVZ9P2p+8M1TiJI3XBGNZpFRDTjpPpAK+NrDGFTolEGyTTOTOKGHDlz0NLvHBqSF+EiRr2eDy7FTS3ng1k1pRaE95eSrDxU5iBrUruboeToNymZWgU9z1SiyngXwA/UInP/NBVB1m3/XMEY3BjDTfORZ0bcs4Uh89lAkrJPGfXsHm5Mzw13Tjtr9FxjgCTtWP5cHa90RMvXUpuyIlJilA0NgcWfcKsWpuEzJfNvvB8ed4BUc2UktqTMO+egF8pxOwLHPIpQDThQSm4qtEoBA1xaExmnQHRNQzjsMI9PtJCvPnHv1j0rOndtA3b1/WemuXg8Lcfvl+VkqbPf6QuNw+7E7y02KfBP9cNOmwHdxJjBkCmFXVKKznsJRPM4SRhnY+AbmtboMP6nIfd9Em0DmdpLUU+JUV4z8q1HrhfWheB7O6Y8qrUFQ4hYBFZrczuC1+KFFtMXXzSTzONPDSMTp4mEpYMng2XKAnPd9AycJoUaVEWJs85bwmAZZTupmlbU+Q08M6dWmDKZm8atsG50Hm72fcRRRN0Ng5aYnsACnzRWhIxacGa6H6KPHjCJIvZjKE2TYIbMrj3MDtJKEVCbxMbhnOHEKr3Kc+JRgvtndTYRYppk70zTqpTR9x+EWrWTnHP1zkpmGgJdgSfdXXxNHIegMmqUZ9xBetwxHYxCQsKgKOULjENX6bBfgF5YBUd4meW4uAZpAxmwqL6nVkbLg1o9jFIb+CxDOIyf1/bObA/0Z+/tn1kfbmIIn2u3M4Wnh+X0v87LyRnDJRgDUl6kqxy1AYcr3qTgCDYZ88KwRmvxlCFRkIpMsyb0r/9WVbSBqabv85aa6hWiRNLXg8TC6aoCIyX0MPQpoxm1ERiL4POCoJuoxyxJxxJaa8yK7/S503lSlVuH3mAlJ57w9vI1MIbdJpI5+JmPHwRqFykq6zX5vhCAVpGuYfpMiBdzbVDEIEqZhtol4rfl8GoMDrXACWcJlpzSIjWvIHdIWNcD7LS/IkAow5gRxNVY+31bTZ+7oe0VvF6L3tjfZjdBnixzY2YyQLc7IHU0k8mBARaGSU0n3calhrQwbr/OYxJygdbjxD+3iWUwGQPSwBB6bQhr6QlEYo1HMwGf3eP2yWAIyfx8qlruKGUG7LsSZoVV82/5PeGeJkzd3A6DqUyWeLV3oCSuz8sGQ/ikVlLu12f2vv48pnBaDq9vy8P3i1ddO74Zdg6cnt4iqbojtYLVpaUE4BI59xYYCfPMWwoKTRjHjeLEsxoYKNWlK26OdeKeVQzM6rHQCcOEISgDAXwRuWjElZb9ZWNCM1PH3bUwgtT8spICALAAfcEFPx/aEXJLpcYl2OcmDWpEp3gkIQpUVX/2fyLIlrEVCUvZ8c5GKxqPFFzqBDEJD8bt3R599zoAxtQtACszb2Ickhvf958zwxGZ6/vSakxcttTorYncZYufaqkMCaEqmTkFkw7XYalG5hALvLfs3PBS1iIwOFJtdGXvC5afr66Bb5nlWCq4RVcuaw0htQSJTGY+MGW8kvW2rJG/tXloJfQZ0CCz6K4hZNlTeY6agKT96JrBlCFcwfoPE0jiTiLL29btTk+3IpDNNNnLFcbw05nCMbKkfgu8/PgKbyIwBrqejrqt8CqRkPrlCUapj4/lbBKP/SMMYJv7Ws9VzSdszAlYTYy6q8FnGSvQzkherswgDVn2eMI4qcqReR0G/JSBP4CpPsSWYNciIMghXdaytWtMm6C3zdJUY63r0GnaSroQQpP/hMmykAvmq8Jfc6Kfn219l++9smZSxCSN4fycRIz9B2buUrI1VBhzeMPmM+G+kRZiRIXDyJ6Fnuw+MGrarKhldE1roh388oY1cnfULFEb8EdEb4+awz5NTPCWa3r0WsZmU8gYD8K6mi7amGfx1ydcKpCewytSwc2fN8lC6/MbwYRZQZBZOTWhokTu27ppfq/wrEL8ggmE3FeEndT2oRpUQn3iVSi0YCrEcKrzDKkWsbfmh7lkv8UQZlpJfnTnxrpVc9j6/AdhpZuZgrmgWiCM11KwjYtSnJ53BWq7n22LOl2qkTeSxrEUZST8cofJl5eKAyLiOYyeEZw0YOqW3nmXU66l/ugjO+ZvifcSq5UNV/LreFEdeL9wfwKe8b1o5RUh3bC4eYTZw9/bg22iaPtiZXWZa94NiPD4cGhrxt2bRN3hFxDT2OeNGeQjEFRFT51ZPiVtJJpK7ScMYWx0XtcYc6l/y9oI8CTJcdqcMnYiCE744g9PlsMJxAiChzNavlcq1UVeHlbzkup/BmNSC1EtMj8ro9qZmM0JEwl5chhVKu3z51/BIQBEMAvFIIdWVmRb6N6M8RO6sbTsHuEctR3SOG+EGP8yMhn99W1DWE2g0sEQvCfpIEAbAEtmZoS55U2i4OIaspw72/t2vi0QLb2naq4zD0rMuSBjsEsyHzmmVbT2osnzORuCDX6WDK+Det/JEJZ9hjBjBtKHT7eu7exds8sQbhd67mAKB99gxhSsE555kZARfYfTX1pUYSags746lBHYZ6ThZgZDS7OLyOBZUzfCLjn0ZYLUU6QJrcPasb/cqB3jJyOYwElcIOKnrJpFYydtGG70DA8bh9KIqeLy+G6SIG41/hmePcOpx3zxcI7qUqLFdXz2R9ps0WTuNSulRyFTA0gBVQ5sSsCAgZzwYT1MqyyEERg5A+HcJoGANsKPsBdF6UtI0D3vIb1/NufhjvlZQR76GiFKhaGOnzkfcA7w8XuBJmjYdoH9bddpSVfkBWKKCz9XZssiA03oVl2TK+SihDmwegYb2nmPGAS6UqcNgTATHSt8u+MMuBu2pXIBfOrPH4Sd2rQLBeaAoXOYUy/7pzCGjXWZCEZFQ0gCe4X/H3a++zRDEIeUT7cdDWV6OT0LN8rz/jBTeAzj1vHlPQxa6hPse+IMbJPqJDMXwhuJk3GEV4xJQRYNTVXXIiB3O94JmEglGvTiXzVpOw9BV8vUgAWbgI9HUzJPWnpPiVueT1IlgpnWgJshGcGEyazeoVXOlGnE80ryuhVOPw51yf8yq93cx1gO3Cc2sMJvhO3cPDScDnidS6tHSMkw3GcgFAkinud6GCLBs+QpU0uo6p7BbbYvTiHBQvvzAEqzCd10PmYeSVfaFGJTQqQQhsBm1PpMc8pYA6ZLQaZdh1dN07QjK2lhuK9QLyEr+LmgxQzBUUKTc84xiWwyIF+1H/gWZgVBaAdWLySj7jEOrVNAN2xjCs7I6VAyYjFKPfPmjqo2tYRhNRK7acGz+R9eRvhfYRSccH1WzsR9Tdbyqlage/RWxrDq96T1PdrpwCcUlfuYgm0qS7L1gIyEoinkxqKHyBEJuohpulSA11EaMtUXuDpzI9UU1nuTHHBNMpsOr7Rr/RJKrCCsykEH1CFEu91fJA7CMorPd8ZDKMT+kSjbLSXlQ8xhvr+0nuef72hTpAehaEh2SGHM5tq0uSgQkY5BNSj+LWenZkyVPmk1tDzsAnd4VC7de4GXlHgVyTFFF0V2wX7aPpLD4gzFXYuFwdJtmAZ2N2AGROeG5y4qruCfmZvhDc07ebztUtan4NlBFbQMBCMMhnlzxuCMUxwmYMR1m5dDNiMXVASO2RmDNi3a00g2SA2Ga0evnnDjjbTaqDPuxaPwU+YrhSB6+Rkz8NrPyNMEzS2N//a/txGwmGeBTidFux97KelL14r1M4WKlOCvNOmZZ9INxPqyI6x/Bibag326lrGstbubnvsJrnA7U7ArbcO6Ue8hMn5mBkKpjyAeL95/2xR0WbUCIvY3+0oGwqheFq7RRaTaOct8Oxt0tzdIv2g3iEChXlaPCyNYc9mEJFjaN7lXn0EdHdiwHwy/HsQP0rvbY3wzwqaQfT5c34m8rEmn6sueXmHoy4AIaANRL6ScrO037p4ZeIWlDYHPi1QVTmDSC82kwBZQlilrlTnAJdhsBSQign+rjcCeWYqwU2Owd7vEaeNCfWt/x95gfhxSu4YUFGLMPSOQEYmp26+kMprN2+g6S3ZKXYnCXM1wa8+MMaWxuLtwJm4PyNSYiWn9j1prAYwqhY/hWMFYJbehGVPwdDaH5UgbRMbXYK1sXS2qmk4AiDqPLql3keYFw85MpqvQWxNsVgyhbeiiIdxAiNk+Qfc3UYCtz5R+TTXPe95dp+cX1GjGQfOSemAIWcmo0tHYwMiY+PQ0Nop7j4jkjGsPM6mUDwvKN1e7uoRZOqySY2CiLiCIJLWSEPPx6p0QEJAzuUzGJsRVXO/0OdEdMZqSYfrHkeuHBHzEVXcoRyTtJtlWQy+ZgWkjEvPAFMJy3fi9U6wBvTiBHS+6bWNKnqaU2rxGsEicTK7m5bJBwHHo/cB3WI07OjWHCMJLu4POk6f0oJcYs3Ji3Zid0yRpL5cpWDOnnf9y3S8/oDF0Hr9xMolkJLQKuMcCHJ8ew4DsBa0mdYEzRqPGz1S7lzDP8wgIzHggENs0ONuVVkrWPJosII6GbsYfcCrEdhbFbBBgmMGDELrUkK1MP6dUXMZ5jhX6ZFxF22eFJhThUZ5d9m2f+1uMzIf5dze3T3qz7Z61z3rIdWb5s5iCn2e4jLHecclVrjn9JfLUqkg9Pi6Xc6SKPriRkCozezrc3wp8BCNJbOQt29I2QxjnA95M/gde3PF1VO6KYUBaztrMPBjij00jtB5OJeJ2CFRalX46VOQMVvu8Q3sSG6RupJKRSBjsjx8ulHicaU4TEZb1dcNT6vomWkW8dvguGQMghMyLY5BiMMYcNFKKRLZQ3RvYGiDuHtgEA6viyYlDA4vOfEfC6IoPv6+07QcdA+9vTLintvi0tDY50KpZMmKdQgQLNZk3nzEGl/q5fZGfSKOGV8GBDWOUdQ9PLezR3PORP8nOqjEErZ+cc0f7H2Gdd2EIfLadV2qN7kzysVwQiNj3XsQyiVDlF7W4A+73TBgYEzq0ZAp+sGOutILJvPOem6jk4YZrdm7bg4h+RZu9g4L68rNzH/GFFspOCYqq3gCa0QkeRNu4Dy71qPuld01TP3OBVHXkc7jg7st9BfeD1BV7r0ud7Bdmic/W64Soe4oEwXSLtwfe5fnnWQKwJwL0hQBG3Mpg6vw5nEXj9q1tU5IR42hqL+0wzphPZ6rQgrr2VhlBPWxlzUTSZyU6zaDp9ia4RpZ5T5yZcRvsvKRmNi2L+4tv8T1o10eZVJa/zD4kBBOeSJwIlWfqNE5qSFxtcyku7Rc9hoR9kMh1/RfazYDQEkblGvM+7j13kiBsKdrE7LioIuaKwyGYwfOTa/Z0N436yShqlB5JCBh02wHsB2pzwvN9T7tmZv0DMkAvKa6npcaBQTxIAF2J26LkWnAuZc9ibgaDn2gRfZ12lrC02dx1Aev/X5ucqRUd/GlMgWl4nx6giopxh/g0IRGjC9AUDstjuK5ap75FXELmS2enPZirdjqkddSm9fq2M/Uz3l6elVLn3C0yr03pp00in2kb1YiZS092QCSXEzw4vHAO0g345oYNIVMGUMoqWTyJxw7voOKSeg2qKV/vMIYcznAC2E0AyHenJlSl/1VQXL5/Z8N56gKFjlSLC2ZYfNCzRi8zX4r3BGHATPY3Y2TomzFrapeUkjKDrGZqFXiojyEZw8Y8FcK1A7GlRCsaS75C7AGY1xUv0kL2HyOtjNv23FaHgjKzg980h1GTm+kmkE7D9vjz87I8Pzl0ZGuWTMkatRF3dbVgtCjrGUGD3bhu2vW4L5gDYklMQOTYaJtzbyV6WzXNqsy1aAQKH/XJLpDSZDE2UaHZd5cpynq1/ah2oGOf9XPWr713zxjtTymyA6jHmIJvTMd3mxG2Y5oe4PWwHJ64qVGYvA0ioAvBIHUWVD1kIZJtnGXEI0j8REnDy76RmXU4hZIHvKOqxCSHxMeDWsJUn13iGfaGUvQG+Hl6G0EFn3sdrWY/d+M0tYRKvAl/hAaXtC0hlsMKMtprhZF0xqDv5YaVsaTESeiIEu7GyRpEcqQVySyvOQ1yKDb6HjmPWg4trqsnhhtztDosCrf1vViYgHqC7E4haNWYt+Ip1irf+bAMVkEqaFW8XLh6fY9065TS0+1XtIh8bxWWSroXrJcLPs/PoSU8Py0XYwpqw3CIh/WjoSG8vi5nxh/BgSPHSegL7/VxIDhzpO+O+imuEUkaGq8ToXuMe0ZroJc92BiDKBNzoaFskjnEs9f+nZrBtE9D+/0xTPOnaQrRUau+drSDZQYx+oAj+Mw9aljkwhpS6BouerhYhbbnURXJGnc8F5LwD13UsOhMXZ3V2cCgBq1UXJIuclJDgTniT2vGMBrTRyPRHV3xnCkwdQc2Ot8FAsYsjyEJhqdLMCGBmhQaU03hFoawtz/4ggb1JGyiuGsXRaf4I65D7qVKZAaDLoRR+5KamASggRAkASxEcsCC/iefLZpeFX4Qt6C2p7RDiPHaMu2qMwH7Rm1hT9ramhNM9V1Qn2ps+JEJ+mhk5zlIN+IoGuRHwryHOAHGJODJR8NuHhwaa8VwXdyPaRyWs+cMwQzKxgjMIcQ8jpKBCyPx/qAymlY284eHo4RVyAvPqUi74XNGTdhpxHld0tbHGwkjFbqNVBqSup5wa1lLXZ4buHL/vWhjGwT/0Ihve08cpSvM597WGd3mdTfwBT2Lv8Km4JqCSW1Pp+Vs7mZPVhYwFsQWzmvBukr5thzerTQbDh2Iw+XxEtKIEWtkDk0CplAQ8VWV7IlFFqm3TSCJrfuyw2fay/ONMpYhzYfLXSm84pvVwe7Eq4MZsBjOgH+CnrCAjgbfDCKc8JVqDVohqnuNaFNJFVNydTEhgUdUuf0NicsDj8YBXhvl8Xfi0WhOwLnrJipsEklcYzg/03xgPpkSQdM5R7nViTSr682C7CRIWsub9zEGAh5uISsQFlpnY5VurZxgxlia59u0iVfSalK22hAGDqt+TWwEtKuYp57Bk0VDRb0SvJPEN+J8QvDRCPm0wWlNC95tWq0xA0JGrtUJvIXrXcjz+smRw8iZg9b4cBqOqmtqpHZ+AZsChTIXtozgA0by82B2HsQrBMEAjBSJD1Mg3KTckyXY1OC2CC68ozYZww4T+FkMYQsyGi9ef1jG3OgI6c6KlvxUQ3NIvu4FcUJJSQ+MMeJoEsdjbBxIE2YstFw0hJE88ZVhl17LE2kLtiRlcWt0VdNSFTCVsndFqneVLuKAuUE7kqHFvMSdkcGUwTzx/FIwx/FKSDKuJUh9YD7f+zdznYSh036ljeTQCIAErd0GHV3j7uP97snhDFU2A+NA9h7PcakrKSCYPChTjWJcOW1qYO+SsEuXOOT0uiFTyAhw2BjMRYthy9wvZMbmfsvNn5LwWJvwphKpN/suWsvdbYOobHzvfOayUZeCfvie6mfEcyST9KSKSO/B621fGhHX95BJcn+VYlLUUC5rhkCp3mwIpwlDoIbg5WKhJagwxzV0jymxvyG5ZeRFg12Q+9EFFXz/EFlfmQJm8FsV9mIuMpX2zeuyIdDMWqgkmxccci73Xv8TsKVNG8K/t93OFKDulsPFJGv0MPGMlu8h5ZiaS/e6pRnakAojvSUEkzQCUA2itCcg6MwL9tC+NVzUrDHgJcsKQooZeHaU47Q4C3p4UPVMAqblBSVoKF7A/1W3uXHORWMgcVNbS2oza7e7TQkmJ0Y/m6QVdt9/Yw6RAyilWmQVHfl/lqlNYEQKD/hBYSko8es9kdMxbBalVkYszDBUMg0DJUPRXkY5UUkxbs20FsIRJYe+2Ccyo2c13A/0ZhYdHoOIQi/l6jbIPWrSmGZeOhU76926DwC9+D9CbcwCa4nl7G7H/58yxXTUkYgU7SHBj4jm0i++FtBM2HkeM5eRMwRrZCZwJQ0bAp6dmZDJaKnlwXMpPZZwvikIqWDA63n+z+GEkP32NdU0+lFLxDPIdkRhqzWS8lNo6uEnM4Spzco/EHjr3mf+PDPD7XEK9lKPwhVvCGtGXF16gaGK2REtAIYV15i10jsvzKEQWSFW1jT/vmsp1Aos8Es3pWCugp26dEHJy/7Zd5Yw0w8PJCErfp4eLcCb06d9YsiS7g5byHCPLOgDD1oSTnUTFaJ7bfHTUkzGNz4rWKpG/Cq3gl0npcisYaEQBs6ce1ANIp91CzoDy4PXCBD72ut2m18905kYkSOsZww6cxUxYSJdUMRrTJkyoEDfV28fK6Y7UpQwL/9EwizwYyPk5aDuBQt1qrPFIDbUdwoInm4e8QHPj8vl+TG0J5Zvtf36L9gPmDQS0EtWOnx5GZ5A2WfdGxIkynKw9AjjfLumCcLvECeSVZKRZy4kI9LWRbrMhveSMxnajziv5+EplNlfVfCyOXh/TBtFBjFyUXQvMd18n0edz2IDmMz7HvI3O+eH5ee1AuNMXOspIO9oLFdecD2m5vKzmYIXgj8vR8t9ZF2gm6lqDC4xPC6Xt1e3MWR2Ro8mhqTH/tOrpKiFis23BeM9BkOlN5Hmfh/GxggOYyAQojKtJcQAP/gkoJKAjBCSMITEoWeYs9SjTeJUAuPUwKzpim+d+a0Fwf/ae0rKg9TQRNJOl90mQfJDzSvIsjw9W2xuPpHoyzoKk0aAYwgLYXNK5uhzK0xdx0KIqEvejF8gvJL8VVI3kD5bGggSyLyvzaFoO7VdM/g1onO5A6fm3FCqdzwewgvtBJbjiMZgOHHkYAmteTZe1CpQG1bvD712JJAw8yvBJujzY0yWWjY0ulKHgZK+4/7iWXeyPkqkOc+jClYlUZ0IYKidoVHVZe4BAWoJz6vtVrpKRnX1eZed63buV+Z863vIDHPf3kMoNFbk8wTmrohm21DHf73EYpq/cglCgzRiUZhvr14rwbyUXBX1jQ2cPZNbYTOkfUA2LzeFTiTzuKBko2e8BLSUE8LJgHeKawrqG85Nm8XROZGa00g3Nq4QGqhS+JC6mWdGtBtleEpQ1X1wT+fL9299L2POReK7+m0bLsMdvvLPCleIpqk8yqZjvv1qeOS7sriRS7VhbxrBZfQuGd5gqvGN+hftUCYez4SCJJSsyzD6kaZvSpCyz7L/ySe2iFLTBe48oyvtgOOjYwCgSif8KKtJIStqayMjQBp8XwMORPI5h3eMQXh+IoHetFAUJe1MbifZantCO6Su8N8bVGweRgH9YnD0aCmeYA224rszrYvuf9H2G/NxDdK3SivRu0XsNmn24TpDUGZrjdDv1bZz3Qxl2H2UMkx6W7WxNmRg9fvPsGvczxROy+Eff8XG+h6aQLprop6CQ0mPT8v59WU5Ozb5lL7+q0Wwjb9EptQYEzeZ1m/mYO2giJtiJjYTiINNMe1cnBomPy6VhSXRL0Rc1OaetM21HTzDcW8lUq1PAiHFx8p4Zou/rUrW1MC6SCByni5CCtazMlY/sJ355tyM/mQmWkJPZDqAMBzXVq1R14PV5op/PN6RfWkbWkstsuY3bQfMSFs0wxYAJ/0IDQfRzXlg2mGd9QGHPaZI9orOSqdNU4IlUSU+DHM/VU+gy0gBAjjHmYIFf6U2EO6iB/PsM6b617dleQpp3o3QDvmZ4fgJ6SuGN1O6umpOJRqyff1ol2Daio/m4ST7zTWLGpm+2uMZG0IIsJ1FaiS+tlKnHB6KqVEo/ChaRslo0M/7PZCLClQbWvshn9vG2fbI/Pmt/9ek9qRB3aOoXdPvWT1X6MWOvPkTDc3wPPrvb5GTxQuIj9S4CUu4Ieu0HN6jjKJLkhLVOjxSWCR8MJWUGlINbYTdD7fCF8LhGcHaV2pN5yaL2YzJwgz6HBQV3a83aCzKJcbfEl2KRespN5IX5DMOG3jwBkPY3P+sGSDagl8veCucpCK1NGTpfP3kwZjbyFODm4XwZCZW2jmWkSMn0x7L/Fm0ayA50BjVW4jXecAToAzx0HKjo9ogOEe8pnwuh7kduFXQ3ooZ7B34+F8wWdEs+KN/pn1JHsTEcdRe6b4LQSPTQUBboPFWoZ2s2HdZDk+agRaEl5ALBBo6fQwXV8YRcJ34e2UI3vB+Lb9ZhCZqAT5AI/gizKngiI3vTg3pFUaBq52v9EhDvM/lViIr53OPT+SSixa5+hytMKOJBrjV9hhDeT7+N6Nfn26Hf4Om4PTvuFy+fVuW1+NyschK2BFSpSRBtfExqtE33DvC3T+q+srykN0QzU2hNgP2Izl7kw6d7pmHEjb1LHNkn7QVgY//uXTqjMqM1WJQ9rEJvERpawmDeuZ4y2hqQhTdjgBs1tM5U028stOuEasxQTLmdUbJiJIVKW08XCK8aWJu80ZGg2czODAS73HtEMGqB0HX1I2o8ALLILL6L54LDUOZq3mfmU3JORuSKBYpH54x2V11VpCx8vr87nADw12vx0oSK3M6YQ4cx1kdIRTOGZJpwixFSkXUf9O4EyJNQhudo9t1JmVUzJ9zAAiUzCrgPBmHexdKttQ8q5o1IBiRuwBzzOnN12qKUADMWJMYl8PC5UxSa7DnIigUhYJ2F6WgCxutK+B5rXzR981sD11rfMYm5NWfM3luYRSipZMWbAlzvO1eGOvuiGbzZ/ZC3EEcHRe1ABtNFieHLYroSGIzx5aB+bvrHQ/DyIlSPGoyIlUOi3JsEqeUWnSTR0AMVXbeX85qgRHwv66W2iWeNRQGL/TLJVb/NA5E5CobCcpGygY+HwSsw1VFZRDC1FwopwzBh3OZq7oTJuTzWTLbyqaaSNRFgiovHVJi2g3CqDRw/jRuDsLk+4RxIbQtgdEzn1R4ogTTKH1J6I2ES5gUn1/gQiZxu6zzaZWDAoa4e+hv0cPn18RRkMMM+JPno6Slxg3OK23fWjoZFrPiEnupTrhxjoHVqNXccwOeSW2hZIwd+ya6RoeMQawzqr8bkS+aLh1eS5JdGJ0d0JDXVXkbR1nhRF9OqZPA8fiWGszOhIhraVnGRHFU+vlqYdp3Iny6rKbX3CEwaJsxBL6rQ7nT1hgCO5f7eM9p5XMCz+0RzUZwU62NvDahyn4s5493SCFUT+U+Ca8O2KDic6P8YJOophxOtQORlIg9S46UdHFjhDSfO1PvlSGk1Avi755+ILyOzfIZulgSPEWiS79rvgo5/CMNOOckMPJSoWzM3PX8OhOmstIUimQR78woUaZGmEookXmyawvhRlpzSjFi3EnCxyAonjLksJYyw1nAGKxodSYRgsAEQ2CXh5EyjaICF0aMARmC2KF8vajJjQkLFBIp2bPfM+Kh6ztTCza0hTJZOrUB3WXZzSOxfdnnZgdwpoi6JUi4GHWSxZVb3TSxViFV698ydl7r8A8ysFJBJdRjr0jvOHrvSQnV7KMGyY0CO+XM2jUITyqxTKjGFusk0eh5npDyQ+Gj8470vyUFzxRv0Qg/1Q6fv3X9HNHUdlWYW/rD/T5hDvdovp9iCoYIyMJHNTEYtiQd8SBEagsgR2DnhdIJVFSkkDJweXwu7pBW8gBoNsxlTbR6PdgxmawzLR4z7ketUj7C+S0sH8zRb9d89qyNS4yd88UoYx2UDKOOT6YGNpQ9jXmuQUhahVTRhxGv1IgmQ8R9ea3Pbea/rY9X4pzSOtJXY41M7XeGwDiRdIuldlddkf3PEoEuOeANNvJ8RsI8JZo9NcH8W4PRWn6t1PjMDjQYQ5+/TQlRNQ8y+a31ES1n3CZ5sjjPCOzzeSOBdIbwvly+fUf+JzhxeFnMB5+Po9cpkZcLZBPaoWiqkNZZKIrCgccS8H4treseUcKELo0h0Eup1QBnbBLjDRhV7toNiywx2p1ThOjzYEYaxxSCXkzThuAyFRybtjATfFTa3ly6w/SdXNPN7+6Ea+rzhD7x804nNsez9cz72h2aAtXKsWj0blD1sle0GhKeLTJSFijnb3jzaiAdzkkpgOm2QwLzBFrusgqDuCelo4cMg8s0vQL5ARnZZWx2l5JHCu4Y58Bml5N4dng6ALjz0ZWPkab2JsdSJxLOiitcZA+ooWyaF1Wepc9cNjSGoUK6lHh+n7qYMrFfFkrp0pVoZwmR8RJhuC4FG/HyICn4xeeMUiMgtKceWC0ugZX1vGCRraOM1fcfuy4+//x7Nk8yR8kY3K7DQL1bJpp9HYy7+tjrekBDVnkAtaPTwwZBaH617ydg7+Z+6tHEKErFeywWyI3IlsKewYrsDKCafDaZMGKFoCmUtC2wa1HAca2bDMHhvhFMmLaATJAHZwDaSDxIVKql6RmXoNBRnnNoNFEqt9EEzuNM4t1kCNSw6xm6px1y/fTRCv02xvAjzID97s9TwSSXeOv5rR/6rMuvZAo0oip2S5e6YsASQof6q8VlsOS2b2peV3t1UML4ExPVyXMCR7UU2D9TR0u8wJDutEoZ8XGkTvD7B5mILI3MTWMHgW55oiVoYJwQyCE18QwQd23IT4eByuTvfdDUC21N+NADV/rVs4/ShTU7yYcorrkSXaKl++MIkMrDm15RbX42cdfJh1j3EYWOFCiiGa6k+7xXtFGHwWjPWG5vXWorho39+4oGlJAmjOrYS4cjgv1Y15xEiIxQDbieYBLvJ0SjhXFcMIoo9fDagmeTSKGRFrsKamEO4HXNfqRRzmQIDERk/qLWRuU8MTZrTQw3MhutaAxgj8beRICbUNO/uqcdlEBvCQGfbO1MbDOiLXgJjPAntfs0BaY+UIm+VSXzrqcUWg9Mus1BmjmUCWnSbRozBdJwlRjSl0xW+NGjDrMRZRIiagoqGXH+yBQIhyCbKpPnhRZBZuKKcBw6S/5nhwHvX6+FbmoJxssFvYY13LIYyknWG9+HqXYK/TqhoUgvXW5yCGBIeA6vIHFfQlE6hmYf8h8gXN2jZqRvFkO8wjvi+x9TprYpqZOhbspCuOL/WueZ80PtR5iiNUcYPRMdJJ7J+m0vQBKGqYY+w695HY2+OEOhmULbPIWU7oyCsQIcLxkJtTnTwhJuxd5UeMjeRycPJhRky+I5bU6KpC5nUxkC018UhqCT0J0cqNlBi7HnqKYww8KT8F5bh42lUU27Hzn9fqNdqLVN7RV3dmqGfvTnFa1+ooHMnuk/pcObAxLB75d4H7FDJJyeUVSwRxIGL0YvQUNigyjGMhIE/2cEg5uw+kDnpqIqq/ALAnacCCE/TA+WyZrDnEnuRWDYgYki2Ish+963ETzlXzMK1SWiyCM/Hotx6iGzVqKj58tS1WAmn7tVEtpbLI53ot6WiOF466pcpE0Da0mnfaIRAPTfax4TvujGScBs9K0vHkuZGHFjXNgPWSNAxyR5r8IADhtBeuZMpFKdB2cMbQ5vghsgNMykxhlD4COLzUsILmxYoVlr30UDZ22KTNQouDucKTIampqzNQpn2sckeBMhbLVuiIdgdUGtp6CpRZSxKMyo85Ip2etUJoOFE0PGOvTnqKZHLbOcHcx9P4N9KVLI3IAalyaTl3MzEbK2XqXnrmCIfFZOVvksr0xNQbSFtKspM98g9yvmvPxkTUF/UeKh6m0hPuNwaJ4VbvRy9nwD0/dcAmLSvdEIMnyVPc+wEQP4L/Mhql67hwUPRlsQNQwmA2KeGYvGRqZH79cxjH1v0bdRaFy0B530VWCVbpoNqahNsArQm41Y/np1Zhe3voCRmpdQgYYaQSOToxsrDL1h1G0lEsnY0qioQgKkaWf6UoJUCUkfSpFU2b/RRx7qTKZnjzLYxKEkanh6eCZSFw5q8GvV5DrRmoqxVVvanXtlRoReR3oJumxfPHkcfP3TUCz2O8+iKllNFQZidtP8jFCfROQ7MT8mA/dRZ1GfkUQynSKgaQxGgCSGyRDGPOQo00VaNUlhiG7bY7ZVaGlaIZHMIM96gyz7umxK0pPlKt/fSB0v7R2dsOd3NzxvdcllxURL1lxHQ2ZPlrNx2yCWe9t9mkJ5/uhZKX9oLaUI3OTFahip3IkmHk5XRhohuaG1YAgJv39nkpPktqEWkuH4iKlYtTrTLlky8Rgjry1+wpvBRBFt6Ru9SYGwUFT1mHPFQ5nG0oC4Mo9Lm1uVWK7v2Zn7mX4NaMJhH45leB+5zYSEetaKtgDJllItsPFSJJ4tmUGTNint03OFVd1mW14ZQhaqn0idKWCIF5JdjURym+OiZpPSpsxnakE7VAXXb/ICZdhCVNK4bF9m+g6Mz2MPAL8mLKZRxKHBXowpPM6ZgsNPlEiRBThzGbkLLDyDoMl7Y7Zgtw+gTC41DNrHXFMAIWciSZ2XDFiTjynh0/UZEeRpB9Io99wfhHCxB7Rq4Qyu5PrpnmlrVJdyCAclA/BOuyR5mlxXhL2rT5gIGzKe8q++L6rabXZsYwgTbUK3988rslP4wJBIkmiD86+K0DC5F7WFSczAinBozMCAD3hgHMJxTwekEs6Ed/CJZkg/c7pk50dxnWQ6OIjevwwmksjQVHpwHQ12ZdNCCmRwmy5wwmTuvlUgtaE87FH4ecuNPdschAuU4HHNEpIQbzI+E14g+Rimg3BiMwhJQtSpJcbPZPp5YPBu2hKkNOomxKLMAmPY3MskoD7P0CLV1VPXJ+GvGVzFCzURWSMaXKdya1X7V+p8MoQKYbmWRmnbhSX10GHWUTKD2M/BEIwxDKbA8Im4n95DkWLECT3/OZyL6e1xO7QVaDAZnS4YrZ4Eu2mdZR4bVS44uWo+VimOwh5pCM46HD1omPaGvt/XZghBv+K2p17oej6DYK+d02saf7xgyD1dK+6eVP2Vl9ttHVsw2U9gCtK5JJzUCEDweHC60KUEyZrvwYmBeQyjjyokFi+4wwyZj/H4kmyPQrHFE4BpsCOUZFkHWjWAzLkkcQ6JYG/BY+uJ9k+VO2PcuehHezdz76yfswVJ97nwqfO0D7eqrpS+DA4ijIDROTMbHlj+TMtR5Co+vJWyW3VhazpkcTckYXZIYGQFTcjmJklbIIh2sFf2jw5p6oEkXn2aTA3go0z8V5ZjqPj1UDXoaO/AUaBIm9i4JzyvJh3yspVSoIpM4CHsBv5TKtT5MFkPQSumgSl4riEYxQ/mOZcRzSxKheR8WJjMWcU65wrH6eQ2ITG3va+3Zh8AM9Y0JJl0TwRC629CwC3HUieAu+r0jfDQje3Sn3jFZlFvvqWvnN9PGtdv6YP//BVMgS+wcTC62ULxXeLWnCmK28vhHhSb1BO7ehzsOJiBscZ9o9RgeEBEgJmrw4/wjMnMj5IcjVInIZ3iETMC7hIRVYZAg5gyqhU2vWZalIrGWRnQkqVmyDssn5LjpsFIa+TwNelDvHR2LxxG76wdDZe/0ABUuzrFZ5Kb33MMaeYBSl7CtQpDEK+y4RCABHwJBQqckn7pg/UW2GVzWMoZBr9b3dGNc4Qi1ZCqP4XwhxCgKnxjCFtwglJJ/L2KtC4URmJ4RIAIrRWaq10l7rejGuCQ1hVNYCCcu7S+YT0zzfg57BKeiLLulZh2BnE2hqBMMSGTJsGqh19WF1ymHmPhEGD9sjM4oM5cI68fca5ZX3UNZkRWFJIVrHP4MWZxmPx2X9tRGXIDT9ACFXL09bNHzWTUlaD9q5gCesAsh+4T7WofFnjWfJNIEra9fiYh40ahZsGcOCbJRnZVZw6vMCwy4ZdfO5iP+nVHydDTKPpeJNHkXmvtJQvvHDfGh/sU304j4pAqBrw0/M0jFqAZ3q/NfoFtti5a3xRZRqvGZ59lLWojf56iGcRRdwf4LZGV4fWh0MckB1bWRlhLTPeMudzV8Nzc90MHT20xCae5nardq/1MO0OBj/penRyyDlH4raBMrphpZb92+CW+ImqDj4ykkUZbEkVCA3NzUZ+RjkmzXxSmvG4DYgRc26aXkUCviu0XhiCeXzlnW7RGbBqFMTRtFfskGBY0Bq3/4JpNoxmzjdJgunJdYQR3SPVqJ+nM55bHbBL5nWu5//qXuc1JQ/bG0riCnoVfzxQEO3aV2wrDw3gkee97XzPpWRQJnnOxTiU8pTBdXOU0oPh35HYfqbcz5F8NYr7xbSOCaXgNByopkreFxjkuAA5TJmYrB7sVY+c4NNGY1pnWxUdt6IGH04/8xgW8R23tjRqQjkWl/IQTDHJAptNV1yZrJrYW9S4bxFqEpVKT4gd15XTfE1AWAXKJs6dEHZUDV/WjOwyicl2xI+xpCHlx/SohSaTF1rMhkr7vO9MaM6Oo2QtE81BakDEo8K6jBobf0zvOGt/NBHwK7RWFV2okq1YBt+EUQkQQiZ6tRFOsiaRYwTpRqwjDqRiS6X1FhsTrNa2JzvXKbrPaFBtaAvvXmcbGeh4O259vSuz63g031NX1M8aG77hH7lA8xgUbr/slcQqL+lvHmw4HQDxmQOsdcNFGJP7ps1XaGtASN0/QzJA+DI9311Hk1En3R0qlvpmxgRlfoMRBcq5EuuuR6pnYq1+jrpN+g0rmsoETc+2YNhmUxANM8O+R259S0U8AFdN4tc4YExKJMl5m0IyCR96n9+NyNujBEh2WHEOD4VnBoZhX8QQiQxDmGblruIZdgrsCFa2a9JvaHOeXhJF1poXhhR0jmEKkDt84aPkWwEk/gPHG3kU5WGFC6bXl2XytTx+IEQkPo2IfozE64xFgKCZhA1xk2VQPHgHN3Fsf1cmDBng6euRCUouXiH4tbkSrEw+QZnOVM5RTyOh0dTRhcjy9ibWdD4flrBpF0SbVUWMINFOJemMdV5u/w1Bd2xat0VsRbps2osz98gkBjkxDrsnUOZ258D03tZln4iwlyE/SFIbAINImJ9owcmYYHVePThrxzT5ucGN9Pr9jyUv4UltmTWKuWfSDOd4R/HNR3Jr+15o6gzi/Se0eNETCJ9k/03MJqX1LUA8uz0yoEvST1+jYBpzF8RecuF/6o40MgRIXf1cvLc5DMjNLkwADtJdHtFQLr4hNGH0ffuiyeTmGvnb52dYBKeLqHeNrCf8SphpQRzKEzohXsAT6QS0meZV+/wls1qX+5oYtMIwb8c3pgHYnSVOhhuBMBEhN4F0Zowk18DbyspzhSRTBgVoNT9Jj6DokTCoMAZH91eAjDKHfX3+ZPBtuqXhMCkPUmK1lGVt9PuGTOH8XtcFlzNxEU8l2hQgqg1DCvExagyvL5/rxvXskBaWZRr7zHr7rJkI/rukp2H48eE2lsa41eIWoU3gHTX3fR/nB+IuEqPF8SswF95bqTUa0nAATppB/8YDEA7kJDxYsI5KQ55YBfKUZJktmzVR9oT4jh311t5SxaV/KBkG/KL2iT11LKH7/fOYPt+4WmAMdh9IvE5gtJdjAti/vasicSGSlDoBK7/TiGcR6RYT7GLfO895h6VKRYt9b6r9cV5gZX7RyEb6TIfjcyn4p2X/Hs6hleSS4VzAcuaKyVgeFDoczQ6t1GMzP44A8Lb12VGOD1qvBaDlkejphofwHg0Q1TkBcuGk7yr3b5zRB0eEyvpr79d7LswI7oDMknw8EpjJgT5PoWWCfxdz4mAhtrfTgDbLe3LK3lu7K99nukLqn93Q7lH8m3V9J+vyfahAb7+hk5BNdvTN4jQFRw+d7QAfE41VaRK94vbuhsYBG2zjuZh4RqcEY5Pn0kLEas6q65VzJe3EIqfK61kA/bINEiCcReiCcAKmkBpHxvRp8pxtfNABKPulyizrJ9r1WjNKDo1IRd0NiXTsLsUekOmPr//heGua1lKJ2hddpTYjNd1ZM33Mq2fWKd+dDtvquXlWV8aZWvWrqYTQOng9zZRCevB/31PKc/b57pT9xfW4pI0atau3DCLiMaGXkBXPJnUnyRmlNh5D8J75jPIFlVZV4kExBoZBksbuQWY81KmnE05lgwmB1rPlr03y7Vp2avwp58LAiBGi3wv02fkrajkx8eJJQnNrfuom5fqtfKjFOVGFC7C+kqqo19e9vaHsCinb5lmNSbujPEy6QmsTO+v0spjCXPpURzKUvl4oyt0uTqHPjIuKSz8vgMUi2tolEgl8RO04Q01TkY4wxQIJ6xakqe0TsAKRAKq0wynkqgcr4V6UOOQbWoG43FobWpQn8r3P7ZKYbu4Zja+s1auhC7nfo7DQYZkr3olWTqd7SyGR1HFl3AcbfFXGXl9WHrTd9sVqXScqBFw1hJSWxA6ER1lFRKmYq9U82eHilpKsMgbEDLV9PGr4zUA1p3qkNYF5jCqBluLAsKUOYn4ipKMq8i1cREuWlZpB2ODlPRUMQSnuNMcjPKX5tH1EIMa3IU20LU+C8ixaRNhA/V9bfiDsKZWFSZ2BLS9hZr+nvpTVHkGZXudqunZ88zsJ89PO9ezaZ4WBmSf96JomflvtIjLk1a+bGJOZGCU3CN0up9jWS1qVEeZIDQe8ia57eYlkuVvkMRrWsCWs3aHFwZQzUAPx3HBrTdnTyO53vSccyn49qBZFFdF9aaKURwQ/He/ekMGyUw2ckE0qmwHSZFC21rxG3cQFBqdg3As4SWlKNZtIH7gn7ne9YpGjRTf3eEY86HxBGU9RuzicFh9XjJkyoM+9PtwaNiN++wSNJtH1ZJabmcFiO3FMkhLgvYg5GRcE0RBI60voGyhAaUwhZhy7S49lM9ljgDNVWJwS/zNtsXYtgGM8gXOywmNkELD2NEfgPZh5AFUe1K+Q5jpgaf7MpCS6wwU38MhGiUoPneHR9lun1wqKXwXW0qQSu79HXiAC8de+1tq0ST941ESSzG6rdHP4NuY90U6IkYoE0gqIPgp9qI6EKLQyCG0iAXLM2bQKh/E6gZeAnW0BLj40DAZXZtY+zpPBW1d0Dtj7AMe2zD3hwiFcQ+82D15hCutA16Mdz83mZwVpgfjyzajQhrQsjKdDFhCgq4+92gdV7JgumAUEeCITDRaZJ3NsY7PJaokg97w2vFeZVk6Sqeo14EaRjprfPyFjaN1MfE5n3hqDRXht7iPNbCbIzudleVDxfH8sYDkKgewdTpP/6eewdJuljCnZPaWEMwQQZ+4y2A/vW6pVDSKLTg6WxcJpjz/+ARq11ImKiJZGcuKU27UA1AJRVSFtCprdPTVvHN7EjbAowjRCu7iNkRGk1EAF/JzK/Xk4fwymEhAwEz88sUnrQWcKq+kXwpUBxK5uV/FqEmrLw9af2/XIDjLh67uQMX+MJ/bF7++8aQyjXNZq2J4D+WJzCxOtjflnTFNhZTYc74dxwt4sQefFj60FRJXcLajFrV+yAEYeUCcol84PGILfZYlO70YJCQ6WIZ1AKMiYXGpA/vxsJ1ZMp928LNLpFSNhTlafSjUqExjjhZovUH65lMUjqbJGvkQk1AgDpVdWghCQeaiATxiASZ7oRb7b5vN8WuiACSMFORzBWsQ3RDx73VBtKO2TXFmLrK2FKOV9M7ghHg8PTY9qnuN8vb6y/HIw6Sr2a7EJCiHOhzIhCFLUJLe4EBpHfc58ZIaXLeIMV46gKVNuldY6P3kBBCSr1uexoFPBoS487NrPnuVAFMqSVAOWdY26jlnfSAmZCzjWpQspYWP3Zlm37q3WbaQn/1nYDQyjXfg4K/bymMOWc7MvIGzRbp8hDQ4xXX4ADwNtcUgiJNSXcDheAGIxMrVpYR9Rgqq7Od4SIJAwxvGScgNqFTscbRsrArBwznm1Jvi6mheDalXoPTw/ep/WiZ3PdNN/RX5mrvL5LQPyzS48cp/a9Q0ZCTNMNsdUjyMcjsEkJAvuhXcm6zLqP1mNmXECm5y4TcItaHRK3E6E06JLQy9ylC+SaIcyrUreOrt4t9oxkLKN4U8Cilpb9MUqU+l5ETIVpEKEqDImayo/PHaCnmPCqqWcU8IQZEKNPZwfpOxZiKKzDYSSdKTJdiVxTppsVDNnRrb0sn8OrMGtvoP7JwYoLwaA+EAeMSeyDXVMXgtPWp2vS/aPuEHNDO2xoSdc4Sv+6C8LTZ36mLz+v3eeSugVTpMqHRSJxHu7/a0pAkV25noS6M+VuFlFhqchUd4fkngFT0BxMmgjCjhelOKCSD/sRmyrr7BoRd8YwXApdVbVnElbygycwEmo6Lyb15fiYWAwph5kdlO8HhLXKPFmYxfCuySmaro5AJPosSsWCHdf3Dcaa2otDbYcrh+dwnTEUTYgEaEMz03xOlA7b0HZTHasmSkY3CzxMQimuntxHHpuBGJeuLaygosmh1FgAHRfrGZiGQE2BwZHHNxEuJD28RwTj/mQcjShaTrBXy1MViR2T47ZSsHFfE+QEvtQ5GylhpGDWDgEqcN/WdSqAZAptfEyPvjIuhadRztcESJ373JNcixmW3zqR9BsxSn0dN6GYw+5jU7Ccbc9+71TC727VG++adeMWqKmfp1+W+0iDh4TIZkBKpo4gN5GF21w8oXpIa+HZHmUSp7emoKKJwZoUrtqF39OYg0rR7vsdScTSR5rMgYXmHSKQPEJ80MrzA9BRRj1T+mYSsEk/e5+TbuOQ5hJ0FXni3iNw3YD9dIM1Bql9KFKZKnUbGkOHlKQfyRB213/OELIK3TU9n+uQtiBIoi7Mkgiu369ChzLh3Y62OS4pwyV7aSG+3U1V5nZkCBjCUQSqSS0DS2Rn0dHsrxP/12GgTXqv3nwyZ1MmN7IGpLYgEE9hCF0TxA+tTYEnlktWa2TnyAmzxV2ER6EZnJ0h0xDOPk5g1tToXFgDvnZTmpixvjcxhEMn6Hr9xuPL9Ve6k9c0pjYj9P9muOo++IgFuhOTtb+5meumSuK4es5M/R4Sa76MzMFST+iFCv3oIykJmzratZoedLfqg7jtOQZvkA/eSEOwe+uE+xwDhyKRXCW84fqKtB8p9YSnh29k5t+5pjrOJOasikUNCu/k5loxwhywSJBCnAtx0DkWJjqhKfMGIQC/j+sHAdmW9CeFRLIr2oFtxjP6DU8xc1bwdUBB+L1BwJPMC/S4Y8JtWvzAulEkx7PNSilYCAHxa9MgCCvatVYQSqJ7LW3F8vq2XL6/eKEoqwZ4+OOrFHHCXn2NErT+HBUKyjg3pOgpDDMRoPLacnMZy6YLamveDWcKcYEzBNQ9z9xjfF/eMNaeBZ6KRuhfTWhNEX76eO9sh4lQsmISQrt2J2Dy1YrhtrimrXb9UH6q3R+nwM4kB5eR+uRJrvYZrEHtokgz4xGDwAUm64/yxzLgTVxDS19gD+BnabQS9710w9NxDYmdJQF78FQYaqVkILFvuy8PKj8346HlDaolC+O/SB9Bd8WBy6ohtCVA64ySGzSnT6RglFnUPvphYi0BnRclHly38spKGWK6RWtrG3KdFqJ1/6qKf6UVWIaaoWg/R00fIlK5eahZapS2pkUNY/AlUqdH9GyDWOpga1wCGQLhRT6ewkQTEHwNrF9G8BEcGUkSQWRNO/j+ulz++hbXf/2yXIwpUAAxQcm0CHfD5tFjcaimre7NZeEHYh9QBlwYQpeeu11w9q42dfY/Froyd1NnCqh7/o4a26uzrQJO12In2m55mdRJF1G21D/O+zcEgi2tYkuSyffPCHvnVu229C7YuuYOpra1bj89ToFEPt3Z5J1U63xz0jgmvtH+MxbYNnRme+QL1EUTLqNO0EwaMnzRLrJskykpc/0kNXNuYmFaeiidmvPGzlzwGathMXkeNBEeNstVv1KtFct14+BbI+imKTwOKEdhp9yw69NVJPi+ICmBKmPALzq3OT5qCnLPnkSSdHP9XYcNcpxlLrU/k2d3tV/XYqtxbflD1W4SeE/uZ8TGUq+8Z7W4Wv9BYB8yEhMIXJub2BXWE1BKZ9biQmL30oNs/fAUDSMtxfLlebhA2w63++xzKyVrGoARzaen5fIUyQrDy20IFv50CbT0Ws/Yg/uw1/CSGtK3Dq9/1qVl2UdbNGcisPmSkdkwWM8KBxljsHlwiLY+g7mTBl8Y/Q4tHq7neU/bJ0pj+iVjxPsMxlpnIL+izcnAYND3tMu/SVMY/dogMtY6oRWtIOmyS8zKDMwFTw4S6gw7MbaPaBhk9TBZH8vYGZuMdQFApF0KB2OaYfdT6YIdjLgH1mhwQp7VoszH/H0MlYbDHK9I/DkP+268hQYpoVOJbeM+1wZSa5BDm1JkG19/lNwyfml5VG5tXQHcfEaDgrJvewS5QwANEnNCb7+iYtkDcHnNPWW/aGAigx7hjhsSe4WA1t1Q4yztBRONgkSXhnymn/AiOG/xnXkjsQ4zj5Gkcc+ynKldisZHrdeZEqHMj3UfdG4Ja6UWIJskxyUQYiGoOj6VqifzpGehlOddRoyPMWCrGWH11j1QLyKds6/5HCIAs3WQ/mxRQf2478U2L8sW4RXB9+a2CTF9pt1672Qe7oSZ7oePMo2vLPSWaqdJ8FJqx08S0dyUqs5hYP6f5XexjxDBbF5FdGfl+1xTkCRk7DDd8OgvznE0BjYCl6RvJ6QzRpWqGCJsJdhEzhhIXAgNdKiMP6eSOKQ6P8wm3RoDHFL2UCZ2NioYcnRpdjBu9UDQe2su+EztfJPo0SFBkTBnm3P6yOGFs4bIWmpufYYSYc+VgxQKltgv8WrUOaCUiriCKEdKiVtqU2/MVXjNtLiE1BLG2rOUa0bzf8CjyL6z/Wp1l1sENPdR7GfCk/jOxuEGaNRfduY/0mPctDZyzjQ+ITWoziz6erKf/HuDMdBNPLP1okRnaJjhqOEQ2IOhAag211JwDGay0Y/kXnqmN8ad6MCNJPbA525oELfg/tnFe5lJg2BXtyvU196VAjWg6Zos/yfCR5BMHP/MEn+SQmFgDSlFOSHXWswd0klRYmzQOIx867g+3tWILjDWrLgmft4FruqTpn0tCfRM0GJtiI8hCfZNoZuVhXlygwiGudo0TXIoqjk1ruRANyxKPaBT4bYc8DYRXYOYifgde70qdWxIbDuC3O6z9vqc/VTIOCBGCyh0WxBLWlq23HSVbs4R3HPHlrl2S1sATJW1PHRuUIQ+4RuFHLqdAnVBPJKZzykQD+5lJlT77vV9Oby+o0IeapZrTRFqeLPWIb0iWFFI4UQ2hsC5VUElNeLGGPo5gT3PYWCrD+5BaE1zIMPPecRZ/3CYYKxREmoROLqQUDs9xp7qhp7pcgiWNQNo2tH08Tsq9b0MIe/bYEQbXdltSg9/KlMwDdwIs+GbzJfjXh6aAoLzIy5jJOwrjsaDtFbNeCCDTpmEb/YHyYOf0oNJSLo5W0Wt9r7Yd0LoaCRGmmjHagXP9Vc4IWmSHH9uEMrctv2AF+mzbUz/pzEVty4M/qdMaUJXBhGU+A2sVS7E1EaAUqSsya3+//e0WzbloOyzQZZHjWlq0IcUkXGplBj8MsxchYjlOsr8dUbasXjJ4pl+9tir6Q3k8TLMudUCINWbjdBRT7RGxwrTGMwTiZlTjRm8vi3nb9/juU9Pw2Ua2lCGSBatu3/W5n0WgFfmeGNZOjGmNM6FIoFHOm8fg49VbI3ISBBnnG7ciNAGPShuu8IMot6zMMO1VPTjRHWzrVSHieD3Ay/aZAiHu87ZNJX8z2AKRwuRt4V6floONIIZ4fSgH4Sga6epLaQXTvPFHaO4bYV874SNoZT6S1c+CUbyPWcbiK6tcl0ZPZjCB8tHXpblcWxMP16oaeupLJQJ2D8ylZkYPFO7kwfowavS4+0wTXv2BBabH365nloNmUreLtIfMHMjgkywV/zJV33B/7oNZ2tIZZ7uOEDaXyXk4iAQTIy5hDrkAan+WFNgKJNZVflimU/H7zXfP/sTxCzsTfY3ck2RcOn+sV9dS0CeL0jL3k+FJA3aNC+lC/aiebbZ+TNC+vWLu6tyviPgsy7GyiFANXEVQJQJXNXO+uecv2YnuvQU3wF9xbvgxEG0gYzj3Zw0JP13rhdshRlzojYQrc0+0nDPYZe9duXiKo1cv/9HGMJPuf9zz7hdU3iPw3M0b4m3t+X8j38NjwcWZunSEPzPM1pYv6s7s0rw2LiDZFLiGP/G+ghUo5OgtLVpImEbkRrJImVGaUrUnc6MrjXGYdAWxZJ1snY2TZF04rPweKEEOYSouzZ0TpZAUVPpDi/2+YmCMMvJ4A4jHpokMOIqVGrWOItR5H6nP5zrmzp/Y/N3EmqTz/gcMgYfIxKusR8KP4KIp/DAaOfcQ9iDKGrjDRX4hteRzDtrertXEZLfnaQ/WaUPr3FXVDAE5gXS97snDqFJODYwdQc1hC/P6bET2kkTWrp2y3XsMFBZhk5JNxa5M466EAP29TgROGJYhTjfT4ir8ABVYRbuFBC2EnqNHR6g93gMiAbXDaeT3A4WZ+Jz8AlNtp/Zy8a2vFGG3bx29Z6dvnb6NW07470TNvoEU0CxD2MKr2/L8o9/hsTC6kmuWR/XUkpPOcBNp9KdbtDZPxpLJMFcehTR+D0jxEUaV2lQcOXUYqh5QDK274xQUjpeKuHwQ63V2PJ92wa/2Mtt7P4FfONTOr91VTbftD7X+otj55I4ji6NqzQYyPrJw0gp1q6xQ1uIcX19gRDWHVmr2V27uOWkrYyRECEokLA4k42L42C8il2Imr9FYyvTKJ9n0rhWq5hGVECqDqvaNRZ97DXEI/PsxbyCMj20PeexaTeHMTqrMMjKeDmXQ+gJA/XjcvjyBXm3oJloqU70bTqjjfGtNXdZn9RAr1hnJ3yhzhFsCpYuRpiVlxNl3AWyyV5EG1ouD5DLTDO3vSeR4/47vZPElbi/u6zvHp1YdfxeVWN9/5Zwpu/e3PeNZvH6cj/+N4V+x2+rolM/BT56hz3hy9NyeH1yVzrDNh1/74SRUrvUIghPub652qZ0mhqqtoe9d8qWNQIimGwQA7qFqkSknYdqm1PUFrt5OZj07N5IrASn3hCZuIzeK9SGMICtttp0eigFxrF2a6EX1XzwTE8mt4ohqJpaSrrWHBpCZKw7DWid7RkqBkbvvp92LQnWtcPTtUX8T4WCmxnDeB7Td6wy5S4tZbMKLOJBFulMFA4bDOUCqdO7h1iGjFiWuQ8NAUTNpsIT34EhkAA6GhKBa1FbfHg/uf2MLqeskW3XmIumqMxpjH48Lofnp+XybJUI48BbShjWF1mtvYwtq6mppsvf9ZaVsiAfzghTcjDVFlTDh0Zg5XCJAtFG5bAYaqRQS6BNbwmty5frwxit2MKYWBDQdWgME6zyHtp+2GBsKuhcuz8XTF5+lTHwWva5CVCqeW8JwHzGij/8KpdUC71/fFjOzw/LyVRXYxDG1W1BHz6C8PuhEcIBz6ARYNIf2jYaiTeJlkkVqr4rjJSagkxKUVT6xDXcmM+ipFeYDCQPXlqk+pELabxjonFsGbl5fZ7NAfeEgX3Wdw5hg+nl3yNmZNSc3mgpTcPzBU4DB5e++KwgMBk1zPmQKdzd7JvMRRjgVMOYbOx60Xim2qualhPJ11jalcFdmghPnqPaqszPkPS6Bitj8j0UzNQN0EzfoLYNMiB/v2VKjbTlDjGdmqaAFNr0nPJ30fsOWsiF8Q2qKRdCPiEwClt2qHe1GDrlOgfL7Q1rkhCSBaB+0KuLmgKYghuWR8bX6LK4k7s7q9gndQ21MJGnse9eXKIZXW0H8f5bbc4b7ycdu/GW/YN63/WFMTR6dCNzuM8l1Xyqn04eXem54V/gnuoLannihdiwf74ZhADkQZJ6BnYoeIF4FKRngdQiGMQcBCs7KDDApjbWpIhSpaqVJ1TsWd+h0iekxlDSK4QR+2J98FY+4AknaIToXlWrrQVSxjCkxXj8zuYGA3ZiZgnKFAdQYqbG2Ozbhkq+klhk3vVwTiUeed5Us5iMPaWs4cmSq8DxeaQ9oJeunbR3pCBDYkKmIQS11BVgZl9ox8VVVeYwM6bScM/COqyjYI3R114G1qKYySmkuH16LAGr73sq50Xmz20hek4mcznjDToPZc0m0vBaRB37RebBGQO9jZBFOAsyZRoaaub9kUNT0CzK/C60Rp/IdXdy7WRsvV22qqrttMMdkBHQiHL99Jk3cJSuXehnE/L1C4LXApeNfCWAHDxvCTi8YJ5lQISQ3DiMaEsSXyfqTfrV0oiAhOKrtkFWOLI/JBa9e8ekdtFVW6l7kH1qgTOdqAfGVQmLbPxkKH0eVn+zv7JRDpDweci6hNelYZ033cz9mr2dkYzOiOWHV7VKQqgpSvD8rBM8gJVJawwhfxeClHOgXQGhTbiiTNYVraTZF8rcDUeCEFy0Rrg+F8yZjMQJtBiHVUuQ4SXxUniJfWJ0sgSkZVF6wkd2pjRiGULH5Qlz5XCqGWJxRaZ0h1bepnVFoHUOyNm6tjBrRauY4e4UZjbonBIohZJMY+DfrDeRMPDIF+VQkAQYDtliGNKDP0gRHjIGE0YJTei48+/VBpoKCFdbn8cetzVtk/M42e67TbV3/Wzr2l/CFGhotJaLwEMMP2P9jnQsPZRQg4C1CFaHWJ8dMEYU3jBCpCc4R1oE0LLR7R+iRqtGIIW3pQ+DfrUNrI1EMZOwTYLkdAxyiKr/92xym6agUpxuzpyGRvCWyeciRYRU1qV7eT6gOj+AXp2LKvS6BvWABlk7WESScnDL5K3fudNSUdogWl06LARnJj1xX/jaIwWGziXvEcEiHQ4042tqwTNpXDQH88cHYWZNjUxxTdiIpUOZVRg20+yr1yfHc90dFVKx5wHT8XSi16At7bPAR6NGeWe4yl1uoU6r1ZkSvWnFxtyvgJmZGhzp91l+c+Snwln+GEx7dFe8CV3YNM2sa9wibK3OzYTZH5Z/Y1PmuvPiroXfSvDvYAw3M4XIUWKqH/yKk3jJRoQ75yBKgI8cr5aYBRbQccJvMFGjJIWINntEquTeq0bAecAsHQaT1FFFRRZMlexW91+ZTNeI1GcU75SDEJK+Eg+RUrcksyRGrVjHFF45XP89EaiuGc20n+Elln75RYtpw3QpDh9LgNEmZFeauJJeZQxD4GjfzA85qsal/aPf5sbbB0A4ImRwP/k4DutcXObWSo1Jg6c4p/l8fMYMqIqPMwU8Dciaz0jtSCJg+HnjuxxKPThjMIt1aN0xFyvatUVUhOHV1Bac67Yuq0dU5leho1y0BiXNtemiSbsd0SAx9MNgaC9KNUuHzWBEvIK0hu7B9EjqTIfnHXtvFIu74iBx2YpLkLno0/IZRqL7dYsZdWag81/WYYPZ/wqmcH4M17fDa/gTu0TpaW6VUyM5mG9kbECvnGQJr3hAdPNvEBRKNzQMFklaBtnVVJ0Q8foo95QJmnjE9AO66me4M+ZeYYU3SrZ8NxmBX8d0Gbxpr4lKvnnJNYrKH80LiZuv7B+RsFxTmEhSytQIc/g1ZoMgg5zACNPGsXWtaVsrWDMGfVyvGw6YRqBFXZN0m3YNkcS9vZ+Mwd1CEUmP5yRBpd1GBJJUlKwPXp41+pP9Vxfmrjk2RY7xC7HHYmLDE85cT6F1bGhEmaKjc3TVhLptITe07FMlKBxcWabZulDLnBDSmQCWZ4YBsCZ0ImcV09nT1mDNjdCSII+Bf7SZkDH4c8TZIgk8+rjFEA7tsz6UTi9m479NQhrP79dP+zZxpuCtymgp6Ex6c2v2nNuZwrPlpn9bji9vy8Hcx7w62ZCOh5o3AnHc4wAExP15KNEZpjjLKqnSaXK6w3ayNyEEvgEcoqIKqURQmMFkU46cS7axgCPTiFdf2BAsSQaYZwnEh2khytj2ICS9knaFz++1aQMhGQb6oeH54y3twinqSo/3HsILxuEMSq52D5mI3TiSza0I7KwPRaLM17TxDg3M/09bxuajNbhRjK8bDCXOIveJeLWoYFHSKBAGXEu9DOSrSNJ4r2uMSNWd0NeKuAD2IPFzQzQJJqA6i5BWjds9cmRECYd2qVF6oowhNTJlGo03rJj3T9iUqgmrRMsEAc784L7rQW/vcSYTIgQN4fNYsyK7pWeN2qQ8+25p/rLud75r+bFGDUYFwa596GelWxM4LvsWA819fke7mSl8GFOwRfr2GsFrFp/AKmJSatA3sqePgISXmuogdAcWIu8tN+ys0MxkYFDPM82wZV60rKpSVEfd3ErrsRWJR4JAtusLYcDfnvqiD8Aea1G0umBgcjW9hUipRehrAWScEjmcu5LzeMzO53qAIKG5HYd++81+ZD73dI+MDoTGp/EK+HzK6Hc71GwE40PcRuK1efN4N+EFJimcpDZxR4AkoGAIxTlA+gqnh3SGSHVdanqrPYPD5b7gOmmsRHC4sSfRHd/ypn2b67d9BC+3gFkj0C7hJrsAhZ+CkYjXzgRuy71DLbgIMMoEmjarmoR+zt9XRGtWq7gy2fp+0GvNLZbwcNTMZoTywUJAnBkyuzKPFTMPHDahMinUe52IXyZ7uMh2+pzrAt52k4nKPdPRiTZXXSDl5knbTM1uvEJCfjpT+AJj2L++RZCJaQuYwMzlPtMWuLF9c4/DkykVuFErFxgDo+o/TQsNIpCbJ9ISaKW1dAVsMAqDvDiZ/nQhIimdqtS825qWo+m0eVj4YP3JDm1h4ZQ0bypwc9kluCI7jD6wwAy1IJ8z0XJSU3io1xjWboFInhNppF+4/5RcUeF5+FZq9kTTXBFpujtXxlGvA0zhBXZs3Eq4+A5hCpcmsWqZ0zHJMr8hIJWkZHxvEnUIHGavM6YgBLrEiKDPqXGLxjDVgnV6031zEOeiJRTi3RnC3prOmP6tGoS8o7/X0WIwNOSoyrPk55LJ/0wrx/cq5BUBDP2bEffPtIPSrIlGvzdf5b2yH6ZT1jfTDDraY2hjT9wkSN6vKRyW5elxWf7vfw6cj635C0ceEmMOXFj85GGwtSLXh2pdS2ySsMIYjUplni21HC45YIqnuRvgxxyTRH9HTJIwBqYWYKNxmsYtnWz9ZLbQftlMu/kJKueUMbTDSZin7pr1wXCmDW8xRymA59Jwm2tJO5FjRyPoLb9rY795HNJ/lWDLRRPps+x9ujoKoU+mLNfktdXtmEzNq39Rmvd3yO+pJYgEJppoaCDS9YQjRVvBdXkOkDXUNQTUZs4solZ3gcKiM2ItrMP7IbBgrThJodsosVVmoP1rBDSXcGM/r5aXc9HWqKzXZMNvftf6YTYrBgQCOgswEQIl3FuDUQ+7jzdWY8sob+ew28ygS9uHK8JKjvNWBrjRbr1Vz0m3j/hz9vs81cZ/XFNYlsuXx+Xy8pKpapNgwNNFqyaZ73UQe0k3wNKa3kvzXKEOSP9ujI+DUSMU4Zrk0oMYJL7I3CceoQzPGHoyFAnZrjaIh1kvB5TkXicuOJtPuTCJHsC1UmqkTx1aAPMac9QOmkqCq8cebvfQUU2FnVTGoL9jbjOamZ4s1LpsTmc1AZa2ppvagVCIq+fmiq1FD+yuyt7w1WQKwzspM+Cy3Cuvp2aRNpNRIjM1PVyTNb0h2ChBDvyQ6U8kFQP7cpA00o6XW60EYwQhKPn5cMiqwlCuRTB5HBmKx5S0vcl3UtPIM1S1hDLvJW33xvzi3A2tFWBwrivXeQZlzJdqMFpl2sKw7H92Bq2AFrWDst8m2nWHhH1tDea+odTMHmM47Fz/Q23vGZ0J9XON+0s/u3AlwvrP9z5alsvXp+FDbDVjZ2oxF9idALhAyKL6yJJ7CGPnpmh0hZqFqrnpG7+aKx54ekKNpGdUM1npKptUl7owvbEQPDdaO5MySTncZ8tm09ahrywBWg2RmV5cmUU5nHMVstZzvqY1yMHssEe9A9CGEHZ6bWgFrw61cLy8XmoTXzy+oS5LXasdzrDFV8YA27Pa5/pOEPpgvoiJ6WOBB8ushRFe1ppMQjWhBtWkYwCFBSYcbDUSHKJCrA7ze2UBHXsPMoX6sJgJwD34oOnad0hZz3oNud8TZorJHFARibnM38y+Vbo63+tViVDC2TzK7iGWjEBPbT8QhrHP4GFkezOFy4lbuldLpKW6CZW5jjf067Llhnq7pD3mT57JPm1p8Zcbz4Se64IOtDUrGtfG+fnhOIXHi+c9enh+io1sUJI1BJBExCE8gSS9dEijWFjmhYGRLLHoVOH7RpxILV0yLQYXEFfFWFVyZHOJjcFX8O4o0rtIrp0L5/cqmZcOlnuYwM983oPZCNQW3E/gijnhVE2g2xeuti2GkAPo8IL0o7j/jUjfopHwszTgtrnadLOVdUzhYGdsRfpZrgdHMaaEl5Ow+n6lFw4ebPvPCY8INtzTFB4yHkP2lPjFE+Ycayn9LfasrsmgX7Y/TGuArc5zH6WwBQ0O6afdpueBnce1EEQhqwgqs/ncOltbN8yWBMxwizFsrWcneCkPqWZd59BdTpGZ2Ic5K9nJegrUECj4+BqPqm5JjzeJ7g3Ch3++3Hbt6rMZc1IkY3ufl4vUs0r3WyFZ8KzsRZx+ClOw3FuPp2V5fg6ibgm5WCFJVPAouUfPCFaYNDgJ2KAH7tBjiEW97QWQlggj3SRsKMbbpfTJM2QxWBfAGINrC32huDAbjCEjRHtLWiupIggfmYaVUgukmqItjFTA28MVSXbWdO66NLJ1W2echOWQwiSzc9LonOq1lB4t0lVnAByfSk06fcKUbz2ofczst8NDgalbVCvvJXMez6saroNnCe0lV8ix5nImSmQa5pD6gzHoPIpWsZpv+aluqLQTZN0BmU9xnogzZ4JWjK94unF8O4RraJ7TyW5fDSITR0wNujgHCEadr/vsNe3soN9xfBszSYjyEhHPtC8yiR7PssfX2F92rjrsyXBxfd8WV7B2CwGlELp/yU3X3b3fOdfNO6kIfBMh7CqjuTfNhc25SfmmISA5nmsMlFQ0AVjxFxfXz0wmJ4NJKKXh1Z2g7bWOR0qfV+ojN3WRVDpDUKxcGMMMvlKtgX0gRMHAO+XSTHHsBEoMmeT8Zd0m49qUcATi2Ap+m05neyDPi+LUfvgEaqKanu9rc87NuiKITXuYtU3pcm9Dg/BnChLMvxvEOS7RIHtWUaY8iYELYWxODfUXLBtyJA3s6goBaMSQ2oLdhhTSVn95MKV+7XCsSMEGdoYsfLR634bwstnFG4hdOwORq6gRK143K/UpDFl/BrQ8hI50gjBiLwJfoBGaxJKRzhSu9OzWvGCSrnMy7sPt83RruzbXKUSol+IV7ULnryMY1B4wxl9WjtPXyZPhWdGLS6TudaYQhcUjmhn5WpgS14mhqcLMn64PFI8KlfhTJBOJv0+ODjz3vEo4wj6poVBqZ76UzHIqarpM3AhoC+Na6cYMMmKjqyDyNhWmqAvp84PqWMoYVMKcMqv5+ngfkxnIBlt5R7Q5nTIPvNvWk4QIKS2SEPFmkSjH/haJ9XzLGBpjXX1946ZOum7zSkMybEacFpWsdBxs5AmdeCnD5hhSu4F9IJWRnb424SFhVLPJeCr69+Xy/SUuNa2MZ8qIvhfTiZv9O/vMJ53QGEvJivCS8k3f41fmfNXtW6Vd4QZlvuT9KhT2/Z7nXrQ1z3A7ghjD6AxkQSC4ywn2N79nIrzlz6bJ9bX+EaZZoJxrD9JnXn3p+rDqXkrycYPg9fOypOIdXjfh4FCSxyB47v6P6t5oDUzBu8XDs8IBTSpgilssaOYXanOgkjrUzKIGc1JTK2kHABh5MoXcPJLmmO/Rd/bNsjIQi0SX8AU0JKZqNgZq7y/5XFrkLbu51RS2Wm5hDHXo2fdSi0KSE/J70abSuymjSTWbrBDhwkzvszPW+Z1JtPdJOTPiMtJgA0ZQLbDMkTCzFVOI60e0OSq7gVAFAzRplWlZlNhxTibr7HYCGJON6L++evEqF2S4b8iULWDUWgskTK0UdQdk8upP3atljvks/g5p+p5533pWsaGJjSb3cw5EhEF5mGdm1vihU7j1ItX7cDeHfTLPMgzWfY0nsNV6DIfr49tszbZyS+OhUSY6e+8Umtv4Ip8p+/DGdkc9BXkZchwx9sDf7YePPuvIm+42Myyg3ZoudXpwB6EtxsJbJkS/V8LKA5xSyWBKmvo5x5MfqNorsJHvsfAd96Zpv7u0BXji4MVTTsti5UvNOG/NDj0iwYdbJMsKqnF7Nt49DFSnAgnWuDlXmtdgyCFRd0VCTqWqtjmHSGk8KF2ZxpvyFUlvC+HaO4uTgzJ7x4CQNI1xIzR5WBozJEH1lBT3HOr6qAJZzdT4JMywRcCW4FrCK+qTeE4f3Gp2Oq9K9jHqIvD5fPzGfJfkd6XD+nP+58YDx+/6TtICXrNiPJoTbLiL5nlEEZ5OELMqnc8FCndZ9TXzYjy8h5upn6NgxmmAp32oBKnqgyUZ4vhwGfsDN8yO3ecE8P12CwPW/vbfe5vJBj9dU/CqXKNzkYDKJh4pLWhwTgKEgiJ62MzQqr7WMyntU20tuadLoB+uccCietOQsJSJlA5p3hvmZu+bSLR0/5N4JzNhPj0ty3/8sZz/eI7nWULBb6/L4dtLFCfyAkW9EphKo7p5uV8nLqqqemb/5rh+1Loedp/EcIs0F6krXIvLZwoz8ajEKgkGjDFRUaYMDt4/fdrvbNNMqtxLhLH03fRE6cGX0JxCAsXa5Zrs5E+aaFiHsha8Rg4vYMtIzId1Ta8i1Ca2ltmGUe7TmMb5YzlaypFSs0GYTyFgjTDnuVAC2bGOGQO5oyljWH03fmZWWGHMJYpgRoSJRJxM+9bsCSashSOBa2lZ3U41PDQXWO17/L2SpHXPqnfPFc+4Nge/tHUkQ/d8/5t/3CZPfkJTMFujZ0alNGW/0o6AQ/4WaX3zGgsccwYCBgHPiqi/emmS7Q+0cr9oCL5pIk2xSxusK0BMv6htM5VNyoKSQeg/afmX536KXEGXv31dPv7zj+X9z0ffzMe383L69rSc/vu0HP77LzwPRYqK7aH1IyWC9uU1bLNfp7Ad04N4anOZR3UEsLXi+xM6mhBJHvCmOczrYMwO/fUNkFHtMhdbUFrWa04ojC8FwyN0h7GNimhROMoJNoWJ3b4LTMU8RsV7jsy0MtBRuQ8X2q2uKQTht7Qxw90U0BExdKbdXknsjflkZ7ehsNW4hCFM6cjWOs0Ia2qYk2u71oC5SnaS2i0K5nCYrJ0NZpBzlO+g0Znp+KVv3J/USMrc9d8PrRLiBHq6tV0jyE3burmVa+UlXXtUDflXMAVGVaZPt24kEhOLzsxEXy2GgcnrCCHxoLDPdk9Jd9GMpbLA0ypYvikY8i6ElNIvuu2bjdeXvHcyucQ8XQoW1ZZjkcUc5x/5911LeFzOf3te3v7+tLz9/WE5W4Ds22V5eD4thhQ/vLyjQhTmw/K3yHtv22Bz3TanTOdQb6F0mr77Wm2Nj5F6FP4jDM1h92mMVLUtIYh53lXaXmllO00IWdeOlCEMZjGBzPwzgeX8O9qsEEQJYhNlLrUIjmbX7ERXNGDWYij590bkczSBCEnMsuCTCCq+hzQqWbxrMlhQoq1Tm6td3JziNaWffnETQ1h9NiT/+T7GZ5LsL9fXpfx2tiQ3lC+Z0hrEPXncgrt6w5ZDlIJr3Ps704T0nQeFTPn9rTR1Nkezy2ba5z0MpzP0SSK8XNb7GdnNTOH4fliORvBNerTF8IUahy25OAm2lukUaSrgmEFsVlJM5p4h8bJrKhGqDKGFfKcR1KCA9vzyHuZG0U0xkYBZlQyFe7LuMglD3ivPtY365Wn5+PN5ef37w/LyX0evHXJ8W5bHL7FZT9+QghwR4jXT41ry1pbZZ28pWDN7jmo7rjGYfagVDmJfaLxcpRHnAklT19tkDOsslZv7dAqL9Ut2VCMxsgZ6JJoChRP7jgVuzkZkxU351LSEwdXE3ZPrrmp7l5ar9q7vXknKAkERrgsJWNwqOQ7aqcTLr4y/7ONbDMWNSKaANiE6s99vsP0J615rCKnBjucp0uAeR6hlHSm3YKSGA4d7QrpQdZI0JTolWKfcl5dWtKtL0vPzVscsz5lM503ts9rB3ns66qGQ153vuA8+MqbgqqzVZjZPC0lCpQQ0880Mr4hq5GyDSddNkSR94oD5WzQiDdEr9RPvovTrzwzpMHgOC7cbQ8JhYtFvN0iBcWX6ZE76oRVyx9g6IRaNyD8GUbk8Pywff5yWt78dlrc/D54m5GiGd4v9ej8tj/94Wg7/fFgOb2+Bprq2wIC2jYWfGcA21PSsorbFEBynRv6cCRw2xtgPCyXyHeiqMGtlDDpx/Z5tInYtzcfKP3+FAQuRZGJGEJ0BMcFTzAnuqIpWDdUtxkVyDJVpUmFoNUYVanivwm4QieXRuY7YW3TcKOndC6xJqVdjBqptKvucL5Y+brUtIlOI3IS2CgNgVcKSOgYMzRkCq/mx2pynDDFKdfK4k2QMNg9eDfLBYekQNkWw6vSGdMWzrZJOEUq6sjezYfJWl6tWcYf9Yasl9DCxC+U1E42sICxNCLmjT3d5Hzl0ZNGWD4h07Z0qHRvaATOWjsG2zqKOs05C8ASBbZwxiArZB0qsnMEppgXwPV4OVAxHRTqCraQcMH2PPMMTc61rLawFjMA9zydjBofl4ylyRxmEZP14+HJYPp5Py4PXncADVOrc25xTVbZK4qVPE00hvbzIsE395rUkaOVh7bBPN2Sbh94P6U6Z+39LS1VBOgHiTE+yLr2yKaFNRFLLlmqbMGA93P7skV6+pAdhPAzXS9O/w57gTMuhI6TWEDRw1YfLDVPS5mZ3OWYMIbXl9aPLR0UDnvSDtII2R46bRYlUY6MGBbrhzMFtMKBHWK/MLcVUJAUlYPJMng/aENo8JjMZH21XlZNR/zBjaGrmvW115icoyM+zKdgGNa+ZU2R3tNgm1k3ICxsx9T5JkBo7LZLbKlbA516T3DVJZsUQAHMAkyWzCiXjsBxMs7EBZClGna9heKWqWdMASApvw/1p/NpQnYPgIiWydZ9ekfY98np5yhDiqjk3+iwJmEsJby/tb987M3GtjdmlKtEWMM5CBL3P6It7YG09kwsk38ty18pla+K8+r0/ul+j9pwiIW88oO+fYowTyQx7Ib3Nlp5sTvb3lmZFQiKwkH/m/hg1AaFLwX6NeNK4FoCUKLbP3IU5gtU890+6Z7JLAplqJ/cYLwWPxgCD5jVoY2u9ZpqQ3re5HtrCppcZUN0D633QBGvmsUebUdpaeJ4wb4xZSGgaKAOb5+5UDYUaGSMVEe/Qq58dyszoJOlEyjUy/ltb39M3zds97dr5+NE4BVfvkHsFeUcinXUdWNgV1DiL37NfTUsoUhqfFRklLSI1KOtxwlTaZFKCV7wW/fVSiG7TqFixN81Tvyi0JEQAJf+cMRRpujUajt8+luPreTm+XpbjG5ICcj+xjgqu9+yZXeruWpCq/jueN3m/3zf5Ds9yD41Dd+Er1FPWxNbIomcbQy8S8OpFQdyqyHgfQ4BUnteo5FHKTsrYVio3nrIBeTA9haerJlHxvSp7mswiH9i9mnR8OwdbNIWAIy01e8S7OERpRJ/3WsyCfYaYhSyJSo2aRDlf1cY+0zgLrDDvYlH29nBpSvf5nRr5hxajc72e/3FGPcWHjbULiPa9MQb7nbYgfOekR4p7+fl2RoP6GBwmr5E1Yp2XaHSzVhfsZVtD6HO+M5+77Ue0iVvuhUDiy6jr9dPiFPIlSOHAugipmmmHaXBFYQy6h2UPmQYXDIFSkhINqrQeNQ2unikImuRHrJd/u5cGLrL+Ol7fook74eUJ4/4gxKLEj+q+b8B+P/pOOObldXn46215/NfD8v7VtJV4hjGJkzGKV0hGTINsuaSSqOhjG7O4ZXNc0xZV+1hhr+XhReuLc9Gkta3n61zv9blrfe31u60czhvuTyhiotFYinRmI9Xr6CGWUJBOrkR9t7msNRaEqVHST43BrpIMoF6qFkwPLsEuoNAzqhQ80jGmmnLb5O0gE/Hxjs1o9azLbRCGaNkjwJAeiXS5FabgU0gh6x1zgO/LvMq/hKJSjwpN2FPKVG3D5z1HLLbQWaMm1hGKz2oImwF02m4TnKbPZdfu1BLuT3PBFyoObe/sPsdcXDRX/9TYLAR/MJQ+AVKGMzcBJZOmLbh0B596GvJ0U6ux24m6MJAGWyVjsJ/qmUJtwbBLrTlQpAUeZgtSe1mO//19ef764DEKVrnOUYL3ZXn6x3k5/vW2LC+vo6ypukBmjeoraniZO9kIOk/KaKfP0k0jVKZAgCIRIl/UWhsRKWoGr+W7GhPfaUUbKn2c1JGYHeYVNDTRLDLlR4wvK5bl981bTqGkWdMSnTPby6Xtb67PwzmI3qsFgjJCFw3QUdoTAPOFAR9krSm/U6qf+wB7bOIsEFumaXRbS3Vlf+Ztak/M9OXi/YZ04HkeS3LNIWy5t57HksjoZL9n8SOslTMeQLU+f6xzzUSU6RZrQi55w+U2QtrhXN2eulevCjbtPf1cfFab+AQz+Jym4C8TIssNaTCPqmiMeM6bpBQnO1vUUpE888/xvEiuZyp3w3YTvuD1IFr5PfvMWs6VKXka6iJl8Pks9ycpd4UxZHqIAZr773lIPQjpshz+1z+XR0s3boLLl1NEY54vy8M/35fTXy/L8v01MFR1QSTRWGlfMkcCI+V4lRlcJvnu96SSZACNvuo7db1KbvYRi0Ij6dZezPKenLOplnPFdrJLkWfS78Dc+1cp3afjQBtXqtzN3RV9nEJSykimfWqCDel0xh8cYWzFeznfnjplaNgjb9aGxL9D+OsYJ/usMIXppF4XWPIamZustTyYQtgp4YjCoLyiPeIno74JW5cYjz6mIZj4VR71jBQhvDzti4BTGWW/dO15Z4wrpt8Z8cRY/e+Aln6w3c8UrKXEZAsqxkj7XAhGMAYluGYQUne97kqo2i9+IYZqRL1JoEU+LMxldfzxo2GdSeCbNA1DlZf5dIniOKnlLBqFStJu9LLo1Lfl8Nd5OfyvBw9WO1kpU3Ohs9d9e1uWb6+eB2nUBVaiLxJkqTjXKYBqVzv0cktTSMJUuHESm+hK5xST56YrcnhxbNOLmTrcNIBrh4FEaou5rZ4BrbQTG7XTqCSb80Q8dj6pq4qDem+Hjlpfso9dsMkpgeND2o4mNjf5WzP6jr0iz76F4c5I/4+shb+P/2sStQqIqcX3KUtukq7tXsfFBK44lKoqoDsjTiq1Btw3Ivjt0EotE/8HTcP/PIuNsWmHm8KK7CWOd0+74j16/2yt7oHw9L61Uv1vYArZAfsfIxEDWvH4AKndm7Waqc7RLz7zH40DuIaRGLsgWSgTL4ufPdfKWnrD/8pF4z3hB62GLWwgtwyH//PBjMvTA9kWF26DF7NfvBpjeF0O/+sf3seTJcUzu4Hd9wroyLSELewdHhIlqGzK7JpEJdrUZlI61a6mDQSRr0iYRbUKpmxA+g8yBjoeTKT6aXI2Pl8+34xLmN2r8yJQ38p5QSFO3JNTlVUAx9jzedZo+xoDqX1xJww8Z3POW79WUz7cUDPthheu555ve68wiurOneO7ShTaBb1vV5jInJDJn5jI3Gq5RZlEs149NCxo4nBs4efuMEIbBPuracL7GrM/jH9wCAlE3xT4XEOsiwWnnkMLCwbCPFlbTH7SdN9nPyZ7NwVlOf/9FV0Wm9G22fxTsNQ5+WVMISUaWWEe6pQUjV8j1xDTV2uKYUuslwstg+MIBd4ZhJcvZ/bDyhi0f5Els6/hxLioGgkkMhKIyASKoh32RBcmWBQH0rATQwKR7B+eZ0nLbOO/vCznv/6y2ViWL1+iWp1pEibtWCEVrVqnkINIeRmEtllLuO+MnSZYZ2pwOs86jjJZwMn9QNPgb2uMqwFnOBGDN4AnS3Tt8dbDRBhqkp4gu7Uzzk7QVAvqhyL3sGDQZAo9IAz3Bz+shvfRH01FzjlrLyxz3MZtUKMzpsiU6gKU7SGbV2bVlYIyOoZSbpG+95vzyz2qmsrO9Tq3e40ETjWVzWXnPqr7MeMKkpAyeR28HRlzQMnfyQHz1shaM1cZy6P6awAdp8MJ3I7Fppm1U45Boy42lx6gq3Dy3l6eaESE/+45o+WRjbnMvutdKu9VOn35VZoCJZTJxzP1Wwmm/JqpMDq3DPwpcEclUlzsGd5H/2Je6wSdxDS476h12zBClfyLsRybKas5EWaqYfLOGDIR2nimf44QfMuNf/7rW2x4K2WK4iiF+GSdB3ENFQKUqIJCZ7ONkvtvptJvbYq2Rqr9kFFJVttMTY3DE4V4ZJ3yEIx+XlbS0CfU4jvuWTka6FBVizmsIY3hadSk+xJtfpwbljlnqfwyo+0OvJIuzJY2GzEJjw/L8fk5nTXcGeH0FgbnXqC+aNbinr8xVyVZ4J0S5G7re26gMmOs+W8yHzZf5qCRWicEwJJVeVRfG1AyGXfQjazh7KlTdV1sbS1L8zE8Jw/mpo76IzzT9vc54GSm8vbrzHPQ399iVu5pM2HhM8+5qYnArmfxxvfdVY4zf6b0P4NqsGheolCKaPfNW+7DwSrnmJyc9zDhWIv47NJgSgczoijMJA/EmggPKXKpmCTvoVtbPke1EEhCLA5iKS/Mz9pd6h5QhGjAEn6LEwYL0rEESYTMQHyYNqSnzO6SQJnYvvnu2MgKncAI6JKavoNSU1b+0qlcE0EyhjT8pSRTLrqiCbQ+Jt3e0EZU2+rP71oEtw5ghuDxVL9FmJjBK1oSMm0RExiprwEFGvjoe5Aa3C6dIZhmCcPr5R//dK3TBQorh1vsTH3gAh/m1805gNq4nIOfyB6q5lDG31R4rpF7Bx2Xy8t7rIG5srvWKRliO8buMlQIgP7ZiUjCachGnGNrXFt3V4+4hxBO7Wekv2FAqTcyB2cc1gdo15YYcgb1zFq5ZubyfUPbhFI30A8OnvtNi2r9OptCI6JsVK2kM9G/wO+y43yGqq8+CA1ua++bwQBZVcmwYUm/3Yk+JQ8yEtUykq6KNrJiMkgB7pIDJUYwKLOjUCLku4r2gSyOZlC29CBUX3VcZC7uYdWYnKbl6BrOFRr6+aZzFNKVvjejcq0x5XdOFIj0VFNRZrFtuJvWi7jS9ov6qB1h0hdChxwvhQpernndhdHw4GWyQNEQtiVC9dFHNK9H8qL4ku0jq8Hx5Xk5fHmK/WOfvbwu5+/ffR95+nn105fxFXasjHo23xO0YzXrM+jtWptBSfkS8ehi8ky/R9xQwQBqtT87G4jmVjjEj4gIdx7dPIYdTIUwFAVJc38lVMQ03DxnIiB0TT3nQda7jK2L4lVInU/0Ne23C4EbjXuuCx3cnx9Nm/35NoXJQMrLGAAW6WsjPTS9krrBtxHfxK5pXIPHksI7vM9/isvost2nehCZFRTueFwk5F9yjymmfWDHSAD4HE4G+rWplpExeJxGSNwHS4BEaZrj10p0qwnHZpvh5bMdtYNW3NwSThFiMvXgATOn1AkGOXvcPZ0KJrtzIK5tbmpZqvkppCQSfUr1JOhIMZLvzbnWd6/Tp+/2a+s8554EobG9YraEx8flAo3APW6eHpeDlem0KGfTLKzyWGoyIlx4vq+6AdL2NOuDwmgbTHo+nnY2+ufX9mDfuxyD5yiDiyrmx/cCXXV13+U7mrAkmnZoAyHlJ67GOuOeZylgp1R2BUZasntuEUwmMyx8Ugu7zCf7sjP+JCHXVAf5fm/PN3kr9ze1BGOEm0L3z/Y+YmdXnEoIincaiz1uktEo/bV7TCqHtEnIRqssFWmeN7eJ7TYEHDx1SwuJFpPHnDMeXWZEu0oCYQSTYDZ9z7WDRLxT6xfMbKll0ck0OFUNoN2FWn6AKzSJVrH1UdBEjf/qc46/G0KwtoM0AnvT/O0PsWgJuSdEs+lQEfZmZtZkSvdu6NZxl/oGul5geSsRXbdmG4PerxKdNYOIzEvNsoK6e7P9/RhFm7xSH42umhguPOjcOcJxcZ0LvrBBOtSUs8u95oX0cQVbygD1HQUJkHfmOq7XMsMkKJRZAN9beBo5ZJpR3MfbgiB9naDNw6vo4n7zUiPeszEEhHRQO4u1kzGBAV0z3kr50BBYpLTwNfhM+77HCFZHdyeeR9/Hz/IfBTtA3y6Q/gpNocBGorZNVRdoC46IMPdPXFvSJdgXWeSFEhi5Oj0rLPmehyaOSSgTNZkgbYqrpVSm3hoSIARtIdVYgVIiIhJQFN/DqOuZ5Ehogym6uYG2Uic1QrOa+wIc7rTktbd4/1zmREKJeWHGch+vW3WbhJLPbl3e2+S3SKvUtFS2mGmgqiUUIUI1IcG6275KAzMw7jr2pmEoXq6Fo5bJe0GEVhUH8f6L2KO8xoMxBGMWZnPwAC5oZ6o5F21osuYrhtBCoC3NR8+o1Yn47tIUj4gq2Ezpxlhrv5ppcTy/WNgF/Dmc/y3Xyi0hjVkHUoYRIYtr76k1qFGQll/AKMazmAuNmfd9+hSCXp2xykJWX2VfJgLclC5M5j6fI0G39q9D5Orm/EuYQpd8eDj55RRDBGSDYBOnn8Bxa0pqGI3orpqujxFZ7POv1cm8jrBI9Hy1ShRZjxUl+jIdcXgTmdukK4TEJlV9NIakLqPwDvLnFAYgUhHHrBPmsQvGZOx5hh3DzkA7QnovfQK/3Vso3ay5Pyc7i9IyiQRxVGeOZJw1Edl4DTWFvRoHOx5HW5KURtluSVxC4LSm9Qp26v/6e5WxlRrGxKgjNfOowtaYvzID5aopM+HQdCZEwklozhjBO+FNaLbHUbTJIKTLd9b2tkpjHE+XxDWKePyIS8HMfOluSNNexnRdyB3z0dYo6cXkfs6P5yWCTUD3TeZCUhfS/U4kffF6CwJ9Nu8yZwBOb+D1xJomD0g5ghirUlgLNCIK/zgVHuurmtLWfCkR71lZVxoHByNnaNaCg6YLb4Fy3S4YtK9kwP018FE/YE1d5EfCGMJwFkTYGQAquKVxyItlDGLu/sX+TErtcWGom+O5NKKUyGB/rxELzXukkY08jIEZJpPLdAM4NKjlnO6pWpSmHMLJhBfoyzJe2oF+H+6dgiWvCFUnPluQUJdC+d7ckI3I6pqBng0f8VGEKBh21I9YBwXt9Cefv9YgrxqRk1AiuOkqtIT/6XM3pO8VMSmqNhhKu7ZAFzKkAZ1N4IMiJylzw//IRLKGAyPmoYE60TeYyOIUcLMRqAeDkE74/g0ZQiVH0Ox9yVBlD6SW0PfFjQLJLjPZ0nA33iEanMUFMDOxCX9x1iWdCFPYzJIxTuCbSOkfDiGp6dKWYPARIWXPuRQpuz2T8umU8J1XUHy0RIXDmSLn3emPppiZVSbcEWr4nTLAGQxVmL5wGdn7ZW8VdEOCAQvT+lVMgcSjq6UcgErwxNE7vmgfMac8Tlu4f0ZW1FTZhVG40UieH2oRJPoVU9AIW50oBq/wcGJAB3GP8z7gWZDmnag5cUdeJyUWbIUg0AcahMWgTWN8Ll2HV0ScsS5F762eaigTabsJre6ZtYdLXlqCsqwsh0PI2rdTdaBK2dq2eeW251H2CWNLJrInoRaiqx+0a/r8Yn/Y/qE79HjOgC2GZ4we2sqs4+NrJ26i4nNgzhiwR5kcjnW7cb6MIVzMAP39xSPifbmMcNn8MIFe0WC3II0Yx25KpFnrZ/pWnjAeUJ7FugZJ3DI2AdqfV0oUxpeaRhNGC1IA5o6qi1q+0w3K9Jbz2ANzPWaG1liLuOcY+DttO/Z8m/usfTEY44i34vi2CK9OGPsr65TwzwRpUa2fTGLrLHdNlBl++f2v1xRuOaAyUO8btAWfg7HQPuHkuom7V+N0GnsP5sUjm5QQDKEoThiMiUHr1C5wHqqfqPHh5NTxZ0jzbuNYaxdxyaQ+g0xHbhxPAQ6Ng/UhaDvpTGV6WskdNwj01mIUYjZpIl14tLKqqcn4mQNGFhjrlMZaTTA201buokBXxpUClHLAteq98pZi/5jWwiRGZqmlkMJYCmXm/X59m0p6K2hJugeyU/adMlRPacFylAEh0fjse8i06seH5cL0KN7/hxHxXsreyrtXQttMAJkQNK2VroQ4NYXJeVfGsdmarSf3jWRFZmZU4Zu572daSs4lXMU9wI+pP/geuwRpLjyA7RTZWf0VxoQx9wvicowxmPbw8bRcPLpcszXTBb/tdf9d51Fhn6Iy1+kXRKVtmSpEXdU6eB5VO9D1X/5NTCE3ghKt7qfcDHLOsE1akc3GCWcNBvdCqCUyA06S19D+wOpL9GV2SYgGXVtsSEfFrjCIvBN6lTT1ELkkqekoor8ufxsBMYniFpiDKQlYxlRTIyskwcFVqiP4ohIoeb4SAn0vYJhucM7YCt6omymXhV5gLf0D5ibrb5MYbWkLn+QIJRK9wEMzxrDTuqCGjJseMGaQHnIH0Q4ydT1mP1aw0wZDyJdhrb3kHj/W+Zaaziubw+iC73HzQjJXVcS9RKCdSuCiuW0qZzyLHVaSqVJIqS9dl9T9d3nnSnNd74shEMrzfa+IRUiI5EiJMZGYurYLZ5CSE43xCH690ZcTElFa7Qo0QEqHhISt6l14ex3eBpw09UJTBqFrrH2ctdWZHIx72KDHcztS3xlNwNFtX6a2Ddr3y5jC6ozTGEIq1LUF2TgKJckOZGqKUbsWqp5WObOBuyRlRHqpxdaNIVCdBkY+cu3rgR6G5txIrF/gQ2lGTvpNM/rYywVish+jZuzAHHeaHJqaYVOZjxk1hSAUQn+vvq/rMhGmhV+n9Gw5nWYEqTMESNTKEEIDqtlk10S7aTlbks9UPW5yx+rL7VsoiAThDe3Ack9ZlLCvB6OEPQ4AAsBkzF2qa53e7UvZwzRwUttiLiwji5bKgu6ojNKltkAC5AJT2B7iPCB/U05fk/LL3DWhQolLXidjmeFM14SgvWs7DKRcIV8rBmGHghA7wrnfhEL5OAaBtrQxIjAGmTihbK6hBzjXB/WSigqKzoDdC6zFS9QO1EN2C/3Nc97sAJz3si5DjJttfwZf1md02+dPZgqZUXCmkvcNR2LQcUieKkBJ9EAaGgP+pz7LxswpqTcvGccb4boXueaxYKW8pDSRxMLgrJtFr8XvSFJGt0Dvkx1EV98hA7t3QqjxHWYY6XvjfRmIw1q79rWrrEEYxjOq9Mbnje59QvKe7IeUiJ1R01NraH9pjCslO0U7JMTBcXoh7wgGmkE6uSRTafPKQUroo6nn+kOhHyXetC2hAJIxAyfCtoe+fl0OlsHW2utxWYxZuAaBeACH+eR97QAzvmGsWR+IECWOwzWV9xiSSfsm+X9/iWjmP74uF0tzYd5GXo0PHjh2MQSgw/tzaDiWPsUYisEcrj3E3nSNWiVazEdK3UMcFwLUPu/zvbfnbqM1V5rkZGK+LVZjcxddOzvDdXyqMXOQpC9OW+hKzvEYzIxoat8aCJQ1jcAFgmXkIaMQkf0Q+1LRdkYsS7EPrvq4IbystE+5VoVCRQ1E2B11QS4bTKE5JPx0TaEcZH1Rs46jw/V3cF/7iXTbhOQLPAD7QEyDGdJahkK6ebr7WESAmlHI/Jv9DuPsllysN5dAJMV3uCk3d0vh3p7i4bAcXW1/ivcZQzCiYhvFDH7MT+O+1RPvD2oantsIUqkzBeRAsvrNlPZYCKS41UqfcoK4wVVA3VJR+9/cWI1IoeTppRBVZpTEwWAhklTFm3ET9wznAdWQxtqVn/zd/57goPkYBhhxn9XxlEhqPWBnYQhGaIwZWLoIW4Mvz8tiTMHWxC5HWgkbq0uHZNTmHgftUOGKDDjL8SHCvjCGtre8Bonh1pHv6vwShZaMMR3+/h/L8ucfy+XL03J5ttgE8W4xhvf0sBzsc4NBSTTfrRb4m9saPB2G7U1jENCCRyZdgZfKGV1xCnyk/d+S0LcMqzc09kOkZeaCcsZGxuxCohnaLc8Wy+w21/N8Xts7ON8ZZ2DjzPsjFsTtNPYW3+OmGSArLZ7JDK0u2PEdJagR5xV72GEqKRY0mbH1mc59JePQpjYL0ERGVqcNQc+iCETO9MhIb1yr++MUehF0Vel1b12ubAQnaGAQ03KbYBg+MKSK4EQ7MzDf7afl8seX5fL8tFyeoHLbo1+tHCaYiklUeLe/NvFHzclOrwQh5vgX9XEjEdnh9DAyVBpxcVw6XNlozKI9INIxQ0Nwj6Pzcnx6CuZCNZTeFWY8tB3MgJqsWa1zx/mWSc7NtSHFzRhFJ7aUrlMqVgIv0gcq7UX9CTEEsgyq9nPVpdbnqfa9PgwMp8p8RA5vYY75sCJYSYU1BBsm5u4E1BKumQ/6gzOEw5cvy+Xrc0jkODw+H+8fnmuo5Bni3KgkJv0dtZobBKNjdc2QeXgig65v/z//XJb/+Nty+c8/l/MfT8v5+eQMwWuR2LLQ5dp/h0eMb2FLpndeDi/vy+H763L4/rIcDXqyeAZjDvTL960NokDJH9BMVSFmms5MS7gDItlrutc+RIAiNKa2Kl8feIRxUpPQ6Z7FLwL/Be2w8sG2o5qWQQ0A8NDF4cW3wvxVkHJoTivhNQ/Jy5sZqOn0gkDYLSWr9F2r9bWJ5RxkXW4kouR3GhiL9XT6Qycc2D1/vk0BhqAymfwiifk1FWUiWdAraSU50k0PbpHQIApD+POP5fznl+X89XE5O1OIPEKnl4flpAm2TJJytRATxYprtkks3S7hEzIs3Qyo70oXNUu9y0FcziaVRTlNJ142m5l8LyRB39zuA31aDgYLmERoz4KmcLFgNnucHWTzrALXD28sgZG65CZTGhSCi6QbayJFz1YlGYNUPtF3FrVWJH3XCMx9d0LQizQ0CGapDqEbu6z9YCBVA1Bpt/Z/aBi4jPuKhxVSf6zj47L8xx/L2RjC82NIjB9nL+MeOXHekWvIoEO6H+M5Ko1tT6gIQNJnsb/4vnx4WI6mHfz9z+X89z+Wt/98Xt7/MNfTAakePi7L8R17lN50ggwd7fuXj+X4/XE5/vXoWrOP0c4JtIbwuLN7IdCg/yVuJCXs6HDh3V1LuIsZ7AktY6583pE+3O097hmkHjUgbnb2c49Qom8P9aOszJuFfcyGYDBcg6xNGLDnnh+5U6MfhBDtMqMBtm84t2pH5HqbfeIj8lM5UqGxLmqrnO4X1XYm12hyzJX2WTWEnC864lCQ/SXw0dag9vBgMgpKT71jiV0Se5X7+Dk1BGJ6Fvb/5Xm5/O15ef+P5+Xjj9Py8XxczqyB/D24s2dAMWJgC+zcH55E0DzyoPoENtxX+8/cSFjgNMaaBmBSPrUFJ/SSX0VcHg/Pj8EQnk0yhXHcNRGTTqAhvGAT0uibc3T7OStrkYeOQyFxbqorH5laVHtZs5UUiKdV+BpPE61PJLhMpqACxB02EiVkrJdR+oR+xeuQ/JD3gKgsf/tjufz9j+XDSqSaMHE8LEfzdLsYAX6KA+1QIQKdqK5TsOjSKcfJ+SnuijpvYUMy7dUI9/HPvy3Lf/19ef8ff1ve/vNpef37aXn/w/YxfB9MUXm7LKfXYAz+SE/RFS7ULjiajPN6Wh6+n5bT88NyenpwbcGJ2PHolrvL22vahCJIrM8bOpl5uSZaztgl48+E/Npl3Za4gpWrwOEMD5CRMwQ7M06IH8I+ArjN7Ca+hmlTaAiDEkV/pRjn3fPwHBCznX+tB+00ZTC/A59l17tjCdKd4Pw6VC1OLQ4zkqm923mwn7AXERK2+zf3+WDS19qIi5g8gvcThkOG40gR1OqG/wym4ML6e0i/frA5WTwg+jPhBi6ScsHLps9ySTHgHJ9SFbJDMh+MGeH+9sUZwut/Pi5vfx6Xj+dDHqaHb8fl/HBYHh+OHul+tMNuB+nbdxgaXwMGMXXUFpfh7VT1PoYKGdKB2SyQudKaG7bBfT0q0rDhl9gYXADHpY0hnJej5cb/2x/L8revwczc/hFzd3gyhjIIq6v9jqdOvBxUip59x9aJpBJKbUmoHRREPnmRaghzZPlDwnpQoZ1IAUcHIVQaWKiFqrZuU9ooM9rHMRurpLXQcQxmiHf5gcca2zqaQfmPL8v5v/62vP39efn4evJ94hDO63l5tF9d2vtYjobP//VXYtspWaZXTCWG7vxDN8hkFAZ3xV4e5T5tzZ+Wo+0HYwj/82/Ly//+vLz812l5+fthef9FyNEKAAAHXElEQVSKKFxnCMtyerH9bEwL7Daywni/Kfib48zp5bicvp+Wx3+eloevD8vp69Ny/OfTcvzXU+x7hzvNQcK00YHVa8R9CF7DcJpldfUfN8IGokT7YElzgi8i1QSEKl1Tg2Ft31s/bb5PRz8zrhXAO4uG9OX796I15Hpn1gLAKewWDcFwRCHS4BkGlDH0uIOjnjWzPTyGQGfa5ReBq31PBOTosLWX2n1z+44zCeyfszG6ouFPbDSdNipdpSB0iwCVRbxMEDp5KnYXcMBsfxpTsE15+PaynP/5r1BPE+dCp5uBIw0dMwJVxj4Yw2VldBR3SLqdPj8t57//bXn/H1+X7//jafn+v52Wt/84LB9f7KDEbQ/fD8vbn4fl6c/T8vz1tDx+eVxO//11Ofzr23L465szBcI6l9eX6MbpYUgPLiFEVOPhj6/LwewWRkyegc25QepjWZ6hrhPDBY7LRfUKWoYX//F1Wf78upz//Lp8/O1xOT9GXiG3e72el9OXx+VozzJJ5J9/LZdv38JNMesiz1ZkC+edwEzUunaa31XMA00SzgNlLpMmrYmNAPjlcCOkn/9a+0qoqWsrnYjoGMthgWue7JskOGqH4Xut38YM/vybawfvf/+yvP7X0/L6n6ZdIlGjadgv5u0W6/JgmsMZggTzDXm6k4FBb6XgmAbMcQzuaPC0LF+/LMvf/7a8/e9/W779H1+Wb//zuLz8b4fl9T8vy8fXWITj22E5vhyWh78Oy8O/luVkzlJuDA+G8PG0LOcnoKv26LeDj+Hxr9Py+I/H5emfz8vjP74uD/94WY7/elkOf70EQTWtGdlWHT83YnEJj6XcbMTOhThOx9UZeBc8msZNg+/w94fg4efwNTQ6s7sZw7Q5MoLmthNo+//6azn/9X25/PVXEGqeVzAD2vFKDJC/O4SYSGp3EsYgbuzUaM+YE5f6bV4uy9FsTibMGQ3484uf4Y8vpzjHpwHzmdB8ej0DzjM7jzEHK570FkW2WDCIUfQUx1zpoX1Czi0LAxkTh31gaEFyPgrEHN5GjkzAdmbwpNktj98mzjc/whRcMnl+TA5upSW7ajxjDhrBmdJjl+x42ATiyI2EBQ8nECSyM7jm+/ty+mZS0WE5P0W94GT0xpQ/mpGUOG7abJhyODBCJ3S+UYKAuibkm9Wwwe8BLXw/jQIy9jwYxJJB8jnafzcGvgck8WLfG8FBsjk3ihtsZN+9xjUanZ3qsRDAa+id2gH4zaZ00bUN7rUa1e2PE1gp3Pia26zPm0Q1d6KPeTvgEE1V5S31eaoxxPtXrsCz65U2udYbcEyUYgyp/PT9spxsT31/X45+kEeOqtSG5Jl7ivhu4jFqkUYoUHjJyj26N69pB2akdGHB+rQsD39dlqd/Wt+gZT0clvOjQSGHcMrzWBl604RgZJ+FJoGMq96nlg4+oRNIwnYOZN2TKRTmG5r0vUWQct74LzMPYAO9x7lzYcs+dw8/E9yGJK61q30cHkxmruDYS5mocpINVPtPGBiMweEkaBGlUNd7MCIT+LI/7ml0CLOhL1PgeT7HmJeA9mLufa87zbKFUScFWm1UgBP3eArbWpOmpAkaA1PlLRGZ3Pegea61vDnc/lOZwvf/z2X55//5X8vz//jDDTVGiEsTHLscCsEP05NQ1fw0nODjFGqZbhu4o3HiN1PrP5aT4ax/vS5//K/vy9f/77J8uORtCxVQh6lxzq0vx+VIVfhyXj5M5fvzP5bF3PpssUwyfDCoyYx7wfXt0GUMnpdKjOcZE7KfbkyKbH7Lcnpalsf/yIjqUEeJqeJ+v/cNXiJvy8PLWoXzvlmE939+XS7/x9/hXivP2j2DYnCarkeHWSa3+u8YNzZmzCfSAxSjltgeuKbt93DxplDQ3ydrvRrKzkBnipHWG+5jzetCgjODuJmMT/94Xb58/1ie/2+slwsRhleb18iHH+iPLw/L+evfl8vDfwbRQL857uKuqR3SOd+aayM2Bi+8nZeH/+tfy5//1z+Xr0bI/3h0OMtgicDCD8vRmMSr2RSsfyTqkbfofLws58N5OTteGk4Jvt++vS2Hf70sp/NhORrRXA7u/f1h1/yP/3BXV9/3TgjjsJGpZEbQZAQCCPJcTtZolQIc05G7RjVWpLUJPwqeNZxvmxuuhWniyEnkzYj2kzkJwKGEWXyxHyNTwYT+oO/+ybESbT/rp/HPaPdCmuNV8S5uazp9/1iOf70vp7/evFT28a/X5fTfISgOKJXxJ6iwyBrxKBtw+WKleC1IEmNJeGp4kvl4VaByW0d4og36Ii7ChY7WAD3fp27wfl+O31499uWv//N/Lre0w+UW68bv9rv9br/b7/b/inZDfobf7Xf73X633+3/Le03U/jdfrff7Xf73bL9Zgq/2+/2u/1uv1u230zhd/vdfrff7XfL9psp/G6/2+/2u/1u2X4zhd/td/vdfrffLdtvpvC7/W6/2+/2u2X7zRR+t9/td/vdfrdsv5nC7/a7/W6/2++2sP3/AMdvZkxWnzz2AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "#@title Create Error Map GUI\n", + "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", "\n", "import numpy as np\n", @@ -316,6 +451,7 @@ "from matplotlib import pyplot as plt\n", "from nanopyx.core.transform import ErrorMap\n", "\n", + "\n", "def run_error(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -324,7 +460,9 @@ " gui_error[\"run\"].description = \"Calculating...\"\n", " global errormap\n", " error_map = ErrorMap()\n", - " error_map.optimise(np.mean(dataset_original, axis=0), np.mean(dataset_srrf, axis=0))\n", + " error_map.optimise(\n", + " np.mean(dataset_original, axis=0), np.mean(dataset_srrf, axis=0)\n", + " )\n", " gui_error[\"run\"].disabled = False\n", " gui_error[\"run\"].description = \"Calculate\"\n", " print(\"RSE: \", error_map.getRSE())\n", @@ -336,7 +474,9 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_error_map.tif\", errormap)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map.tif\", errormap)\n", " plt.imshow(errormap)\n", " plt.axis(\"off\")\n", @@ -346,11 +486,13 @@ " with output_plot:\n", " display(Image.open(img_buf))\n", " gui_error._main_display.children = gui_error._main_display.children + (\n", - " widgets.Label(value=\"RSE: \"+str(error_map.getRSE())),\n", - " widgets.Label(value=\"RSP: \"+str(error_map.getRSP())),\n", - " output_plot)\n", + " widgets.Label(value=\"RSE: \" + str(error_map.getRSE())),\n", + " widgets.Label(value=\"RSP: \" + str(error_map.getRSP())),\n", + " output_plot,\n", + " )\n", " plt.clf()\n", "\n", + "\n", "def run_error_stack(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -364,27 +506,40 @@ " rse_rsp_table = []\n", "\n", " # Ensure datasets are 3D\n", - " if np.ndim(dataset_original) == 2: \n", - " dataset_original = np.expand_dims(dataset_original, axis=0) \n", - " if np.ndim(dataset_srrf) == 2: \n", - " dataset_srrf = np.expand_dims(dataset_srrf, axis=0) \n", + " if np.ndim(dataset_original) == 2:\n", + " dataset_original = np.expand_dims(dataset_original, axis=0)\n", + " if np.ndim(dataset_srrf) == 2:\n", + " dataset_srrf = np.expand_dims(dataset_srrf, axis=0)\n", + "\n", + " # Ensures datasets have same number of frames\n", + "\n", + " if dataset_original.shape[0] > dataset_srrf.shape[0]:\n", + " factor = dataset_original.shape[0] // dataset_srrf.shape[0]\n", + " remainder = dataset_original.shape[0] % dataset_srrf.shape[0]\n", + " averaged_blocks = [\n", + " np.mean(dataset_original[i * factor : (i + 1) * factor], axis=0)\n", + " for i in range(dataset_srrf.shape[0])\n", + " ]\n", + " if remainder > 0:\n", + " averaged_blocks.append(\n", + " np.mean(dataset_original[-remainder:], axis=0)\n", + " )\n", + " dataset_original = np.array(averaged_blocks)\n", "\n", " # Iterate through each slice\n", " print(\"Processing slices...\")\n", - " for i in tqdm(range(dataset_original.shape[0]),desc=\"Slices processed\"): \n", - " slice_df = dataset_original[i] # \n", - " slice_sr = dataset_srrf[i] # \n", - " \n", + " for i in tqdm(range(dataset_original.shape[0]), desc=\"Slices processed\"):\n", + " slice_df = dataset_original[i] #\n", + " slice_sr = dataset_srrf[i] #\n", + "\n", " error_map = ErrorMap()\n", " error_map.optimise(slice_df, slice_sr)\n", "\n", " # Store the error map and RSE/RSP values\n", " errormap_stack.append(np.array(error_map.imRSE))\n", - " rse_rsp_table.append({\n", - " \"Slice\": i,\n", - " \"RSE\": error_map.getRSE(),\n", - " \"RSP\": error_map.getRSP()\n", - " })\n", + " rse_rsp_table.append(\n", + " {\"Slice\": i, \"RSE\": error_map.getRSE(), \"RSP\": error_map.getRSP()}\n", + " )\n", "\n", " # Convert results to arrays\n", " errormap_stack = np.array(errormap_stack) # 3D stack of error maps\n", @@ -394,10 +549,16 @@ " if own_data:\n", " path = gui_data[\"upload\"].selected_path\n", " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", - " tiff.imwrite(path + os.sep + name + \"_error_map_stack.tif\", errormap_stack)\n", - " rse_rsp_table.to_csv(path + os.sep + name + \"_rse_rsp_table.csv\", index=False)\n", + " tiff.imwrite(\n", + " path + os.sep + name + \"_error_map_stack.tif\", errormap_stack\n", + " )\n", + " rse_rsp_table.to_csv(\n", + " path + os.sep + name + \"_rse_rsp_table.csv\", index=False\n", + " )\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map_stack.tif\", errormap_stack)\n", " rse_rsp_table.to_csv(name + \"_rse_rsp_table.csv\", index=False)\n", "\n", @@ -414,9 +575,13 @@ "\n", "\n", "gui_error.add_checkbox(\"save\", description=\"Save output\", value=True)\n", - "gui_error.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_error.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_error.add_button(\"run\", description=\"Calculate\")\n", "gui_error[\"run\"].on_click(run_error_stack)\n", "gui_error.show()\n", @@ -426,7 +591,7 @@ }, { "cell_type": "markdown", - "id": "f46f54e3", + "id": "1cbff0fd", "metadata": {}, "source": [ "## Let's compare the resolution of the raw data with the SRRF using FRC and DecorrelationAnalysis. Let's start with calculation the FRC resolution of the raw data (select frame 3 and 11).\n", @@ -435,7 +600,7 @@ }, { "cell_type": "markdown", - "id": "63a03836", + "id": "6656985e", "metadata": {}, "source": [ "# FRC Parameters:\n", @@ -449,12 +614,36 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "4c7fdeb0", + "execution_count": 6, + "id": "294051c3", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4b131bea871143858bdbe85465dd0474", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title create FRC GUI for original image\n", "gui_frc_df = EasyGui(\"FRC\")\n", @@ -496,7 +685,7 @@ " gui_frc_df._main_display.children = gui_frc_df._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_df.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=0)\n", "gui_frc_df.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=1)\n", @@ -510,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "8bdcd38c", + "id": "3d6160cf", "metadata": {}, "source": [ "## Now do the same for the SRRF image\n", @@ -520,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f53ebbc0", + "id": "16c1ed7f", "metadata": { "cellView": "form" }, @@ -566,7 +755,7 @@ " gui_frc_srrf._main_display.children = gui_frc_srrf._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_srrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_srrf.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_srrf[0].shape[0]-1, value=0)\n", "gui_frc_srrf.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_srrf[0].shape[0]-1, value=1)\n", @@ -580,7 +769,7 @@ }, { "cell_type": "markdown", - "id": "e4dbf494", + "id": "3b71f8cd", "metadata": {}, "source": [ "## Let's do the same using Decorrelation analysis\n", @@ -589,7 +778,7 @@ }, { "cell_type": "markdown", - "id": "cf511dc0", + "id": "9798b705", "metadata": {}, "source": [ "# Image Decorrelation Analysis Parameters:\n", @@ -605,7 +794,7 @@ { "cell_type": "code", "execution_count": null, - "id": "caae72d5", + "id": "bc5b3e82", "metadata": { "cellView": "form" }, @@ -651,7 +840,7 @@ " gui_decorr_df._main_display.children = gui_decorr_df._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_df.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_original.shape[0]-1, value=0)\n", "gui_decorr_df.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -666,7 +855,7 @@ }, { "cell_type": "markdown", - "id": "e6625292", + "id": "e39f89d9", "metadata": {}, "source": [ "## Now let's measure the resolution of the generated SRRF image using Decorrelation analysis\n", @@ -676,7 +865,7 @@ { "cell_type": "code", "execution_count": null, - "id": "812ce078", + "id": "e9e6d9ce", "metadata": { "cellView": "form" }, @@ -722,7 +911,7 @@ " gui_decorr_srrf._main_display.children = gui_decorr_srrf._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_srrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_srrf.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_srrf.shape[0]-1, value=0)\n", "gui_decorr_srrf.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -736,7 +925,25 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "aiobio", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, "nbformat": 4, "nbformat_minor": 5 } diff --git a/notebooks/NonLocalMeansDenoising.ipynb b/notebooks/NonLocalMeansDenoising.ipynb index 56e9e4c7..61421067 100644 --- a/notebooks/NonLocalMeansDenoising.ipynb +++ b/notebooks/NonLocalMeansDenoising.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "e9506cc6", + "id": "1431ef5d", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -23,7 +23,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b563d1aa", + "id": "62dd0f47", "metadata": { "cellView": "form" }, @@ -45,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "af5c8d8a", + "id": "6554b761", "metadata": { "cellView": "form" }, @@ -77,7 +77,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -100,7 +103,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5ee5d3b4", + "id": "565aafd4", "metadata": { "cellView": "form" }, @@ -114,7 +117,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -125,7 +128,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -184,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "ef4922fa", + "id": "10cf26ea", "metadata": {}, "source": [ "# Use Non-local means denoising on selected data\n", @@ -201,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1ec558bb", + "id": "69c6a01e", "metadata": { "cellView": "form" }, @@ -240,8 +243,8 @@ " tiff.imwrite(name + \"_nlm_denoised.tif\", dataset_nlm)\n", " gui_nlm._main_display.children = gui_nlm._main_display.children + (stackview.slice(dataset_nlm, colormap=gui_nlm[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_nlm.add_int_slider(\"patch_size\", description=\"Patch Size\", min=1, max=dataset_original.shape[-1]//2, value=5, remember_value=True)\n", - "gui_nlm.add_int_slider(\"patch_distance\", description=\"Patch Distance\", min=1, max=dataset_original.shape[-1]//2, value=10, remember_value=True)\n", + "gui_nlm.add_int_slider(\"patch_size\", description=\"Patch Size\", min=1, max=dataset_original.shape[-1]//2, value=5)\n", + "gui_nlm.add_int_slider(\"patch_distance\", description=\"Patch Distance\", min=1, max=dataset_original.shape[-1]//2, value=10)\n", "gui_nlm.add_float_text(\"h\", description=\"h\", value=0.1, remember_value=True)\n", "gui_nlm.add_float_text(\"sigma\", description=\"sigma\", value=0.1, remember_value=True)\n", "gui_nlm.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", diff --git a/notebooks/ParamSweepandeSRRF.ipynb b/notebooks/ParamSweepandeSRRF.ipynb index 1e88c5a5..cd2f12b1 100644 --- a/notebooks/ParamSweepandeSRRF.ipynb +++ b/notebooks/ParamSweepandeSRRF.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "405bfe1c", + "id": "a6dfac91", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d2cef25b", + "id": "fd249678", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7fb6b318", + "id": "86f9bafb", "metadata": { "cellView": "form" }, @@ -82,7 +82,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -105,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c0ff8eff", + "id": "ffc7ae04", "metadata": { "cellView": "form" }, @@ -119,7 +122,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -130,7 +133,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -189,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "ddfd8f13", + "id": "7f7460e4", "metadata": {}, "source": [ "# Run a parameter sweep to find the best combination of parameters\n", @@ -206,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7c94da97", + "id": "29f01d72", "metadata": { "cellView": "form" }, @@ -271,16 +274,19 @@ { "cell_type": "code", "execution_count": null, - "id": "45d467db", + "id": "a8e81b65", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create eSRRF GUI\n", + "# @title Create eSRRF GUI\n", "gui_esrrf = EasyGui(\"esrrf\")\n", "from nanopyx.methods import eSRRF\n", - "from nanopyx.core.transform.sr_temporal_correlations import calculate_eSRRF_temporal_correlations\n", + "from nanopyx.core.transform.sr_temporal_correlations import (\n", + " calculate_eSRRF_temporal_correlations,\n", + ")\n", + "\n", "\n", "def run_esrrf(b):\n", " clear_output()\n", @@ -306,14 +312,22 @@ " elif frames_per_timepoint > dataset_original.shape[0]:\n", " frames_per_timepoint = dataset_original.shape[0]\n", "\n", - " output= []\n", + " output = []\n", "\n", " for i in range(dataset_original.shape[0] // frames_per_timepoint):\n", - " block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint]\n", - " result = eSRRF(block, magnification=magnification, radius=ring_radius,\n", - " sensitivity=sensitivity,\n", - " doIntensityWeighting=True)\n", - " output.append(calculate_eSRRF_temporal_correlations(result[0], esrrf_order))\n", + " block = dataset_original[\n", + " i * frames_per_timepoint : (i + 1) * frames_per_timepoint\n", + " ]\n", + " result = eSRRF(\n", + " block,\n", + " magnification=magnification,\n", + " radius=ring_radius,\n", + " sensitivity=sensitivity,\n", + " doIntensityWeighting=True,\n", + " )\n", + " output.append(\n", + " calculate_eSRRF_temporal_correlations(result[0], esrrf_order)\n", + " )\n", "\n", " global dataset_esrrf\n", " dataset_esrrf = np.array(output)\n", @@ -326,27 +340,69 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_esrrf.tif\", dataset_esrrf)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_esrrf.tif\", dataset_esrrf)\n", - " gui_esrrf._main_display.children = gui_esrrf._main_display.children + (stackview.slice(dataset_esrrf, colormap=gui_esrrf[\"cmaps\"].value, continuous_update=True),)\n", + " gui_esrrf._main_display.children = gui_esrrf._main_display.children + (\n", + " stackview.slice(\n", + " dataset_esrrf,\n", + " colormap=gui_esrrf[\"cmaps\"].value,\n", + " continuous_update=True,\n", + " ),\n", + " )\n", "\n", "\n", "default_radius = 1.5\n", "default_sensitivity = 1\n", "default_magnification = 5\n", "default_esrrf_order = 1\n", - "gui_esrrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=optimal_radius, remember_value=True)\n", - "gui_esrrf.add_int_slider(\"sensitivity\", description=\"Sensitivity:\", min=1, max=10, value=optimal_sensitivity)\n", - "gui_esrrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=global_mag)\n", - "gui_esrrf.add_int_slider(\"esrrf_order\", description=\"eSRRF order:\", min=1, max=3, value=g_temp_corr)\n", - "gui_esrrf.add_label(\"-=-= Time-Lapse =-=-\")\n", - "gui_esrrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=0, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", + "gui_esrrf.add_float_slider(\n", + " \"ring_radius\",\n", + " description=\"Ring Radius:\",\n", + " min=0.1,\n", + " max=3.0,\n", + " value=optimal_radius,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"sensitivity\",\n", + " description=\"Sensitivity:\",\n", + " min=1,\n", + " max=10,\n", + " value=optimal_sensitivity,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"magnification\",\n", + " description=\"Magnification:\",\n", + " min=1,\n", + " max=10,\n", + " value=global_mag,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"esrrf_order\",\n", + " description=\"eSRRF order:\",\n", + " min=1,\n", + " max=3,\n", + " value=g_temp_corr,\n", + ")\n", + "gui_esrrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", + "gui_esrrf.add_int_slider(\n", + " \"frames_per_timepoint\",\n", + " description=\"Frames per time-point (0 - auto)\",\n", + " min=0,\n", + " max=dataset_original.shape[0],\n", + " value=dataset_original.shape[0] // 2,\n", + ")\n", "gui_esrrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", - "gui_esrrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_esrrf.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_esrrf.add_button(\"run\", description=\"Run\")\n", - "gui_esrrf['run'].on_click(run_esrrf)\n", + "gui_esrrf[\"run\"].on_click(run_esrrf)\n", "gui_esrrf.show()\n", "\n", "\n" @@ -354,7 +410,7 @@ }, { "cell_type": "markdown", - "id": "80358e39", + "id": "2d4a4846", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -364,13 +420,13 @@ { "cell_type": "code", "execution_count": null, - "id": "b9fbe131", + "id": "d951ae96", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create Error Map GUI\n", + "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", "\n", "import numpy as np\n", @@ -379,6 +435,7 @@ "from matplotlib import pyplot as plt\n", "from nanopyx.core.transform import ErrorMap\n", "\n", + "\n", "def run_error(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -387,7 +444,9 @@ " gui_error[\"run\"].description = \"Calculating...\"\n", " global errormap\n", " error_map = ErrorMap()\n", - " error_map.optimise(np.mean(dataset_original, axis=0), np.mean(dataset_esrrf, axis=0))\n", + " error_map.optimise(\n", + " np.mean(dataset_original, axis=0), np.mean(dataset_esrrf, axis=0)\n", + " )\n", " gui_error[\"run\"].disabled = False\n", " gui_error[\"run\"].description = \"Calculate\"\n", " print(\"RSE: \", error_map.getRSE())\n", @@ -399,7 +458,9 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_error_map.tif\", errormap)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map.tif\", errormap)\n", " plt.imshow(errormap)\n", " plt.axis(\"off\")\n", @@ -409,11 +470,13 @@ " with output_plot:\n", " display(Image.open(img_buf))\n", " gui_error._main_display.children = gui_error._main_display.children + (\n", - " widgets.Label(value=\"RSE: \"+str(error_map.getRSE())),\n", - " widgets.Label(value=\"RSP: \"+str(error_map.getRSP())),\n", - " output_plot)\n", + " widgets.Label(value=\"RSE: \" + str(error_map.getRSE())),\n", + " widgets.Label(value=\"RSP: \" + str(error_map.getRSP())),\n", + " output_plot,\n", + " )\n", " plt.clf()\n", "\n", + "\n", "def run_error_stack(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -427,27 +490,40 @@ " rse_rsp_table = []\n", "\n", " # Ensure datasets are 3D\n", - " if np.ndim(dataset_original) == 2: \n", - " dataset_original = np.expand_dims(dataset_original, axis=0) \n", - " if np.ndim(dataset_esrrf) == 2: \n", - " dataset_esrrf = np.expand_dims(dataset_esrrf, axis=0) \n", + " if np.ndim(dataset_original) == 2:\n", + " dataset_original = np.expand_dims(dataset_original, axis=0)\n", + " if np.ndim(dataset_esrrf) == 2:\n", + " dataset_esrrf = np.expand_dims(dataset_esrrf, axis=0)\n", + "\n", + " # Ensures datasets have same number of frames\n", + "\n", + " if dataset_original.shape[0] > dataset_esrrf.shape[0]:\n", + " factor = dataset_original.shape[0] // dataset_esrrf.shape[0]\n", + " remainder = dataset_original.shape[0] % dataset_esrrf.shape[0]\n", + " averaged_blocks = [\n", + " np.mean(dataset_original[i * factor : (i + 1) * factor], axis=0)\n", + " for i in range(dataset_esrrf.shape[0])\n", + " ]\n", + " if remainder > 0:\n", + " averaged_blocks.append(\n", + " np.mean(dataset_original[-remainder:], axis=0)\n", + " )\n", + " dataset_original = np.array(averaged_blocks)\n", "\n", " # Iterate through each slice\n", " print(\"Processing slices...\")\n", - " for i in tqdm(range(dataset_original.shape[0]),desc=\"Slices processed\"): \n", - " slice_df = dataset_original[i] # \n", - " slice_sr = dataset_esrrf[i] # \n", - " \n", + " for i in tqdm(range(dataset_original.shape[0]), desc=\"Slices processed\"):\n", + " slice_df = dataset_original[i] #\n", + " slice_sr = dataset_esrrf[i] #\n", + "\n", " error_map = ErrorMap()\n", " error_map.optimise(slice_df, slice_sr)\n", "\n", " # Store the error map and RSE/RSP values\n", " errormap_stack.append(np.array(error_map.imRSE))\n", - " rse_rsp_table.append({\n", - " \"Slice\": i,\n", - " \"RSE\": error_map.getRSE(),\n", - " \"RSP\": error_map.getRSP()\n", - " })\n", + " rse_rsp_table.append(\n", + " {\"Slice\": i, \"RSE\": error_map.getRSE(), \"RSP\": error_map.getRSP()}\n", + " )\n", "\n", " # Convert results to arrays\n", " errormap_stack = np.array(errormap_stack) # 3D stack of error maps\n", @@ -457,10 +533,16 @@ " if own_data:\n", " path = gui_data[\"upload\"].selected_path\n", " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", - " tiff.imwrite(path + os.sep + name + \"_error_map_stack.tif\", errormap_stack)\n", - " rse_rsp_table.to_csv(path + os.sep + name + \"_rse_rsp_table.csv\", index=False)\n", + " tiff.imwrite(\n", + " path + os.sep + name + \"_error_map_stack.tif\", errormap_stack\n", + " )\n", + " rse_rsp_table.to_csv(\n", + " path + os.sep + name + \"_rse_rsp_table.csv\", index=False\n", + " )\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map_stack.tif\", errormap_stack)\n", " rse_rsp_table.to_csv(name + \"_rse_rsp_table.csv\", index=False)\n", "\n", @@ -477,9 +559,13 @@ "\n", "\n", "gui_error.add_checkbox(\"save\", description=\"Save output\", value=True)\n", - "gui_error.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_error.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_error.add_button(\"run\", description=\"Calculate\")\n", "gui_error[\"run\"].on_click(run_error_stack)\n", "gui_error.show()\n", @@ -489,7 +575,7 @@ }, { "cell_type": "markdown", - "id": "c74572b3", + "id": "61e9a48f", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -505,7 +591,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b2909907", + "id": "861fee57", "metadata": { "cellView": "form" }, @@ -551,7 +637,7 @@ " gui_frc._main_display.children = gui_frc._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=0)\n", "gui_frc.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=1)\n", @@ -565,7 +651,7 @@ }, { "cell_type": "markdown", - "id": "3b8a78c8", + "id": "c17f6bf0", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -581,7 +667,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d3a2bfa7", + "id": "f093bac2", "metadata": { "cellView": "form" }, @@ -627,7 +713,7 @@ " gui_frc_esrrf._main_display.children = gui_frc_esrrf._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_esrrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_esrrf.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_esrrf[0].shape[0]-1, value=0)\n", "gui_frc_esrrf.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_esrrf[0].shape[0]-1, value=1)\n", @@ -641,7 +727,7 @@ }, { "cell_type": "markdown", - "id": "a14e3e50", + "id": "9a090d9e", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -658,7 +744,7 @@ { "cell_type": "code", "execution_count": null, - "id": "405400f8", + "id": "8e62e0c3", "metadata": { "cellView": "form" }, @@ -704,7 +790,7 @@ " gui_decorr._main_display.children = gui_decorr._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_original.shape[0]-1, value=0)\n", "gui_decorr.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -719,7 +805,7 @@ }, { "cell_type": "markdown", - "id": "93713482", + "id": "31a82f0a", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -736,7 +822,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25f90483", + "id": "17a98f6c", "metadata": { "cellView": "form" }, @@ -782,7 +868,7 @@ " gui_decorr_esrrf._main_display.children = gui_decorr_esrrf._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_esrrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_esrrf.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_esrrf.shape[0]-1, value=0)\n", "gui_decorr_esrrf.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", diff --git a/notebooks/SRMetrics.ipynb b/notebooks/SRMetrics.ipynb index 3f1034c6..1d333d96 100644 --- a/notebooks/SRMetrics.ipynb +++ b/notebooks/SRMetrics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "5622371f", + "id": "f1e4c9d8", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9e477137", + "id": "2f3fb423", "metadata": { "cellView": "form" }, @@ -48,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d1d6a6dc", + "id": "10f64df9", "metadata": { "cellView": "form" }, @@ -80,7 +80,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -102,7 +105,7 @@ }, { "cell_type": "markdown", - "id": "efd3bfc6", + "id": "97c52d4a", "metadata": {}, "source": [ "## Load difraction limited image (only needed if you want to use the FRC and Decorrelation analysis on this image or if you want to perform the Error Map analysis)\n", @@ -112,7 +115,7 @@ { "cell_type": "code", "execution_count": null, - "id": "de432d18", + "id": "5150b68b", "metadata": { "cellView": "form" }, @@ -126,7 +129,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data_df.add_label(\"Select data to use:\")\n", + " gui_data_df.add_label(value=\"Select data to use:\")\n", " gui_data_df.add_file_upload(\"upload\")\n", " gui_data_df.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -137,7 +140,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data_df.add_label(\"Select data to use:\")\n", + " gui_data_df.add_label(value=\"Select data to use:\")\n", " gui_data_df.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data_df.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -196,7 +199,7 @@ }, { "cell_type": "markdown", - "id": "2074fdd2", + "id": "ad95295a", "metadata": {}, "source": [ "## Load super-resolved image\n", @@ -206,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1942d9ad", + "id": "4b2f6fc0", "metadata": { "cellView": "form" }, @@ -220,7 +223,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data_sr.add_label(\"Select data to use:\")\n", + " gui_data_sr.add_label(value=\"Select data to use:\")\n", " gui_data_sr.add_file_upload(\"upload\")\n", " gui_data_sr.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -231,7 +234,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data_sr.add_label(\"Select data to use:\")\n", + " gui_data_sr.add_label(value=\"Select data to use:\")\n", " gui_data_sr.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data_sr.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -290,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "9158987c", + "id": "519bddc3", "metadata": {}, "source": [ "## Calculate Error Map\n", @@ -300,13 +303,13 @@ { "cell_type": "code", "execution_count": null, - "id": "2f078493", + "id": "8ce61944", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create Error Map GUI\n", + "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", "\n", "import numpy as np\n", @@ -315,6 +318,7 @@ "from matplotlib import pyplot as plt\n", "from nanopyx.core.transform import ErrorMap\n", "\n", + "\n", "def run_error(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -323,7 +327,9 @@ " gui_error[\"run\"].description = \"Calculating...\"\n", " global errormap\n", " error_map = ErrorMap()\n", - " error_map.optimise(np.mean(dataset_df, axis=0), np.mean(dataset_sr, axis=0))\n", + " error_map.optimise(\n", + " np.mean(dataset_df, axis=0), np.mean(dataset_sr, axis=0)\n", + " )\n", " gui_error[\"run\"].disabled = False\n", " gui_error[\"run\"].description = \"Calculate\"\n", " print(\"RSE: \", error_map.getRSE())\n", @@ -335,7 +341,9 @@ " name = gui_data_sr[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_error_map.tif\", errormap)\n", " else:\n", - " name = gui_data_sr[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data_sr[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map.tif\", errormap)\n", " plt.imshow(errormap)\n", " plt.axis(\"off\")\n", @@ -345,11 +353,13 @@ " with output_plot:\n", " display(Image.open(img_buf))\n", " gui_error._main_display.children = gui_error._main_display.children + (\n", - " widgets.Label(value=\"RSE: \"+str(error_map.getRSE())),\n", - " widgets.Label(value=\"RSP: \"+str(error_map.getRSP())),\n", - " output_plot)\n", + " widgets.Label(value=\"RSE: \" + str(error_map.getRSE())),\n", + " widgets.Label(value=\"RSP: \" + str(error_map.getRSP())),\n", + " output_plot,\n", + " )\n", " plt.clf()\n", "\n", + "\n", "def run_error_stack(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -363,27 +373,40 @@ " rse_rsp_table = []\n", "\n", " # Ensure datasets are 3D\n", - " if np.ndim(dataset_df) == 2: \n", - " dataset_df = np.expand_dims(dataset_df, axis=0) \n", - " if np.ndim(dataset_sr) == 2: \n", - " dataset_sr = np.expand_dims(dataset_sr, axis=0) \n", + " if np.ndim(dataset_df) == 2:\n", + " dataset_df = np.expand_dims(dataset_df, axis=0)\n", + " if np.ndim(dataset_sr) == 2:\n", + " dataset_sr = np.expand_dims(dataset_sr, axis=0)\n", + "\n", + " # Ensures datasets have same number of frames\n", + "\n", + " if dataset_df.shape[0] > dataset_sr.shape[0]:\n", + " factor = dataset_df.shape[0] // dataset_sr.shape[0]\n", + " remainder = dataset_df.shape[0] % dataset_sr.shape[0]\n", + " averaged_blocks = [\n", + " np.mean(dataset_df[i * factor : (i + 1) * factor], axis=0)\n", + " for i in range(dataset_sr.shape[0])\n", + " ]\n", + " if remainder > 0:\n", + " averaged_blocks.append(\n", + " np.mean(dataset_df[-remainder:], axis=0)\n", + " )\n", + " dataset_df = np.array(averaged_blocks)\n", "\n", " # Iterate through each slice\n", " print(\"Processing slices...\")\n", - " for i in tqdm(range(dataset_df.shape[0]),desc=\"Slices processed\"): \n", - " slice_df = dataset_df[i] # \n", - " slice_sr = dataset_sr[i] # \n", - " \n", + " for i in tqdm(range(dataset_df.shape[0]), desc=\"Slices processed\"):\n", + " slice_df = dataset_df[i] #\n", + " slice_sr = dataset_sr[i] #\n", + "\n", " error_map = ErrorMap()\n", " error_map.optimise(slice_df, slice_sr)\n", "\n", " # Store the error map and RSE/RSP values\n", " errormap_stack.append(np.array(error_map.imRSE))\n", - " rse_rsp_table.append({\n", - " \"Slice\": i,\n", - " \"RSE\": error_map.getRSE(),\n", - " \"RSP\": error_map.getRSP()\n", - " })\n", + " rse_rsp_table.append(\n", + " {\"Slice\": i, \"RSE\": error_map.getRSE(), \"RSP\": error_map.getRSP()}\n", + " )\n", "\n", " # Convert results to arrays\n", " errormap_stack = np.array(errormap_stack) # 3D stack of error maps\n", @@ -393,10 +416,16 @@ " if own_data_sr:\n", " path = gui_data_sr[\"upload\"].selected_path\n", " name = gui_data_sr[\"upload\"].selected_filename.split(\".\")[0]\n", - " tiff.imwrite(path + os.sep + name + \"_error_map_stack.tif\", errormap_stack)\n", - " rse_rsp_table.to_csv(path + os.sep + name + \"_rse_rsp_table.csv\", index=False)\n", + " tiff.imwrite(\n", + " path + os.sep + name + \"_error_map_stack.tif\", errormap_stack\n", + " )\n", + " rse_rsp_table.to_csv(\n", + " path + os.sep + name + \"_rse_rsp_table.csv\", index=False\n", + " )\n", " else:\n", - " name = gui_data_sr[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data_sr[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map_stack.tif\", errormap_stack)\n", " rse_rsp_table.to_csv(name + \"_rse_rsp_table.csv\", index=False)\n", "\n", @@ -413,9 +442,13 @@ "\n", "\n", "gui_error.add_checkbox(\"save\", description=\"Save output\", value=True)\n", - "gui_error.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_error.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_error.add_button(\"run\", description=\"Calculate\")\n", "gui_error[\"run\"].on_click(run_error_stack)\n", "gui_error.show()\n", @@ -425,7 +458,7 @@ }, { "cell_type": "markdown", - "id": "cf64d5d3", + "id": "fca24754", "metadata": {}, "source": [ "# Calculate FRC of the diffraction limited image\n", @@ -441,7 +474,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1dfcdb21", + "id": "470574d5", "metadata": { "cellView": "form" }, @@ -487,7 +520,7 @@ " gui_frc_df._main_display.children = gui_frc_df._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_df.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_df[0].shape[0]-1, value=0)\n", "gui_frc_df.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_df[0].shape[0]-1, value=1)\n", @@ -501,7 +534,7 @@ }, { "cell_type": "markdown", - "id": "733c185c", + "id": "01ae19c8", "metadata": {}, "source": [ "# Calculate FRC of the SR image\n", @@ -517,7 +550,7 @@ { "cell_type": "code", "execution_count": null, - "id": "83e6929a", + "id": "959d4bee", "metadata": { "cellView": "form" }, @@ -563,7 +596,7 @@ " gui_frc_sr._main_display.children = gui_frc_sr._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_sr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_sr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_sr.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_sr.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_sr[0].shape[0]-1, value=0)\n", "gui_frc_sr.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_sr[0].shape[0]-1, value=1)\n", @@ -577,7 +610,7 @@ }, { "cell_type": "markdown", - "id": "d881c533", + "id": "98a5483d", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of diffraction limited image\n", @@ -594,7 +627,7 @@ { "cell_type": "code", "execution_count": null, - "id": "bb67c169", + "id": "18d3c9f9", "metadata": { "cellView": "form" }, @@ -640,7 +673,7 @@ " gui_decorr_df._main_display.children = gui_decorr_df._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_df.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_df.shape[0]-1, value=0)\n", "gui_decorr_df.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -655,7 +688,7 @@ }, { "cell_type": "markdown", - "id": "320c5ae7", + "id": "44579837", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of SR image\n", @@ -672,7 +705,7 @@ { "cell_type": "code", "execution_count": null, - "id": "77057b97", + "id": "2bca37a6", "metadata": { "cellView": "form" }, @@ -718,7 +751,7 @@ " gui_decorr_sr._main_display.children = gui_decorr_sr._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_sr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_sr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_sr.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_sr.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_sr.shape[0]-1, value=0)\n", "gui_decorr_sr.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", diff --git a/notebooks/SRRFandQC.ipynb b/notebooks/SRRFandQC.ipynb index 2cf3c7bc..bc0359c4 100644 --- a/notebooks/SRRFandQC.ipynb +++ b/notebooks/SRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "17e14260", + "id": "962fcc63", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7acbf83c", + "id": "cd7f7bb3", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8315ab2c", + "id": "fa0992f8", "metadata": { "cellView": "form" }, @@ -82,7 +82,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -105,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5aeb0a4c", + "id": "182483ae", "metadata": { "cellView": "form" }, @@ -119,7 +122,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -130,7 +133,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -189,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "09c3f069", + "id": "b043a5f0", "metadata": {}, "source": [ "# Use SRRF to generate a super-resolved image\n", @@ -206,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85ced68d", + "id": "c2908c91", "metadata": { "cellView": "form" }, @@ -257,10 +260,10 @@ " tiff.imwrite(name + \"_srrf.tif\", dataset_srrf)\n", " gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf[\"cmaps\"].value, continuous_update=True),)\n", "\n", - "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5, remember_value=True)\n", + "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5)\n", "gui_srrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=5)\n", "gui_srrf.add_int_slider(\"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3)\n", - "gui_srrf.add_label(\"-=-= Time-Lapse =-=-\")\n", + "gui_srrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", "gui_srrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", "gui_srrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", "gui_srrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -275,7 +278,7 @@ }, { "cell_type": "markdown", - "id": "1eadfed0", + "id": "9e1b875f", "metadata": {}, "source": [ "## Calculate error map for the SRRF image\n", @@ -285,13 +288,13 @@ { "cell_type": "code", "execution_count": null, - "id": "aeb89a7c", + "id": "a31a529c", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create Error Map GUI\n", + "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", "\n", "import numpy as np\n", @@ -300,6 +303,7 @@ "from matplotlib import pyplot as plt\n", "from nanopyx.core.transform import ErrorMap\n", "\n", + "\n", "def run_error(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -308,7 +312,9 @@ " gui_error[\"run\"].description = \"Calculating...\"\n", " global errormap\n", " error_map = ErrorMap()\n", - " error_map.optimise(np.mean(dataset_original, axis=0), np.mean(dataset_srrf, axis=0))\n", + " error_map.optimise(\n", + " np.mean(dataset_original, axis=0), np.mean(dataset_srrf, axis=0)\n", + " )\n", " gui_error[\"run\"].disabled = False\n", " gui_error[\"run\"].description = \"Calculate\"\n", " print(\"RSE: \", error_map.getRSE())\n", @@ -320,7 +326,9 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_error_map.tif\", errormap)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map.tif\", errormap)\n", " plt.imshow(errormap)\n", " plt.axis(\"off\")\n", @@ -330,11 +338,13 @@ " with output_plot:\n", " display(Image.open(img_buf))\n", " gui_error._main_display.children = gui_error._main_display.children + (\n", - " widgets.Label(value=\"RSE: \"+str(error_map.getRSE())),\n", - " widgets.Label(value=\"RSP: \"+str(error_map.getRSP())),\n", - " output_plot)\n", + " widgets.Label(value=\"RSE: \" + str(error_map.getRSE())),\n", + " widgets.Label(value=\"RSP: \" + str(error_map.getRSP())),\n", + " output_plot,\n", + " )\n", " plt.clf()\n", "\n", + "\n", "def run_error_stack(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -348,27 +358,40 @@ " rse_rsp_table = []\n", "\n", " # Ensure datasets are 3D\n", - " if np.ndim(dataset_original) == 2: \n", - " dataset_original = np.expand_dims(dataset_original, axis=0) \n", - " if np.ndim(dataset_srrf) == 2: \n", - " dataset_srrf = np.expand_dims(dataset_srrf, axis=0) \n", + " if np.ndim(dataset_original) == 2:\n", + " dataset_original = np.expand_dims(dataset_original, axis=0)\n", + " if np.ndim(dataset_srrf) == 2:\n", + " dataset_srrf = np.expand_dims(dataset_srrf, axis=0)\n", + "\n", + " # Ensures datasets have same number of frames\n", + "\n", + " if dataset_original.shape[0] > dataset_srrf.shape[0]:\n", + " factor = dataset_original.shape[0] // dataset_srrf.shape[0]\n", + " remainder = dataset_original.shape[0] % dataset_srrf.shape[0]\n", + " averaged_blocks = [\n", + " np.mean(dataset_original[i * factor : (i + 1) * factor], axis=0)\n", + " for i in range(dataset_srrf.shape[0])\n", + " ]\n", + " if remainder > 0:\n", + " averaged_blocks.append(\n", + " np.mean(dataset_original[-remainder:], axis=0)\n", + " )\n", + " dataset_original = np.array(averaged_blocks)\n", "\n", " # Iterate through each slice\n", " print(\"Processing slices...\")\n", - " for i in tqdm(range(dataset_original.shape[0]),desc=\"Slices processed\"): \n", - " slice_df = dataset_original[i] # \n", - " slice_sr = dataset_srrf[i] # \n", - " \n", + " for i in tqdm(range(dataset_original.shape[0]), desc=\"Slices processed\"):\n", + " slice_df = dataset_original[i] #\n", + " slice_sr = dataset_srrf[i] #\n", + "\n", " error_map = ErrorMap()\n", " error_map.optimise(slice_df, slice_sr)\n", "\n", " # Store the error map and RSE/RSP values\n", " errormap_stack.append(np.array(error_map.imRSE))\n", - " rse_rsp_table.append({\n", - " \"Slice\": i,\n", - " \"RSE\": error_map.getRSE(),\n", - " \"RSP\": error_map.getRSP()\n", - " })\n", + " rse_rsp_table.append(\n", + " {\"Slice\": i, \"RSE\": error_map.getRSE(), \"RSP\": error_map.getRSP()}\n", + " )\n", "\n", " # Convert results to arrays\n", " errormap_stack = np.array(errormap_stack) # 3D stack of error maps\n", @@ -378,10 +401,16 @@ " if own_data:\n", " path = gui_data[\"upload\"].selected_path\n", " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", - " tiff.imwrite(path + os.sep + name + \"_error_map_stack.tif\", errormap_stack)\n", - " rse_rsp_table.to_csv(path + os.sep + name + \"_rse_rsp_table.csv\", index=False)\n", + " tiff.imwrite(\n", + " path + os.sep + name + \"_error_map_stack.tif\", errormap_stack\n", + " )\n", + " rse_rsp_table.to_csv(\n", + " path + os.sep + name + \"_rse_rsp_table.csv\", index=False\n", + " )\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map_stack.tif\", errormap_stack)\n", " rse_rsp_table.to_csv(name + \"_rse_rsp_table.csv\", index=False)\n", "\n", @@ -398,9 +427,13 @@ "\n", "\n", "gui_error.add_checkbox(\"save\", description=\"Save output\", value=True)\n", - "gui_error.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_error.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_error.add_button(\"run\", description=\"Calculate\")\n", "gui_error[\"run\"].on_click(run_error_stack)\n", "gui_error.show()\n", @@ -410,7 +443,7 @@ }, { "cell_type": "markdown", - "id": "a1272841", + "id": "174a3d86", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -426,7 +459,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f62cd72a", + "id": "43f55d4d", "metadata": { "cellView": "form" }, @@ -472,7 +505,7 @@ " gui_frc_df._main_display.children = gui_frc_df._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_df.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=0)\n", "gui_frc_df.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=1)\n", @@ -486,7 +519,7 @@ }, { "cell_type": "markdown", - "id": "bf0fa835", + "id": "fd610c51", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -502,7 +535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "06e8367f", + "id": "63557a6e", "metadata": { "cellView": "form" }, @@ -548,7 +581,7 @@ " gui_frc_srrf._main_display.children = gui_frc_srrf._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_srrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_srrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_srrf.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_srrf[0].shape[0]-1, value=0)\n", "gui_frc_srrf.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_srrf[0].shape[0]-1, value=1)\n", @@ -562,7 +595,7 @@ }, { "cell_type": "markdown", - "id": "fb9621fc", + "id": "5b56f6d3", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -579,7 +612,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14d30daa", + "id": "3d485c4d", "metadata": { "cellView": "form" }, @@ -625,7 +658,7 @@ " gui_decorr_df._main_display.children = gui_decorr_df._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_df.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_df.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_df.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_original.shape[0]-1, value=0)\n", "gui_decorr_df.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -640,7 +673,7 @@ }, { "cell_type": "markdown", - "id": "a051a495", + "id": "be5b0abc", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -657,7 +690,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8dc2fbb2", + "id": "3f161a44", "metadata": { "cellView": "form" }, @@ -703,7 +736,7 @@ " gui_decorr._main_display.children = gui_decorr._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_srrf.shape[0]-1, value=0)\n", "gui_decorr.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", diff --git a/notebooks/eSRRFandQC.ipynb b/notebooks/eSRRFandQC.ipynb index a0d5abd6..cc6ae039 100644 --- a/notebooks/eSRRFandQC.ipynb +++ b/notebooks/eSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "d30993c0", + "id": "ff65de9b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c035a99e", + "id": "66cb1942", "metadata": { "cellView": "form" }, @@ -49,12 +49,28 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "a0d233e1", + "execution_count": 1, + "id": "c4f9e0b7", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/miniconda3/envs/aiobio/lib/python3.11/site-packages/pytools/persistent_dict.py:52: RecommendedHashNotFoundWarning: Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.\n", + " warn(\"Unable to import recommended hash 'siphash24.siphash13', \"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cupy implementation is not available. Make sure you have the right version of Cupy and CUDA installed.\n" + ] + } + ], "source": [ "#@title Install NanoPyx, import necessary libraries and connect to Google Drive\n", "import sys\n", @@ -82,7 +98,10 @@ "from IPython.display import display, clear_output\n", "from matplotlib import pyplot as plt\n", "\n", - "from nanopyx.core.utils.easy_gui import EasyGui\n", + "try:\n", + " from ezinput import EZInput as EasyGui\n", + "except ImportError:\n", + " from nanopyx.core.utils.easy_gui import EasyGui\n", "from nanopyx.core.utils.find_files import find_files\n", "from nanopyx.data.download import ExampleDataManager\n", "\n", @@ -105,11 +124,26 @@ { "cell_type": "code", "execution_count": null, - "id": "82f1b92d", + "id": "b2dc1a3c", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a57a99595a594f70afe42a3f7083f807", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Button(description='Use Own data', layout=Layout(width='50%'), style=ButtonStyle()), Button(des…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title Load image stack\n", "# Create a GUI\n", @@ -119,7 +153,7 @@ "\n", "def on_button_select_own(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_file_upload(\"upload\")\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", " options=sorted(list(mpl.colormaps)),\n", @@ -130,7 +164,7 @@ "\n", "def on_button_select_example(b):\n", " clear_output()\n", - " gui_data.add_label(\"Select data to use:\")\n", + " gui_data.add_label(value=\"Select data to use:\")\n", " gui_data.add_dropdown(\"data_source\", options=image_files,\n", " value=\"Example dataset: \"+example_datasets[4], remember_value=True)\n", " gui_data.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", @@ -189,7 +223,7 @@ }, { "cell_type": "markdown", - "id": "9aff14d9", + "id": "ff0fed51", "metadata": {}, "source": [ "# Use eSRRF to generate a super-resolved image\n", @@ -205,17 +239,35 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "4f7338cf", + "execution_count": 3, + "id": "667d6292", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "0bc7997ab78f48699f1a3e53ddce5ab3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(FloatSlider(value=1.5, description='Ring Radius:', layout=Layout(width='50%'), max=3.0, min=0.1…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "#@title Create eSRRF GUI\n", + "# @title Create eSRRF GUI\n", "gui_esrrf = EasyGui(\"esrrf\")\n", "from nanopyx.methods import eSRRF\n", - "from nanopyx.core.transform.sr_temporal_correlations import calculate_eSRRF_temporal_correlations\n", + "from nanopyx.core.transform.sr_temporal_correlations import (\n", + " calculate_eSRRF_temporal_correlations,\n", + ")\n", + "\n", "\n", "def run_esrrf(b):\n", " clear_output()\n", @@ -241,14 +293,22 @@ " elif frames_per_timepoint > dataset_original.shape[0]:\n", " frames_per_timepoint = dataset_original.shape[0]\n", "\n", - " output= []\n", + " output = []\n", "\n", " for i in range(dataset_original.shape[0] // frames_per_timepoint):\n", - " block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint]\n", - " result = eSRRF(block, magnification=magnification, radius=ring_radius,\n", - " sensitivity=sensitivity,\n", - " doIntensityWeighting=True)\n", - " output.append(calculate_eSRRF_temporal_correlations(result[0], esrrf_order))\n", + " block = dataset_original[\n", + " i * frames_per_timepoint : (i + 1) * frames_per_timepoint\n", + " ]\n", + " result = eSRRF(\n", + " block,\n", + " magnification=magnification,\n", + " radius=ring_radius,\n", + " sensitivity=sensitivity,\n", + " doIntensityWeighting=True,\n", + " )\n", + " output.append(\n", + " calculate_eSRRF_temporal_correlations(result[0], esrrf_order)\n", + " )\n", "\n", " global dataset_esrrf\n", " dataset_esrrf = np.array(output)\n", @@ -261,27 +321,69 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_esrrf.tif\", dataset_esrrf)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_esrrf.tif\", dataset_esrrf)\n", - " gui_esrrf._main_display.children = gui_esrrf._main_display.children + (stackview.slice(dataset_esrrf, colormap=gui_esrrf[\"cmaps\"].value, continuous_update=True),)\n", + " gui_esrrf._main_display.children = gui_esrrf._main_display.children + (\n", + " stackview.slice(\n", + " dataset_esrrf,\n", + " colormap=gui_esrrf[\"cmaps\"].value,\n", + " continuous_update=True,\n", + " ),\n", + " )\n", "\n", "\n", "default_radius = 1.5\n", "default_sensitivity = 1\n", "default_magnification = 5\n", "default_esrrf_order = 1\n", - "gui_esrrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=default_radius, remember_value=True)\n", - "gui_esrrf.add_int_slider(\"sensitivity\", description=\"Sensitivity:\", min=1, max=10, value=default_sensitivity)\n", - "gui_esrrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=default_magnification)\n", - "gui_esrrf.add_int_slider(\"esrrf_order\", description=\"eSRRF order:\", min=1, max=3, value=default_esrrf_order)\n", - "gui_esrrf.add_label(\"-=-= Time-Lapse =-=-\")\n", - "gui_esrrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=0, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", + "gui_esrrf.add_float_slider(\n", + " \"ring_radius\",\n", + " description=\"Ring Radius:\",\n", + " min=0.1,\n", + " max=3.0,\n", + " value=default_radius,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"sensitivity\",\n", + " description=\"Sensitivity:\",\n", + " min=1,\n", + " max=10,\n", + " value=default_sensitivity,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"magnification\",\n", + " description=\"Magnification:\",\n", + " min=1,\n", + " max=10,\n", + " value=default_magnification,\n", + ")\n", + "gui_esrrf.add_int_slider(\n", + " \"esrrf_order\",\n", + " description=\"eSRRF order:\",\n", + " min=1,\n", + " max=3,\n", + " value=default_esrrf_order,\n", + ")\n", + "gui_esrrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", + "gui_esrrf.add_int_slider(\n", + " \"frames_per_timepoint\",\n", + " description=\"Frames per time-point (0 - auto)\",\n", + " min=0,\n", + " max=dataset_original.shape[0],\n", + " value=dataset_original.shape[0] // 2,\n", + ")\n", "gui_esrrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", - "gui_esrrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_esrrf.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_esrrf.add_button(\"run\", description=\"Run\")\n", - "gui_esrrf['run'].on_click(run_esrrf)\n", + "gui_esrrf[\"run\"].on_click(run_esrrf)\n", "gui_esrrf.show()\n", "\n", "\n" @@ -289,7 +391,7 @@ }, { "cell_type": "markdown", - "id": "09c8f79f", + "id": "78fa1296", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -298,14 +400,100 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "88af925f", + "execution_count": 4, + "id": "ee3e878c", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d41fb6aafc6b494783d6f6a5564dfc34", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(Checkbox(value=True, description='Save output', layout=Layout(width='50%'), style=CheckboxStyle…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SliceRSERSP
0049.8408550.931586
1145.9631310.936541
\n", + "
" + ], + "text/plain": [ + " Slice RSE RSP\n", + "0 0 49.840855 0.931586\n", + "1 1 45.963131 0.936541" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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/aW9/HBp7R+f28P759a+O7a7739r3x8e23sjeq21D8f7dnm5b4+/7drPP92344dD2/x0aHe/GAM0iS7ec75v7fRwac9/urTtn57bww9P7cd//NS+e/jN27rfHtunj39qv378rv366317+pf3bfvbtu0/GAFr7by/tPNda8cfz23z/UvbHs5tuzu382nbzs/bdv64b4efdm3/cdP2H1s7/HZpu+eY53HjTS5hjsN8yrz5PNnPvrXT/aYd37V2fH9pz/90bvt/eG4P3z+1P/3jx/bju1/aPxwe25+sE+3SXi779ul0aD+9vG+/He/a8eW+vTy9ax+f7trT87Z9+vW+bT7u2/Zx0/Y23o/NCcnW94ivoHfivGvtfGg+By/fX9rx3xzb3Y9P7bt/99j+w7/9z+1/8+7P7X93/5/bw+bUfj4/tP9y/KH9p78+tP/y54f201/etd3/7749/LV5+/tPIFS47FDLNDgxtffGuI0hxLuP7zbtZOv4rrWX787tcndp5/tLawcQ+/OmbZ62bfdp07ZPm7b/tGm7TzEWk7BtXPmKQ+w1H893rZ2+O7fzw6Vdfji2d98/te8eTu3795/aP/3wl/Y/u/+5/Ye7v7X/xf5v7bA5NTsp/+n4j+1//PTv2/HjP7UPf/6+ffznd+3y677d/bRth19jDm2M7L8fcmdefS1z/+9aO35/aef3p9bs/f/+t/bDjz+1//jDP7f/w3f/Q/uP+1P70zbm68/nU/tPx/ft//b4H9qf/7Zvf/ll137623ft8ueHtvP127TDb6HJhMbWyjnvTEIZg//un3UGMnxXyEHe93syBdIMSO16PvxvY6ZGN2wtHzbt+cc4F3bO7Wxc9sFINi/YC48xN4dfLy6c7J9a27nQF80u5Nwy7sWY3zo27but/yY0wBD4jMHFfg9BqDkdDO2wfV2mYKq+SZQv39nB3rTNKYiNH3zr5BmjP2MFNrph1tld8jRKPCnl1r9xP6XfWbO43w+QTZZPWJ/zy3nTzudNO5237em8b4/nfdueL23bLu35vGsv551LxJfLxoVMX2BuKiA3SVQ3l7bZXtp2e26H7bm93z23H/aP7f32uf169+Cax4eXu/Z09xCS5P7Sti/RmB8mU1COW58unwU/QICyymFarIV8l/MTzeS0DNOu88h5kWdMA7H1vJxibo7nnf+c8JBJtbt2bvst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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "#@title Create Error Map GUI\n", + "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", "\n", "import numpy as np\n", @@ -314,6 +502,7 @@ "from matplotlib import pyplot as plt\n", "from nanopyx.core.transform import ErrorMap\n", "\n", + "\n", "def run_error(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -322,7 +511,9 @@ " gui_error[\"run\"].description = \"Calculating...\"\n", " global errormap\n", " error_map = ErrorMap()\n", - " error_map.optimise(np.mean(dataset_original, axis=0), np.mean(dataset_esrrf, axis=0))\n", + " error_map.optimise(\n", + " np.mean(dataset_original, axis=0), np.mean(dataset_esrrf, axis=0)\n", + " )\n", " gui_error[\"run\"].disabled = False\n", " gui_error[\"run\"].description = \"Calculate\"\n", " print(\"RSE: \", error_map.getRSE())\n", @@ -334,7 +525,9 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_error_map.tif\", errormap)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map.tif\", errormap)\n", " plt.imshow(errormap)\n", " plt.axis(\"off\")\n", @@ -344,11 +537,13 @@ " with output_plot:\n", " display(Image.open(img_buf))\n", " gui_error._main_display.children = gui_error._main_display.children + (\n", - " widgets.Label(value=\"RSE: \"+str(error_map.getRSE())),\n", - " widgets.Label(value=\"RSP: \"+str(error_map.getRSP())),\n", - " output_plot)\n", + " widgets.Label(value=\"RSE: \" + str(error_map.getRSE())),\n", + " widgets.Label(value=\"RSP: \" + str(error_map.getRSP())),\n", + " output_plot,\n", + " )\n", " plt.clf()\n", "\n", + "\n", "def run_error_stack(b):\n", " clear_output()\n", " gui_error.show()\n", @@ -362,27 +557,40 @@ " rse_rsp_table = []\n", "\n", " # Ensure datasets are 3D\n", - " if np.ndim(dataset_original) == 2: \n", - " dataset_original = np.expand_dims(dataset_original, axis=0) \n", - " if np.ndim(dataset_esrrf) == 2: \n", - " dataset_esrrf = np.expand_dims(dataset_esrrf, axis=0) \n", + " if np.ndim(dataset_original) == 2:\n", + " dataset_original = np.expand_dims(dataset_original, axis=0)\n", + " if np.ndim(dataset_esrrf) == 2:\n", + " dataset_esrrf = np.expand_dims(dataset_esrrf, axis=0)\n", + "\n", + " # Ensures datasets have same number of frames\n", + "\n", + " if dataset_original.shape[0] > dataset_esrrf.shape[0]:\n", + " factor = dataset_original.shape[0] // dataset_esrrf.shape[0]\n", + " remainder = dataset_original.shape[0] % dataset_esrrf.shape[0]\n", + " averaged_blocks = [\n", + " np.mean(dataset_original[i * factor : (i + 1) * factor], axis=0)\n", + " for i in range(dataset_esrrf.shape[0])\n", + " ]\n", + " if remainder > 0:\n", + " averaged_blocks.append(\n", + " np.mean(dataset_original[-remainder:], axis=0)\n", + " )\n", + " dataset_original = np.array(averaged_blocks)\n", "\n", " # Iterate through each slice\n", " print(\"Processing slices...\")\n", - " for i in tqdm(range(dataset_original.shape[0]),desc=\"Slices processed\"): \n", - " slice_df = dataset_original[i] # \n", - " slice_sr = dataset_esrrf[i] # \n", - " \n", + " for i in tqdm(range(dataset_original.shape[0]), desc=\"Slices processed\"):\n", + " slice_df = dataset_original[i] #\n", + " slice_sr = dataset_esrrf[i] #\n", + "\n", " error_map = ErrorMap()\n", " error_map.optimise(slice_df, slice_sr)\n", "\n", " # Store the error map and RSE/RSP values\n", " errormap_stack.append(np.array(error_map.imRSE))\n", - " rse_rsp_table.append({\n", - " \"Slice\": i,\n", - " \"RSE\": error_map.getRSE(),\n", - " \"RSP\": error_map.getRSP()\n", - " })\n", + " rse_rsp_table.append(\n", + " {\"Slice\": i, \"RSE\": error_map.getRSE(), \"RSP\": error_map.getRSP()}\n", + " )\n", "\n", " # Convert results to arrays\n", " errormap_stack = np.array(errormap_stack) # 3D stack of error maps\n", @@ -392,10 +600,16 @@ " if own_data:\n", " path = gui_data[\"upload\"].selected_path\n", " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", - " tiff.imwrite(path + os.sep + name + \"_error_map_stack.tif\", errormap_stack)\n", - " rse_rsp_table.to_csv(path + os.sep + name + \"_rse_rsp_table.csv\", index=False)\n", + " tiff.imwrite(\n", + " path + os.sep + name + \"_error_map_stack.tif\", errormap_stack\n", + " )\n", + " rse_rsp_table.to_csv(\n", + " path + os.sep + name + \"_rse_rsp_table.csv\", index=False\n", + " )\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_error_map_stack.tif\", errormap_stack)\n", " rse_rsp_table.to_csv(name + \"_rse_rsp_table.csv\", index=False)\n", "\n", @@ -412,9 +626,13 @@ "\n", "\n", "gui_error.add_checkbox(\"save\", description=\"Save output\", value=True)\n", - "gui_error.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_error.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_error.add_button(\"run\", description=\"Calculate\")\n", "gui_error[\"run\"].on_click(run_error_stack)\n", "gui_error.show()\n", @@ -424,7 +642,7 @@ }, { "cell_type": "markdown", - "id": "e08b5c91", + "id": "edbc1fee", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -439,12 +657,36 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "816b51e1", + "execution_count": 5, + "id": "4464be04", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "814291cbb1a64dea9413bed409e4ed85", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title create FRC GUI for original image\n", "gui_frc = EasyGui(\"FRC\")\n", @@ -486,7 +728,7 @@ " gui_frc._main_display.children = gui_frc._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=0)\n", "gui_frc.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_original[0].shape[0]-1, value=1)\n", @@ -500,7 +742,7 @@ }, { "cell_type": "markdown", - "id": "aa0a8a80", + "id": "282970c2", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -516,11 +758,35 @@ { "cell_type": "code", "execution_count": null, - "id": "129e9f86", + "id": "fbc8f076", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7e72195723d1414382edef1d1ac7ec9d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title create FRC GUI for SR image\n", "gui_frc_esrrf = EasyGui(\"FRC\")\n", @@ -562,7 +828,7 @@ " gui_frc_esrrf._main_display.children = gui_frc_esrrf._main_display.children + (output_plot,)\n", " plt.clf()\n", " \n", - "gui_frc_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_frc_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_frc_esrrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_frc_esrrf.add_int_slider(\"first_frame\", description=\"First Frame:\", min=0, max=dataset_esrrf[0].shape[0]-1, value=0)\n", "gui_frc_esrrf.add_int_slider (\"second_frame\", description=\"Second Frame:\", min=0, max=dataset_esrrf[0].shape[0]-1, value=1)\n", @@ -576,7 +842,7 @@ }, { "cell_type": "markdown", - "id": "b3b15b1e", + "id": "b4b4f51c", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -592,12 +858,36 @@ }, { "cell_type": "code", - "execution_count": null, - "id": "5bf7dcf9", + "execution_count": 7, + "id": "3a08bd98", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4d9274dbe5da4ee5a642a91108633996", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title Create Decorrelation Analysis GUI for eSRRF data\n", "gui_decorr = EasyGui(\"DecorrAnalysis\")\n", @@ -639,7 +929,7 @@ " gui_decorr._main_display.children = gui_decorr._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_original.shape[0]-1, value=0)\n", "gui_decorr.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -654,7 +944,7 @@ }, { "cell_type": "markdown", - "id": "55b06686", + "id": "19a5a204", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -671,11 +961,35 @@ { "cell_type": "code", "execution_count": null, - "id": "1e4a92ee", + "id": "d2779b72", "metadata": { "cellView": "form" }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "67ebb4524eac4cbf968f67520ac2cab3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "#@title Create Decorrelation Analysis GUI for eSRRF data\n", "gui_decorr_esrrf = EasyGui(\"DecorrAnalysis\")\n", @@ -717,7 +1031,7 @@ " gui_decorr_esrrf._main_display.children = gui_decorr_esrrf._main_display.children + (output_plot,)\n", " plt.clf()\n", "\n", - "gui_decorr_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100, remember_value=True)\n", + "gui_decorr_esrrf.add_int_slider(\"pixel_size\", description=\"Pixel Size:\", min=0.01, max=1000, value=100)\n", "gui_decorr_esrrf.add_dropdown(\"units\", description=\"Units: \", options=[\"nm\", \"um\", \"mm\"], value=\"nm\")\n", "gui_decorr_esrrf.add_int_slider(\"first_frame\", description=\"Frame to be used:\", min=0, max=dataset_esrrf.shape[0]-1, value=0)\n", "gui_decorr_esrrf.add_float_slider(\"rmin\", description=\"Radius Min:\", min=0.0, max=0.5, value=0.0)\n", @@ -730,7 +1044,25 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "aiobio", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, "nbformat": 4, "nbformat_minor": 5 } diff --git a/pyproject.toml b/pyproject.toml index 7e0e8090..8dc3c061 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ dependencies = [ "scikit-learn>=1.1.0", "matplotlib>=3.5", "importlib-resources", + "ezinput>=0.0.2", ] dynamic = ["version"] # changed in setup.py diff --git a/src/notebookchef/pantry/image_loading.py b/src/notebookchef/pantry/image_loading.py index 0078c682..a8c27b9c 100644 --- a/src/notebookchef/pantry/image_loading.py +++ b/src/notebookchef/pantry/image_loading.py @@ -6,7 +6,7 @@ def on_button_select_own(b): clear_output() - gui_data.add_label("Select data to use:") + gui_data.add_label(value="Select data to use:") gui_data.add_file_upload("upload") gui_data.add_dropdown("cmaps", description="Colormap:", options=sorted(list(mpl.colormaps)), @@ -17,7 +17,7 @@ def on_button_select_own(b): def on_button_select_example(b): clear_output() - gui_data.add_label("Select data to use:") + gui_data.add_label(value="Select data to use:") gui_data.add_dropdown("data_source", options=image_files, value="Example dataset: "+example_datasets[4], remember_value=True) gui_data.add_dropdown("cmaps", description="Colormap:", diff --git a/src/notebookchef/pantry/methods/channel_registration.py b/src/notebookchef/pantry/methods/channel_registration.py index 26ef263e..2bab319a 100644 --- a/src/notebookchef/pantry/methods/channel_registration.py +++ b/src/notebookchef/pantry/methods/channel_registration.py @@ -42,7 +42,7 @@ def on_button_register(b): gui_reg._main_display.children = gui_reg._main_display.children + (stackview.slice(dataset_registered, colormap=gui_reg["cmaps"].value, continuous_update=True),) -gui_reg.add_label("Channel Registration parameters:") +gui_reg.add_label(value="Channel Registration parameters:") gui_reg.add_int_slider("ref", description="Reference channel", min=0, max=dataset_original.shape[0]-1, value=0) gui_reg.add_int_slider("max", description="Max expected drift", min=0, max=1000, value=10) gui_reg.add_int_slider("blocks", description="Blocks per axis", min=1, max=10, value=5) diff --git a/src/notebookchef/pantry/methods/channel_registration_apply.py b/src/notebookchef/pantry/methods/channel_registration_apply.py index 17be0de8..994b9f24 100644 --- a/src/notebookchef/pantry/methods/channel_registration_apply.py +++ b/src/notebookchef/pantry/methods/channel_registration_apply.py @@ -47,9 +47,9 @@ def on_button_apply(b): gui_reg_apply["Register Image"].description = "Align" gui_reg_apply._main_display.children = gui_reg_apply._main_display.children + (stackview.slice(aligned_image, colormap=gui_reg_apply["cmaps"].value, continuous_update=True),) -gui_reg_apply.add_label("Load translation mask:") +gui_reg_apply.add_label(value="Load translation mask:") gui_reg_apply.add_file_upload("upload") -gui_reg_apply.add_label("Load image to register:") +gui_reg_apply.add_label(value="Load image to register:") gui_reg_apply.add_file_upload("upload image") gui_reg_apply.add_dropdown("cmaps", description="Colormap:", options=sorted(list(mpl.colormaps)), diff --git a/src/notebookchef/pantry/methods/decorr.py b/src/notebookchef/pantry/methods/decorr.py index 65f187b8..58ce131f 100644 --- a/src/notebookchef/pantry/methods/decorr.py +++ b/src/notebookchef/pantry/methods/decorr.py @@ -38,7 +38,7 @@ def run_decorr(b): gui_decorr._main_display.children = gui_decorr._main_display.children + (output_plot,) plt.clf() -gui_decorr.add_int_slider("pixel_size", description="Pixel Size:", min=0.01, max=1000, value=100, remember_value=True) +gui_decorr.add_int_slider("pixel_size", description="Pixel Size:", min=0.01, max=1000, value=100) gui_decorr.add_dropdown("units", description="Units: ", options=["nm", "um", "mm"], value="nm") gui_decorr.add_int_slider("first_frame", description="Frame to be used:", min=0, max=dataset_original.shape[0]-1, value=0) gui_decorr.add_float_slider("rmin", description="Radius Min:", min=0.0, max=0.5, value=0.0) diff --git a/src/notebookchef/pantry/methods/drift_correction.py b/src/notebookchef/pantry/methods/drift_correction.py index 041de214..7641fbf2 100644 --- a/src/notebookchef/pantry/methods/drift_correction.py +++ b/src/notebookchef/pantry/methods/drift_correction.py @@ -44,7 +44,7 @@ def on_button_align(b): gui_drift["align"].description = "Align" gui_drift._main_display.children = gui_drift._main_display.children + (stackview.slice(dataset_aligned, colormap=gui_drift["cmaps"].value, continuous_update=True),) -gui_drift.add_label("Drift Correction parameters:") +gui_drift.add_label(value="Drift Correction parameters:") gui_drift.add_dropdown("ref", description="Reference frame", options=["First frame", "Previous frame"], value="First frame") gui_drift.add_int_slider("max", description="Max expected drift", min=0, max=1000, value=10) gui_drift.add_int_slider("time_averaging", description="Time averaging", min=1, max=dataset_original.shape[0], value=1) diff --git a/src/notebookchef/pantry/methods/drift_correction_apply.py b/src/notebookchef/pantry/methods/drift_correction_apply.py index ba33431d..3955e9c5 100644 --- a/src/notebookchef/pantry/methods/drift_correction_apply.py +++ b/src/notebookchef/pantry/methods/drift_correction_apply.py @@ -49,9 +49,9 @@ def on_button_apply(b): gui_drift_apply["Align image"].description = "Align" gui_drift_apply._main_display.children = gui_drift_apply._main_display.children + (stackview.slice(aligned_image, colormap=gui_drift_apply["cmaps"].value, continuous_update=True),) -gui_drift_apply.add_label("Load drift table:") +gui_drift_apply.add_label(value="Load drift table:") gui_drift_apply.add_file_upload("upload") -gui_drift_apply.add_label("Load image to align:") +gui_drift_apply.add_label(value="Load image to align:") gui_drift_apply.add_file_upload("upload image") gui_drift_apply.add_dropdown("cmaps", description="Colormap:", options=sorted(list(mpl.colormaps)), diff --git a/src/notebookchef/pantry/methods/error_map.py b/src/notebookchef/pantry/methods/error_map.py index 69964a03..50053164 100644 --- a/src/notebookchef/pantry/methods/error_map.py +++ b/src/notebookchef/pantry/methods/error_map.py @@ -1,4 +1,4 @@ -#@title Create Error Map GUI +# @title Create Error Map GUI gui_error = EasyGui("Error") import numpy as np @@ -7,6 +7,7 @@ from matplotlib import pyplot as plt from nanopyx.core.transform import ErrorMap + def run_error(b): clear_output() gui_error.show() @@ -15,7 +16,9 @@ def run_error(b): gui_error["run"].description = "Calculating..." global errormap error_map = ErrorMap() - error_map.optimise(np.mean(dataset_original, axis=0), np.mean(dataset_sr, axis=0)) + error_map.optimise( + np.mean(dataset_original, axis=0), np.mean(dataset_sr, axis=0) + ) gui_error["run"].disabled = False gui_error["run"].description = "Calculate" print("RSE: ", error_map.getRSE()) @@ -27,7 +30,9 @@ def run_error(b): name = gui_data["upload"].selected_filename.split(".")[0] tiff.imwrite(path + os.sep + name + "_error_map.tif", errormap) else: - name = gui_data["data_source"].value.replace("Example dataset: ", "") + name = gui_data["data_source"].value.replace( + "Example dataset: ", "" + ) tiff.imwrite(name + "_error_map.tif", errormap) plt.imshow(errormap) plt.axis("off") @@ -37,11 +42,13 @@ def run_error(b): with output_plot: display(Image.open(img_buf)) gui_error._main_display.children = gui_error._main_display.children + ( - widgets.Label(value="RSE: "+str(error_map.getRSE())), - widgets.Label(value="RSP: "+str(error_map.getRSP())), - output_plot) + widgets.Label(value="RSE: " + str(error_map.getRSE())), + widgets.Label(value="RSP: " + str(error_map.getRSP())), + output_plot, + ) plt.clf() + def run_error_stack(b): clear_output() gui_error.show() @@ -55,27 +62,40 @@ def run_error_stack(b): rse_rsp_table = [] # Ensure datasets are 3D - if np.ndim(dataset_original) == 2: - dataset_original = np.expand_dims(dataset_original, axis=0) - if np.ndim(dataset_sr) == 2: - dataset_sr = np.expand_dims(dataset_sr, axis=0) + if np.ndim(dataset_original) == 2: + dataset_original = np.expand_dims(dataset_original, axis=0) + if np.ndim(dataset_sr) == 2: + dataset_sr = np.expand_dims(dataset_sr, axis=0) + + # Ensures datasets have same number of frames + + if dataset_original.shape[0] > dataset_sr.shape[0]: + factor = dataset_original.shape[0] // dataset_sr.shape[0] + remainder = dataset_original.shape[0] % dataset_sr.shape[0] + averaged_blocks = [ + np.mean(dataset_original[i * factor : (i + 1) * factor], axis=0) + for i in range(dataset_sr.shape[0]) + ] + if remainder > 0: + averaged_blocks.append( + np.mean(dataset_original[-remainder:], axis=0) + ) + dataset_original = np.array(averaged_blocks) # Iterate through each slice print("Processing slices...") - for i in tqdm(range(dataset_original.shape[0]),desc="Slices processed"): - slice_df = dataset_original[i] # - slice_sr = dataset_sr[i] # - + for i in tqdm(range(dataset_original.shape[0]), desc="Slices processed"): + slice_df = dataset_original[i] # + slice_sr = dataset_sr[i] # + error_map = ErrorMap() error_map.optimise(slice_df, slice_sr) # Store the error map and RSE/RSP values errormap_stack.append(np.array(error_map.imRSE)) - rse_rsp_table.append({ - "Slice": i, - "RSE": error_map.getRSE(), - "RSP": error_map.getRSP() - }) + rse_rsp_table.append( + {"Slice": i, "RSE": error_map.getRSE(), "RSP": error_map.getRSP()} + ) # Convert results to arrays errormap_stack = np.array(errormap_stack) # 3D stack of error maps @@ -85,10 +105,16 @@ def run_error_stack(b): if own_data: path = gui_data["upload"].selected_path name = gui_data["upload"].selected_filename.split(".")[0] - tiff.imwrite(path + os.sep + name + "_error_map_stack.tif", errormap_stack) - rse_rsp_table.to_csv(path + os.sep + name + "_rse_rsp_table.csv", index=False) + tiff.imwrite( + path + os.sep + name + "_error_map_stack.tif", errormap_stack + ) + rse_rsp_table.to_csv( + path + os.sep + name + "_rse_rsp_table.csv", index=False + ) else: - name = gui_data["data_source"].value.replace("Example dataset: ", "") + name = gui_data["data_source"].value.replace( + "Example dataset: ", "" + ) tiff.imwrite(name + "_error_map_stack.tif", errormap_stack) rse_rsp_table.to_csv(name + "_rse_rsp_table.csv", index=False) @@ -105,9 +131,13 @@ def run_error_stack(b): gui_error.add_checkbox("save", description="Save output", value=True) -gui_error.add_dropdown("cmaps", description="Colormap:", - options=sorted(list(mpl.colormaps)), - value="viridis", remember_value=True) +gui_error.add_dropdown( + "cmaps", + description="Colormap:", + options=sorted(list(mpl.colormaps)), + value="viridis", + remember_value=True, +) gui_error.add_button("run", description="Calculate") gui_error["run"].on_click(run_error_stack) -gui_error.show() \ No newline at end of file +gui_error.show() diff --git a/src/notebookchef/pantry/methods/esrrf.py b/src/notebookchef/pantry/methods/esrrf.py index 0f162923..bad4f1c3 100644 --- a/src/notebookchef/pantry/methods/esrrf.py +++ b/src/notebookchef/pantry/methods/esrrf.py @@ -1,7 +1,10 @@ -#@title Create eSRRF GUI +# @title Create eSRRF GUI gui_esrrf = EasyGui("esrrf") from nanopyx.methods import eSRRF -from nanopyx.core.transform.sr_temporal_correlations import calculate_eSRRF_temporal_correlations +from nanopyx.core.transform.sr_temporal_correlations import ( + calculate_eSRRF_temporal_correlations, +) + def run_esrrf(b): clear_output() @@ -27,14 +30,22 @@ def run_esrrf(b): elif frames_per_timepoint > dataset_original.shape[0]: frames_per_timepoint = dataset_original.shape[0] - output= [] + output = [] for i in range(dataset_original.shape[0] // frames_per_timepoint): - block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint] - result = eSRRF(block, magnification=magnification, radius=ring_radius, - sensitivity=sensitivity, - doIntensityWeighting=True) - output.append(calculate_eSRRF_temporal_correlations(result[0], esrrf_order)) + block = dataset_original[ + i * frames_per_timepoint : (i + 1) * frames_per_timepoint + ] + result = eSRRF( + block, + magnification=magnification, + radius=ring_radius, + sensitivity=sensitivity, + doIntensityWeighting=True, + ) + output.append( + calculate_eSRRF_temporal_correlations(result[0], esrrf_order) + ) global dataset_esrrf dataset_esrrf = np.array(output) @@ -47,25 +58,67 @@ def run_esrrf(b): name = gui_data["upload"].selected_filename.split(".")[0] tiff.imwrite(path + os.sep + name + "_esrrf.tif", dataset_esrrf) else: - name = gui_data["data_source"].value.replace("Example dataset: ", "") + name = gui_data["data_source"].value.replace( + "Example dataset: ", "" + ) tiff.imwrite(name + "_esrrf.tif", dataset_esrrf) - gui_esrrf._main_display.children = gui_esrrf._main_display.children + (stackview.slice(dataset_esrrf, colormap=gui_esrrf["cmaps"].value, continuous_update=True),) + gui_esrrf._main_display.children = gui_esrrf._main_display.children + ( + stackview.slice( + dataset_esrrf, + colormap=gui_esrrf["cmaps"].value, + continuous_update=True, + ), + ) default_radius = 1.5 default_sensitivity = 1 default_magnification = 5 default_esrrf_order = 1 -gui_esrrf.add_float_slider("ring_radius", description="Ring Radius:", min=0.1, max=3.0, value=default_radius, remember_value=True) -gui_esrrf.add_int_slider("sensitivity", description="Sensitivity:", min=1, max=10, value=default_sensitivity) -gui_esrrf.add_int_slider("magnification", description="Magnification:", min=1, max=10, value=default_magnification) -gui_esrrf.add_int_slider("esrrf_order", description="eSRRF order:", min=1, max=3, value=default_esrrf_order) -gui_esrrf.add_label("-=-= Time-Lapse =-=-") -gui_esrrf.add_int_slider("frames_per_timepoint", description="Frames per time-point (0 - auto)", min=0, max=dataset_original.shape[0], value=dataset_original.shape[0]//2) +gui_esrrf.add_float_slider( + "ring_radius", + description="Ring Radius:", + min=0.1, + max=3.0, + value=default_radius, +) +gui_esrrf.add_int_slider( + "sensitivity", + description="Sensitivity:", + min=1, + max=10, + value=default_sensitivity, +) +gui_esrrf.add_int_slider( + "magnification", + description="Magnification:", + min=1, + max=10, + value=default_magnification, +) +gui_esrrf.add_int_slider( + "esrrf_order", + description="eSRRF order:", + min=1, + max=3, + value=default_esrrf_order, +) +gui_esrrf.add_label(value="-=-= Time-Lapse =-=-") +gui_esrrf.add_int_slider( + "frames_per_timepoint", + description="Frames per time-point (0 - auto)", + min=0, + max=dataset_original.shape[0], + value=dataset_original.shape[0] // 2, +) gui_esrrf.add_checkbox("save", description="Save Output", value=True) -gui_esrrf.add_dropdown("cmaps", description="Colormap:", - options=sorted(list(mpl.colormaps)), - value="viridis", remember_value=True) +gui_esrrf.add_dropdown( + "cmaps", + description="Colormap:", + options=sorted(list(mpl.colormaps)), + value="viridis", + remember_value=True, +) gui_esrrf.add_button("run", description="Run") -gui_esrrf['run'].on_click(run_esrrf) -gui_esrrf.show() \ No newline at end of file +gui_esrrf["run"].on_click(run_esrrf) +gui_esrrf.show() diff --git a/src/notebookchef/pantry/methods/frc.py b/src/notebookchef/pantry/methods/frc.py index d1702373..e421d01a 100644 --- a/src/notebookchef/pantry/methods/frc.py +++ b/src/notebookchef/pantry/methods/frc.py @@ -38,7 +38,7 @@ def run_frc(b): gui_frc._main_display.children = gui_frc._main_display.children + (output_plot,) plt.clf() -gui_frc.add_int_slider("pixel_size", description="Pixel Size:", min=0.01, max=1000, value=100, remember_value=True) +gui_frc.add_int_slider("pixel_size", description="Pixel Size:", min=0.01, max=1000, value=100) gui_frc.add_dropdown("units", description="Units: ", options=["nm", "um", "mm"], value="nm") gui_frc.add_int_slider("first_frame", description="First Frame:", min=0, max=dataset_original[0].shape[0]-1, value=0) gui_frc.add_int_slider ("second_frame", description="Second Frame:", min=0, max=dataset_original[0].shape[0]-1, value=1) diff --git a/src/notebookchef/pantry/methods/nlm_denoising.py b/src/notebookchef/pantry/methods/nlm_denoising.py index 0933b7a4..c1de211d 100644 --- a/src/notebookchef/pantry/methods/nlm_denoising.py +++ b/src/notebookchef/pantry/methods/nlm_denoising.py @@ -31,8 +31,8 @@ def run_nlm(b): tiff.imwrite(name + "_nlm_denoised.tif", dataset_nlm) gui_nlm._main_display.children = gui_nlm._main_display.children + (stackview.slice(dataset_nlm, colormap=gui_nlm["cmaps"].value, continuous_update=True),) -gui_nlm.add_int_slider("patch_size", description="Patch Size", min=1, max=dataset_original.shape[-1]//2, value=5, remember_value=True) -gui_nlm.add_int_slider("patch_distance", description="Patch Distance", min=1, max=dataset_original.shape[-1]//2, value=10, remember_value=True) +gui_nlm.add_int_slider("patch_size", description="Patch Size", min=1, max=dataset_original.shape[-1]//2, value=5) +gui_nlm.add_int_slider("patch_distance", description="Patch Distance", min=1, max=dataset_original.shape[-1]//2, value=10) gui_nlm.add_float_text("h", description="h", value=0.1, remember_value=True) gui_nlm.add_float_text("sigma", description="sigma", value=0.1, remember_value=True) gui_nlm.add_checkbox("save", description="Save Output", value=True) diff --git a/src/notebookchef/pantry/methods/srrf.py b/src/notebookchef/pantry/methods/srrf.py index 4c34da86..1d3ab64d 100644 --- a/src/notebookchef/pantry/methods/srrf.py +++ b/src/notebookchef/pantry/methods/srrf.py @@ -43,10 +43,10 @@ def run_srrf(b): tiff.imwrite(name + "_srrf.tif", dataset_srrf) gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf["cmaps"].value, continuous_update=True),) -gui_srrf.add_float_slider("ring_radius", description="Ring Radius:", min=0.1, max=3.0, value=0.5, remember_value=True) +gui_srrf.add_float_slider("ring_radius", description="Ring Radius:", min=0.1, max=3.0, value=0.5) gui_srrf.add_int_slider("magnification", description="Magnification:", min=1, max=10, value=5) gui_srrf.add_int_slider("srrf_order", description="SRRF order:", min=-1, max=4, value=3) -gui_srrf.add_label("-=-= Time-Lapse =-=-") +gui_srrf.add_label(value="-=-= Time-Lapse =-=-") gui_srrf.add_int_slider("frames_per_timepoint", description="Frames per time-point (0 - auto)", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2) gui_srrf.add_checkbox("save", description="Save Output", value=True) gui_srrf.add_dropdown("cmaps", description="Colormap:", diff --git a/src/notebookchef/pantry/notebook_setup.py b/src/notebookchef/pantry/notebook_setup.py index 81b5a9c1..b2a32b7f 100644 --- a/src/notebookchef/pantry/notebook_setup.py +++ b/src/notebookchef/pantry/notebook_setup.py @@ -24,7 +24,10 @@ from IPython.display import display, clear_output from matplotlib import pyplot as plt -from nanopyx.core.utils.easy_gui import EasyGui +try: + from ezinput import EZInput as EasyGui +except ImportError: + from nanopyx.core.utils.easy_gui import EasyGui from nanopyx.core.utils.find_files import find_files from nanopyx.data.download import ExampleDataManager From 542ebc529ec281f4a193333e3471f092c1ed5cce Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 1 Apr 2025 14:31:51 +0100 Subject: [PATCH 02/82] notebook update --- notebooks/ChannelRegistration.ipynb | 14 +- notebooks/DriftCorrection.ipynb | 14 +- notebooks/ExampleDataSRRFandQC.ipynb | 236 +++---------------- notebooks/NonLocalMeansDenoising.ipynb | 12 +- notebooks/ParamSweepandeSRRF.ipynb | 34 +-- notebooks/SRMetrics.ipynb | 34 +-- notebooks/SRRFandQC.ipynb | 32 +-- notebooks/eSRRFandQC.ipynb | 306 +++---------------------- 8 files changed, 131 insertions(+), 551 deletions(-) diff --git a/notebooks/ChannelRegistration.ipynb b/notebooks/ChannelRegistration.ipynb index 598ed1a8..805d70eb 100644 --- a/notebooks/ChannelRegistration.ipynb +++ b/notebooks/ChannelRegistration.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "c8226d8c", + "id": "32b72803", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6f11b9e7", + "id": "3ffbd338", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0f360c1d", + "id": "ef92de59", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "c7f6f43c", + "id": "037b9135", "metadata": {}, "source": [ "# Channel Registration Parameters: \n", @@ -182,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d47c3053", + "id": "957674bc", "metadata": { "cellView": "form" }, @@ -250,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "ef3079fe", + "id": "a2790a79", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated translation mask\n", @@ -260,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ad3082a8", + "id": "a3495a46", "metadata": { "cellView": "form" }, diff --git a/notebooks/DriftCorrection.ipynb b/notebooks/DriftCorrection.ipynb index 2220791b..cb393462 100644 --- a/notebooks/DriftCorrection.ipynb +++ b/notebooks/DriftCorrection.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "8fb2655a", + "id": "5578943b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e3f17e2d", + "id": "1b387091", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "eb67e5c7", + "id": "44ddfb89", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "65e5af99", + "id": "efe0e892", "metadata": {}, "source": [ "# Drift Correction Parameters: \n", @@ -181,7 +181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b7f24156", + "id": "935acd10", "metadata": { "cellView": "form" }, @@ -250,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "803f977b", + "id": "9fe2c429", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated drift table\n", @@ -260,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7203020b", + "id": "406f16c8", "metadata": { "cellView": "form" }, diff --git a/notebooks/ExampleDataSRRFandQC.ipynb b/notebooks/ExampleDataSRRFandQC.ipynb index 73bbb71b..5df932f6 100644 --- a/notebooks/ExampleDataSRRFandQC.ipynb +++ b/notebooks/ExampleDataSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "1557495b", + "id": "9c299982", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cb120f83", + "id": "9542cc57", "metadata": { "cellView": "form" }, @@ -47,28 +47,12 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "5fc82466", + "execution_count": null, + "id": "7ef15b6c", "metadata": { "cellView": "form" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/opt/miniconda3/envs/aiobio/lib/python3.11/site-packages/pytools/persistent_dict.py:52: RecommendedHashNotFoundWarning: Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.\n", - " warn(\"Unable to import recommended hash 'siphash24.siphash13', \"\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cupy implementation is not available. Make sure you have the right version of Cupy and CUDA installed.\n" - ] - } - ], + "outputs": [], "source": [ "#@title Install NanoPyx, import necessary libraries and connect to Google Drive\n", "import sys\n", @@ -121,7 +105,7 @@ }, { "cell_type": "markdown", - "id": "0dae76fd", + "id": "11d6fda3", "metadata": {}, "source": [ "## Next lets create the Data Loader GUI.\n", @@ -131,27 +115,12 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "b6e1bd3d", + "execution_count": null, + "id": "f6e2b5cc", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2f92d1db91d74c41a2bb9e6a4b6f096f", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Button(description='Use Own data', layout=Layout(width='50%'), style=ButtonStyle()), Button(des…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title Load image stack\n", "# Create a GUI\n", @@ -231,7 +200,7 @@ }, { "cell_type": "markdown", - "id": "3631c841", + "id": "31745eeb", "metadata": {}, "source": [ "## Now let's use SRRF to generate a super-resolution image\n", @@ -240,7 +209,7 @@ }, { "cell_type": "markdown", - "id": "3d0566ef", + "id": "7531fbc0", "metadata": {}, "source": [ "# SRRF Parameters:\n", @@ -255,27 +224,12 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "eda46129", + "execution_count": null, + "id": "99166cca", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f9d2b449319040eeadedfc747738f638", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(FloatSlider(value=0.5, description='Ring Radius:', layout=Layout(width='50%'), max=3.0, min=0.1…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title Create SRRF GUI\n", "gui_srrf = EasyGui(\"srrf\")\n", @@ -340,7 +294,7 @@ }, { "cell_type": "markdown", - "id": "68a331c9", + "id": "4d33e8d7", "metadata": {}, "source": [ "## Let's use NanoPyx to generate an error map of the SRRF image\n", @@ -349,98 +303,12 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "81b7bcf5", + "execution_count": null, + "id": "4542056f", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8de8142ffd8f4c0684018201d413b78d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Checkbox(value=True, description='Save output', layout=Layout(width='50%'), style=CheckboxStyle…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
SliceRSERSP
0033.7130740.969177
1132.9383470.967807
\n", - "
" - ], - "text/plain": [ - " Slice RSE RSP\n", - "0 0 33.713074 0.969177\n", - "1 1 32.938347 0.967807" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", @@ -591,7 +459,7 @@ }, { "cell_type": "markdown", - "id": "1cbff0fd", + "id": "48478099", "metadata": {}, "source": [ "## Let's compare the resolution of the raw data with the SRRF using FRC and DecorrelationAnalysis. Let's start with calculation the FRC resolution of the raw data (select frame 3 and 11).\n", @@ -600,7 +468,7 @@ }, { "cell_type": "markdown", - "id": "6656985e", + "id": "902d6e3b", "metadata": {}, "source": [ "# FRC Parameters:\n", @@ -614,36 +482,12 @@ }, { "cell_type": "code", - "execution_count": 6, - "id": "294051c3", + "execution_count": null, + "id": "e1a4424f", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4b131bea871143858bdbe85465dd0474", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title create FRC GUI for original image\n", "gui_frc_df = EasyGui(\"FRC\")\n", @@ -699,7 +543,7 @@ }, { "cell_type": "markdown", - "id": "3d6160cf", + "id": "674aab58", "metadata": {}, "source": [ "## Now do the same for the SRRF image\n", @@ -709,7 +553,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16c1ed7f", + "id": "0f91e309", "metadata": { "cellView": "form" }, @@ -769,7 +613,7 @@ }, { "cell_type": "markdown", - "id": "3b71f8cd", + "id": "d7791e40", "metadata": {}, "source": [ "## Let's do the same using Decorrelation analysis\n", @@ -778,7 +622,7 @@ }, { "cell_type": "markdown", - "id": "9798b705", + "id": "42a9e78a", "metadata": {}, "source": [ "# Image Decorrelation Analysis Parameters:\n", @@ -794,7 +638,7 @@ { "cell_type": "code", "execution_count": null, - "id": "bc5b3e82", + "id": "ad0481e0", "metadata": { "cellView": "form" }, @@ -855,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "e39f89d9", + "id": "f7155db8", "metadata": {}, "source": [ "## Now let's measure the resolution of the generated SRRF image using Decorrelation analysis\n", @@ -865,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e9e6d9ce", + "id": "d59ea934", "metadata": { "cellView": "form" }, @@ -925,25 +769,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "aiobio", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/notebooks/NonLocalMeansDenoising.ipynb b/notebooks/NonLocalMeansDenoising.ipynb index 61421067..af7ab7c9 100644 --- a/notebooks/NonLocalMeansDenoising.ipynb +++ b/notebooks/NonLocalMeansDenoising.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "1431ef5d", + "id": "46d86532", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -23,7 +23,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62dd0f47", + "id": "9a077380", "metadata": { "cellView": "form" }, @@ -45,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6554b761", + "id": "2a440b08", "metadata": { "cellView": "form" }, @@ -103,7 +103,7 @@ { "cell_type": "code", "execution_count": null, - "id": "565aafd4", + "id": "593b62b3", "metadata": { "cellView": "form" }, @@ -187,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "10cf26ea", + "id": "972df323", "metadata": {}, "source": [ "# Use Non-local means denoising on selected data\n", @@ -204,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69c6a01e", + "id": "6efd880b", "metadata": { "cellView": "form" }, diff --git a/notebooks/ParamSweepandeSRRF.ipynb b/notebooks/ParamSweepandeSRRF.ipynb index cd2f12b1..57c66329 100644 --- a/notebooks/ParamSweepandeSRRF.ipynb +++ b/notebooks/ParamSweepandeSRRF.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "a6dfac91", + "id": "2cbaec97", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fd249678", + "id": "6699f205", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "86f9bafb", + "id": "e05acfd0", "metadata": { "cellView": "form" }, @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ffc7ae04", + "id": "d51549f2", "metadata": { "cellView": "form" }, @@ -192,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "7f7460e4", + "id": "1494882e", "metadata": {}, "source": [ "# Run a parameter sweep to find the best combination of parameters\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29f01d72", + "id": "032710fa", "metadata": { "cellView": "form" }, @@ -274,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a8e81b65", + "id": "50e8028e", "metadata": { "cellView": "form" }, @@ -410,7 +410,7 @@ }, { "cell_type": "markdown", - "id": "2d4a4846", + "id": "dd812dab", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -420,7 +420,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d951ae96", + "id": "ff9b71c0", "metadata": { "cellView": "form" }, @@ -575,7 +575,7 @@ }, { "cell_type": "markdown", - "id": "61e9a48f", + "id": "ff41b4ba", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -591,7 +591,7 @@ { "cell_type": "code", "execution_count": null, - "id": "861fee57", + "id": "7436cacf", "metadata": { "cellView": "form" }, @@ -651,7 +651,7 @@ }, { "cell_type": "markdown", - "id": "c17f6bf0", + "id": "a195e6ee", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -667,7 +667,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f093bac2", + "id": "519a5e35", "metadata": { "cellView": "form" }, @@ -727,7 +727,7 @@ }, { "cell_type": "markdown", - "id": "9a090d9e", + "id": "fc8d2f69", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -744,7 +744,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8e62e0c3", + "id": "3493d9ea", "metadata": { "cellView": "form" }, @@ -805,7 +805,7 @@ }, { "cell_type": "markdown", - "id": "31a82f0a", + "id": "ed3ac546", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -822,7 +822,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17a98f6c", + "id": "4bd3ddc6", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRMetrics.ipynb b/notebooks/SRMetrics.ipynb index 1d333d96..2275fd6f 100644 --- a/notebooks/SRMetrics.ipynb +++ b/notebooks/SRMetrics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "f1e4c9d8", + "id": "dbb0862e", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2f3fb423", + "id": "6bde70c9", "metadata": { "cellView": "form" }, @@ -48,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10f64df9", + "id": "0457011e", "metadata": { "cellView": "form" }, @@ -105,7 +105,7 @@ }, { "cell_type": "markdown", - "id": "97c52d4a", + "id": "524271b9", "metadata": {}, "source": [ "## Load difraction limited image (only needed if you want to use the FRC and Decorrelation analysis on this image or if you want to perform the Error Map analysis)\n", @@ -115,7 +115,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5150b68b", + "id": "efe54fbe", "metadata": { "cellView": "form" }, @@ -199,7 +199,7 @@ }, { "cell_type": "markdown", - "id": "ad95295a", + "id": "8a87a4ed", "metadata": {}, "source": [ "## Load super-resolved image\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4b2f6fc0", + "id": "1e29e93c", "metadata": { "cellView": "form" }, @@ -293,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "519bddc3", + "id": "f99f901b", "metadata": {}, "source": [ "## Calculate Error Map\n", @@ -303,7 +303,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8ce61944", + "id": "b8aa963b", "metadata": { "cellView": "form" }, @@ -458,7 +458,7 @@ }, { "cell_type": "markdown", - "id": "fca24754", + "id": "da9b0f27", "metadata": {}, "source": [ "# Calculate FRC of the diffraction limited image\n", @@ -474,7 +474,7 @@ { "cell_type": "code", "execution_count": null, - "id": "470574d5", + "id": "ca14daa8", "metadata": { "cellView": "form" }, @@ -534,7 +534,7 @@ }, { "cell_type": "markdown", - "id": "01ae19c8", + "id": "f13f62dd", "metadata": {}, "source": [ "# Calculate FRC of the SR image\n", @@ -550,7 +550,7 @@ { "cell_type": "code", "execution_count": null, - "id": "959d4bee", + "id": "6752789b", "metadata": { "cellView": "form" }, @@ -610,7 +610,7 @@ }, { "cell_type": "markdown", - "id": "98a5483d", + "id": "ce9e8e84", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of diffraction limited image\n", @@ -627,7 +627,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18d3c9f9", + "id": "22228608", "metadata": { "cellView": "form" }, @@ -688,7 +688,7 @@ }, { "cell_type": "markdown", - "id": "44579837", + "id": "6f865c27", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of SR image\n", @@ -705,7 +705,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2bca37a6", + "id": "d06fc988", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRRFandQC.ipynb b/notebooks/SRRFandQC.ipynb index bc0359c4..df936f7c 100644 --- a/notebooks/SRRFandQC.ipynb +++ b/notebooks/SRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "962fcc63", + "id": "b57f313e", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cd7f7bb3", + "id": "37348e1e", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fa0992f8", + "id": "8e195be7", "metadata": { "cellView": "form" }, @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "182483ae", + "id": "659407d3", "metadata": { "cellView": "form" }, @@ -192,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "b043a5f0", + "id": "6959dd9c", "metadata": {}, "source": [ "# Use SRRF to generate a super-resolved image\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c2908c91", + "id": "26a4b201", "metadata": { "cellView": "form" }, @@ -278,7 +278,7 @@ }, { "cell_type": "markdown", - "id": "9e1b875f", + "id": "43161cbf", "metadata": {}, "source": [ "## Calculate error map for the SRRF image\n", @@ -288,7 +288,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a31a529c", + "id": "02a0c3bc", "metadata": { "cellView": "form" }, @@ -443,7 +443,7 @@ }, { "cell_type": "markdown", - "id": "174a3d86", + "id": "32a7c1db", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -459,7 +459,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43f55d4d", + "id": "741255f4", "metadata": { "cellView": "form" }, @@ -519,7 +519,7 @@ }, { "cell_type": "markdown", - "id": "fd610c51", + "id": "f7ef825f", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -535,7 +535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63557a6e", + "id": "4399e6be", "metadata": { "cellView": "form" }, @@ -595,7 +595,7 @@ }, { "cell_type": "markdown", - "id": "5b56f6d3", + "id": "9b756cfb", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -612,7 +612,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3d485c4d", + "id": "6791e176", "metadata": { "cellView": "form" }, @@ -673,7 +673,7 @@ }, { "cell_type": "markdown", - "id": "be5b0abc", + "id": "79fb17ab", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -690,7 +690,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3f161a44", + "id": "378d681f", "metadata": { "cellView": "form" }, diff --git a/notebooks/eSRRFandQC.ipynb b/notebooks/eSRRFandQC.ipynb index cc6ae039..e8d16583 100644 --- a/notebooks/eSRRFandQC.ipynb +++ b/notebooks/eSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "ff65de9b", + "id": "cecd691b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66cb1942", + "id": "a054905c", "metadata": { "cellView": "form" }, @@ -49,28 +49,12 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "c4f9e0b7", + "execution_count": null, + "id": "b49cc966", "metadata": { "cellView": "form" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/opt/miniconda3/envs/aiobio/lib/python3.11/site-packages/pytools/persistent_dict.py:52: RecommendedHashNotFoundWarning: Unable to import recommended hash 'siphash24.siphash13', falling back to 'hashlib.sha256'. Run 'python3 -m pip install siphash24' to install the recommended hash.\n", - " warn(\"Unable to import recommended hash 'siphash24.siphash13', \"\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Cupy implementation is not available. Make sure you have the right version of Cupy and CUDA installed.\n" - ] - } - ], + "outputs": [], "source": [ "#@title Install NanoPyx, import necessary libraries and connect to Google Drive\n", "import sys\n", @@ -124,26 +108,11 @@ { "cell_type": "code", "execution_count": null, - "id": "b2dc1a3c", + "id": "98d87a68", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a57a99595a594f70afe42a3f7083f807", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Button(description='Use Own data', layout=Layout(width='50%'), style=ButtonStyle()), Button(des…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title Load image stack\n", "# Create a GUI\n", @@ -223,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "ff0fed51", + "id": "2e65c3fb", "metadata": {}, "source": [ "# Use eSRRF to generate a super-resolved image\n", @@ -239,27 +208,12 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "667d6292", + "execution_count": null, + "id": "770a96a0", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0bc7997ab78f48699f1a3e53ddce5ab3", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(FloatSlider(value=1.5, description='Ring Radius:', layout=Layout(width='50%'), max=3.0, min=0.1…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# @title Create eSRRF GUI\n", "gui_esrrf = EasyGui(\"esrrf\")\n", @@ -391,7 +345,7 @@ }, { "cell_type": "markdown", - "id": "78fa1296", + "id": "ad48eada", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -400,98 +354,12 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "ee3e878c", + "execution_count": null, + "id": "90cf4ca6", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d41fb6aafc6b494783d6f6a5564dfc34", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(Checkbox(value=True, description='Save output', layout=Layout(width='50%'), style=CheckboxStyle…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
SliceRSERSP
0049.8408550.931586
1145.9631310.936541
\n", - "
" - ], - "text/plain": [ - " Slice RSE RSP\n", - "0 0 49.840855 0.931586\n", - "1 1 45.963131 0.936541" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAGFCAYAAAASI+9IAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/cmSJEuSJYqJTe4ecYfKqm7qBtAEUL9HABZY4A+wxlfhe/AP+AIsgRXw6BFhoIceKqsz804R4YMNIGY+h+UIq6i5eUTcm7UIzfQb7maqojLycHjaXC6XS/t2fbu+Xd+ub9e3q7W2/Xt34Nv17fp2fbu+Xf96rm9M4dv17fp2fbu+XXl9Ywrfrm/Xt+vb9e3K6xtT+HZ9u75d365vV17fmMK369v17fp2fbvy+sYUvl3frm/Xt+vbldc3pvDt+nZ9u75d3668vjGFb9e369v17fp25bVvN17/x//9/6ld/uuf2+Uvf2ub3Q6fblrbbdtms2nNfrbb+OG39tkXXB5Xx5/ll3wJ/o7PLudTuxyP/vtmt2/bdw+t3d+1zft37fL+oV3uD+18v2/n+1077zftst20zaW1zenSNqdz2306tu3Hp9YeX9rm46d2+fSpXV6O7XI6xesOh7a5u2ub7963y/v7dnm4a6fv7tplv/W27PI2nl7a5tNL2/zyW7s8PbfLy0vMx2Hf2uHQ2vfv/dm237XLZtM253Nrx1PbPB/b5reP8czzczt/+tSafWfj3XLeJ3MymyPOjf8bv3sfrJ/bbR/L/V27WH/e3bXzw6Gd3h/a+bBplx3m59za5mzzc2mb49n/3dq/L+e2sT4fz609PXvf2/HYLk9P7fL80prNmfW9dyj2y37v49483LeLrcu7u3b88aG9/GBrs2mnu62PZ3tsbfd8bnd/fWq7nz+1za8f2+VvP7XL41O7nM++vu1yjn3CORr2xGUyFzbufYzd9sX9va/JxdbF+uX3XWItXjiel9ZeXmJf2d9H2wtoe7trm/3On93cWZv3rd0dfG0v9/t22e98vx3f7dr5sG3nu03MrR0ZW9LnS9s/ntv26dQOPz+1zS8fWvv01JrtO4zTXrXZbVuzc2frZr9zTW0t5fzZel7uDq09HNrxx3ft9G7XTg+79vLdFvvd1rK17cul7V4ubf/x3Pa/Pbftp5e2/fjcNr9+8H1n+9X2oK1h9KHsL7yzn3H0Ab8O37F/a/vzj7jY/0JP/NfzuV1Ox+jSdtu2371v7XDn62m0w+fYruejz017fnG6cH56inHaXrKzyeGcnRj19bllzEI3h/7O7tcx+O92DOysxee+XvY3nt8+PMRnp1P7vzz/n78eU7j887+0l396aMf/+B/bZb/xzbw92bCDqPr7OSs3xkhvZvdu8JHMRd4i+294jK+1z7etnbfndrZ/H7bt9N2+Xezf73et/bhp+3fndv/uuf34/qk9HI7tsDt5Y8+nXXt62bdfPt63T7/ctfPHu7b59X3b/WbEwQhgvPFyt23n+207/bhvmx9a2393bj/8+KH9cP/sbe035/bzx4f28XHfPvx2aKe//mPbfLy07ZMdLGMK23Y5bNrph13bfBcrsDEaaB1+3rfL433b/fKubT+d2taIxYdj2xwvQZTt+ZyQ9UnmbTEvdiB1jmKjOsG/j7Gc3+3a8R92bfvDpe3fn9s//PhL+/7+qT3sj+3d/tlfdT5v28tp1z4d9+35uGvPx317fNq155dtOz7v2vHjd23zeGmbpxZ9/xTMI/oufTPmacTpsG2n97t2/tOm7b6/tB/+zcf2v/7TX9u/efit/fv7n9u+Xdrjad9+fXlo/8+//Pv23/76rv36tx/b9p+/a7tfj233ZPtv6/PiB8H+h6H2wcaB8cvmGOO2Mfs6Puza+b39vvEfPw02P/bMc2vbp0vb2D4H0bY94Ezx5TyO52CE3uYxfi73m3b5btMO78/tcH9s3717bP/z7z60H+4e2493n9qP+09ti0Pz8/P79pfH79tPn961//zX79vLX75v7Tcb46ltP5xifPY6Iy5g0vkviK+fB6Mpu0072fsfNq1919oP/+ax/dvvP7R/+92H9t999y/tH/fn9s7uu1zab8dz++V41/7Lp39o/9+f/7H9/Nt9++XX+3b5672v3fbx0rYfsfddCAjBIISvftZ59rnh9EyP5GxzOw2Ybuob7ll9gTZxWX62Ac2wvXHYtLMLJ7Y/4qft4kz1vXBu249HXx+jgbvz1v/tfdTOTmahjgX8YzrmG/imn3c7A2j8srHTgO+MDr7bB/043jaJNzOF9tef2/G/+1+1p//tv/OJ2384t90LJWwjWCGBuITOAU0nSUcjm0qJFv72QYKw5e/JNEQqweDtWZPETnetne427eX9pr380Nrp/aW9/HBp7R+f28P759a+O7a7739r3x8e23sjeq21D8f7dnm5b4+/7drPP92344dD2/x0aHe/GAM0iS7ec75v7fRwac9/urTtn57bww9P7cd//NS+e/jN27rfHtunj39qv378rv366317+pf3bfvbtu0/GAFr7by/tPNda8cfz23z/UvbHs5tuzu382nbzs/bdv64b4efdm3/cdP2H1s7/HZpu+eY53HjTS5hjsN8yrz5PNnPvrXT/aYd37V2fH9pz/90bvt/eG4P3z+1P/3jx/bju1/aPxwe25+sE+3SXi779ul0aD+9vG+/He/a8eW+vTy9ax+f7trT87Z9+vW+bT7u2/Zx0/Y23o/NCcnW94ivoHfivGvtfGg+By/fX9rx3xzb3Y9P7bt/99j+w7/9z+1/8+7P7X93/5/bw+bUfj4/tP9y/KH9p78+tP/y54f201/etd3/7749/LV5+/tPIFS47FDLNDgxtffGuI0hxLuP7zbtZOv4rrWX787tcndp5/tLawcQ+/OmbZ62bfdp07ZPm7b/tGm7TzEWk7BtXPmKQ+w1H893rZ2+O7fzw6Vdfji2d98/te8eTu3795/aP/3wl/Y/u/+5/Ye7v7X/xf5v7bA5NTsp/+n4j+1//PTv2/HjP7UPf/6+ffznd+3y677d/bRth19jDm2M7L8fcmdefS1z/+9aO35/aef3p9bs/f/+t/bDjz+1//jDP7f/w3f/Q/uP+1P70zbm68/nU/tPx/ft//b4H9qf/7Zvf/ll137623ft8ueHtvP127TDb6HJhMbWyjnvTEIZg//un3UGMnxXyEHe93syBdIMSO16PvxvY6ZGN2wtHzbt+cc4F3bO7Wxc9sFINi/YC48xN4dfLy6c7J9a27nQF80u5Nwy7sWY3zo27but/yY0wBD4jMHFfg9BqDkdDO2wfV2mYKq+SZQv39nB3rTNKYiNH3zr5BmjP2MFNrph1tld8jRKPCnl1r9xP6XfWbO43w+QTZZPWJ/zy3nTzudNO5237em8b4/nfdueL23bLu35vGsv551LxJfLxoVMX2BuKiA3SVQ3l7bZXtp2e26H7bm93z23H/aP7f32uf169+Cax4eXu/Z09xCS5P7Sti/RmB8mU1COW58unwU/QICyymFarIV8l/MTzeS0DNOu88h5kWdMA7H1vJxibo7nnf+c8JBJtbt2bvst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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# @title Create Error Map GUI\n", "gui_error = EasyGui(\"Error\")\n", @@ -642,7 +510,7 @@ }, { "cell_type": "markdown", - "id": "edbc1fee", + "id": "649722a8", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -657,36 +525,12 @@ }, { "cell_type": "code", - "execution_count": 5, - "id": "4464be04", + "execution_count": null, + "id": "f5d013a2", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "814291cbb1a64dea9413bed409e4ed85", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title create FRC GUI for original image\n", "gui_frc = EasyGui(\"FRC\")\n", @@ -742,7 +586,7 @@ }, { "cell_type": "markdown", - "id": "282970c2", + "id": "b9a9e784", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -758,35 +602,11 @@ { "cell_type": "code", "execution_count": null, - "id": "fbc8f076", + "id": "8f4af4d0", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7e72195723d1414382edef1d1ac7ec9d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title create FRC GUI for SR image\n", "gui_frc_esrrf = EasyGui(\"FRC\")\n", @@ -842,7 +662,7 @@ }, { "cell_type": "markdown", - "id": "b4b4f51c", + "id": "b0519e68", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -858,36 +678,12 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "3a08bd98", + "execution_count": null, + "id": "4a261831", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4d9274dbe5da4ee5a642a91108633996", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title Create Decorrelation Analysis GUI for eSRRF data\n", "gui_decorr = EasyGui(\"DecorrAnalysis\")\n", @@ -944,7 +740,7 @@ }, { "cell_type": "markdown", - "id": "19a5a204", + "id": "9b48abb1", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -961,35 +757,11 @@ { "cell_type": "code", "execution_count": null, - "id": "d2779b72", + "id": "0ca641ca", "metadata": { "cellView": "form" }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "67ebb4524eac4cbf968f67520ac2cab3", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(IntSlider(value=100, description='Pixel Size:', layout=Layout(width='50%'), max=1000, style=Sli…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "#@title Create Decorrelation Analysis GUI for eSRRF data\n", "gui_decorr_esrrf = EasyGui(\"DecorrAnalysis\")\n", @@ -1044,25 +816,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "aiobio", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } From 1bad4ffc8163ea992112d5ded831c11074d0625e Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Wed, 9 Apr 2025 14:49:59 +0100 Subject: [PATCH 03/82] re adding 3D changes --- .../_c_sr_radial_gradient_convergence.c | 39 +- .../_c_sr_radial_gradient_convergence.h | 3 +- .../nanopyx.core.transform._le_esrrf3d.pyx | 573 ++++++++---------- src/nanopyx/core/transform/_le_esrrf3d_.cl | 238 ++++++++ src/nanopyx/methods/esrrf_3d/run.py | 52 +- 5 files changed, 525 insertions(+), 380 deletions(-) create mode 100644 src/nanopyx/core/transform/_le_esrrf3d_.cl diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index 5f6850c3..101ed0f2 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -121,37 +121,37 @@ float _c_get_bound_value(float* im, int slices, int rows, int cols, int s, int r return im[_s * rows * cols + _r * cols + _c]; } -float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float Gx_Gy_MAGNIFICATION, float Gz_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { +float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, distanceWeight_xy, distanceWeight_z, GdotR, Dk; - float xc = (xM + 0.5) / magnification_xy; - float yc = (yM + 0.5) / magnification_xy; - float zc = (sliceM + 0.5) / magnification_z; + float xc = (xM) / magnification_xy; + float yc = (yM) / magnification_xy; + float zc = (sliceM) / magnification_z; float RGC = 0; float distanceWeightSum = 0; float distanceWeightSum_xy = 0; float distanceWeightSum_z = 0; - int _start = -(int)(Gx_Gy_MAGNIFICATION * fwhm); - int _end = (int)(Gx_Gy_MAGNIFICATION * fwhm + 1); + int _start = -(int)(2 * fwhm); + int _end = (int)(2 * fwhm + 1); - int _start_z = -(int)(Gz_MAGNIFICATION * fwhm_z); - int _end_z = (int)(Gz_MAGNIFICATION * fwhm_z + 1); + int _start_z = -(int)(2 * fwhm); + int _end_z = (int)(2 * fwhm + 1); for (int j = _start; j <= _end; j++) { - vy = ((float) ((int) (Gx_Gy_MAGNIFICATION*yc)) + j)/(float) Gx_Gy_MAGNIFICATION; + vy = yc + j; - if (0 < vy && vy < rowsM - 1) { + if (0 < vy && vy < rowsM/magnification_xy - 1) { for (int i = _start; i <= _end; i++) { - vx = ((float) ((int) (Gx_Gy_MAGNIFICATION*xc)) + i)/(float) Gx_Gy_MAGNIFICATION; + vx = xc + i; - if (0 < vx && vx < colsM - 1) { + if (0 < vx && vx < colsM/magnification_xy - 1) { for (int k = _start_z; k <= _end_z; k++) { - vz = ((float) ((int) (Gz_MAGNIFICATION*zc)) + k)/(float) Gz_MAGNIFICATION; + vz = zc + k; - if (0 < vz && vz < slicesM - 1) { + if (0 < vz && vz < slicesM/magnification_z - 1) { dx = vx - xc; dy = vy - yc; dz = vz - zc; @@ -161,9 +161,13 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn distance_z = dz_real; if (distance != 0 && distance_xy <= tSO && distance_z <= tSO_z) { - Gx = _c_get_bound_value(imIntGx, slicesM*Gz_MAGNIFICATION, rowsM*Gx_Gy_MAGNIFICATION, colsM*Gx_Gy_MAGNIFICATION, vz * magnification_z * Gz_MAGNIFICATION, magnification_xy * Gx_Gy_MAGNIFICATION * vy, magnification_xy * Gx_Gy_MAGNIFICATION * vx); - Gy = _c_get_bound_value(imIntGy, slicesM*Gz_MAGNIFICATION, rowsM*Gx_Gy_MAGNIFICATION, colsM*Gx_Gy_MAGNIFICATION, vz * magnification_z * Gz_MAGNIFICATION, magnification_xy * Gx_Gy_MAGNIFICATION * vy, magnification_xy * Gx_Gy_MAGNIFICATION * vx); - Gz = _c_get_bound_value(imIntGz, slicesM*Gz_MAGNIFICATION, rowsM*Gx_Gy_MAGNIFICATION, colsM*Gx_Gy_MAGNIFICATION, vz * magnification_z * Gz_MAGNIFICATION, magnification_xy * Gx_Gy_MAGNIFICATION * vy, magnification_xy * Gx_Gy_MAGNIFICATION * vx); + int linear_index = (int)(vz * magnification_z) * rowsM * colsM + + (int)(magnification_xy * vy) * colsM + + (int)(magnification_xy * vx); + + Gx = imIntGx[linear_index]; + Gy = imIntGy[linear_index]; + Gz = imIntGz[linear_index]; // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); @@ -197,6 +201,7 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn } return RGC; + //return imIntGy[(int)(zc * rowsM * colsM) + (int)(yc * colsM) + (int)(xc)]; } float _c_calculate_dk_3d(float dx, float dy, float dz, float Gx, float Gy, float Gz, float Gmag, float distance) { diff --git a/src/include/_c_sr_radial_gradient_convergence.h b/src/include/_c_sr_radial_gradient_convergence.h index 60d48153..4d89e987 100644 --- a/src/include/_c_sr_radial_gradient_convergence.h +++ b/src/include/_c_sr_radial_gradient_convergence.h @@ -21,8 +21,7 @@ double _c_calculate_dw_z(double distance_z, double tSS_z); double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance); -float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float Gx_Gy_MAGNIFICATION, float Gz_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity); - +float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity); float _c_calculate_rgc_3d(int cM, int rM, int sM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float Gx_Gy_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSS, float sensitivity); diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 78cb801c..3c702e8a 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -1,6 +1,9 @@ -# cython: infer_types=True, wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3, profile=False, autogen_pxd=False +<%! +schedulers = ['threaded','threaded_guided','threaded_dynamic','threaded_static', 'unthreaded'] +%># cython: infer_types=True, wraparound=True, nonecheck=False, boundscheck=False, cdivision=True, language_level=3, profile=False, autogen_pxd=False import numpy as np import math +import time cimport numpy as np from libc.math cimport floor @@ -12,12 +15,13 @@ from .sr_temporal_correlations import calculate_eSRRF_temporal_correlations from ._interpolation import interpolate_3d, interpolate_3d_zlinear from ._le_interpolation_catmull_rom import ShiftAndMagnify from ...__liquid_engine__ import LiquidEngine +from ...__opencl__ import cl, cl_array, _fastest_device cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil cdef extern from "_c_sr_radial_gradient_convergence.h": - float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float PSF_voxel_ratio, float Gx_Gy_MAGNIFICATION, float Gz_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil + float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil class eSRRF3D(LiquidEngine): """ @@ -27,379 +31,274 @@ class eSRRF3D(LiquidEngine): def __init__(self, clear_benchmarks=False, testing=False, verbose=True): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - self._gradients_s_interpolated = None - self._gradients_r_interpolated = None - self._gradients_c_interpolated = None - self.keep_gradients = False - self.keep_interpolated = False - self._img_interpolated = None - - def run(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True, keep_gradients=False, keep_interpolated = False, run_type=None): - self.keep_gradients = keep_gradients - self.keep_interpolated = keep_interpolated + + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio,sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) - - def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): - """ - @cpu - @threaded - @cython - """ - cdef float sigma = radius / 2.355 - cdef float fwhm = radius - cdef float tSS = 2 * sigma * sigma - cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio - cdef float tSS_z = 2 * sigma_z * sigma_z - cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 - cdef int _magnification_xy = magnification_xy - cdef int _magnification_z = magnification_z - cdef int _doIntensityWeighting = doIntensityWeighting - - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum - n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() - - with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag): - for cM in range(0, n_cols_mag): - if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + % for sch in schedulers: + def _run_${sch}(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu + % if sch!='unthreaded': @threaded + % endif @cython """ - cdef float sigma = radius / 2.355 - cdef float fwhm = radius - cdef float tSS = 2 * sigma * sigma - cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio - cdef float tSS_z = 2 * sigma_z * sigma_z - cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 - cdef int _magnification_xy = magnification_xy - cdef int _magnification_z = magnification_z - cdef int _doIntensityWeighting = doIntensityWeighting - - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum - n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() - - with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="guided"): - for cM in range(0, n_cols_mag): - if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): - """ - @cpu - @threaded - @cython - """ + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="dynamic"): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + % if sch=="unthreaded": + for rM in range(margin, n_rows_mag-margin): + % elif sch=="threaded": + for rM in prange(margin, n_rows_mag-margin): + % else: + for rM in prange(margin, n_rows_mag-margin, schedule="${sch.split('_')[1]}"): + % endif + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): - """ - @cpu - @threaded - @cython - """ - cdef float sigma = radius / 2.355 - cdef float fwhm = radius - cdef float tSS = 2 * sigma * sigma - cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio - cdef float tSS_z = 2 * sigma_z * sigma_z - cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 - cdef int _magnification_xy = magnification_xy - cdef int _magnification_z = magnification_z - cdef int _doIntensityWeighting = doIntensityWeighting - - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum - n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std + else: + return np.asarray(rgc_avg) + % endfor - with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="static"): - for cM in range(0, n_cols_mag): - if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ - @cpu - @cython + @gpu """ - cdef float sigma = radius / 2.355 - cdef float fwhm = radius - cdef float tSS = 2 * sigma * sigma - cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio - cdef float tSS_z = 2 * sigma_z * sigma_z - cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 - cdef int _magnification_xy = magnification_xy - cdef int _magnification_z = magnification_z - cdef int _doIntensityWeighting = doIntensityWeighting - - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum - n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() - - with nogil: - for sM in range(0, n_slices_mag): - for rM in range(0, n_rows_mag): - for cM in range(0, n_cols_mag): - if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - - def get_gradients(self): - if self._gradients_c_interpolated is None or self._gradients_r_interpolated is None or self._gradients_s_interpolated is None: - print("Gradients not yet calculated") + print("Here 1") + if device is None: + device = _fastest_device + cl_ctx = cl.Context([device['device']]) + dc = device['device'] + cl_queue = cl.CommandQueue(cl_ctx) + + output_shape = (image.shape[1] * magnification_z, image.shape[2] * magnification_xy, image.shape[3] * magnification_xy) + + output_image = np.zeros(output_shape, dtype=np.float32) + print("here before cl to array") + tmp_slice = cl_array.to_device(cl_queue, output_image) + + #max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) + #max_slices = self._check_max_slices(image, max_slices) + + max_slices = 1 + + mf = cl.mem_flags + print("Here 2") + # create cl buffer for input image + input_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(image[:, :, :, :].nbytes, dc, max_slices)) + cl.enqueue_copy(cl_queue, input_cl, image[:,:,:, :]).wait() + + # create magnified input image buffer + magnified_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + + # create gradients buffers + col_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + row_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + z_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + print("Here 3") + # create magnified gradients buffers + col_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + row_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + + # create the output buffer + #output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + output_cl = cl_array.to_device(cl_queue, output_image) + + esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) + esrrf3d_cl_prg = cl.Program(cl_ctx, esrrf3d_cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) + interp_knl = esrrf3d_cl_prg.interpolate_3d + grads_knl = esrrf3d_cl_prg.gradients_3d + rgc_knl = esrrf3d_cl_prg.calculate_rgc3D + + margin = int(radius*2) * magnification_xy + margin_z = int(radius*2) * magnification_z + lowest_row = margin # TODO discuss edges calculation + highest_row = output_shape[1] - margin + lowest_col = margin + highest_col = output_shape[2] - margin + lowest_z = margin_z + highest_z = output_shape[0] - margin_z + print("Here 4") + for f in range(image.shape[0]): + + interp_knl( + cl_queue, + (image.shape[1], image.shape[2], image.shape[3]), + None, + input_cl, + magnified_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(f) + ).wait() + print("Here 5") + + grads_knl( + cl_queue, + (image.shape[1], image.shape[2], image.shape[3]), + None, + input_cl, + col_gradients_cl, + row_gradients_cl, + z_gradients_cl, + np.int32(f), + np.int32(image.shape[1]), + np.int32(image.shape[2]), + np.int32(image.shape[3]), + ).wait() + print("Here 6") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + col_gradients_cl, + col_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(0) + ).wait() + print("Here 7") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + row_gradients_cl, + row_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(0) + ).wait() + print("Here 8") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + z_gradients_cl, + z_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(f) + ).wait() + print("Here 9") + rgc_knl( + cl_queue, + (highest_z-lowest_z, highest_row-lowest_row, highest_col-lowest_col), + None, + col_magnified_gradients_cl, + row_magnified_gradients_cl, + z_magnified_gradients_cl, + magnified_cl, + tmp_slice.data, + np.int32(output_shape[1]), + np.int32(output_shape[2]), + np.int32(output_shape[0]), + np.int32(magnification_xy), + np.int32(magnification_z), + np.float32(voxel_ratio), + np.float32(radius), + np.float32(2 * (radius/2.355) + 1), + np.float32(2 * (radius*voxel_ratio/2.355) + 1), + np.float32(2 * (radius/2.355) * (radius/2.355)), + np.float32(2 * (radius*voxel_ratio/2.355) * (radius*voxel_ratio/2.355)), + np.int32(sensitivity), + np.int32(doIntensityWeighting), + np.int32(f) + + ).wait() + + print("Here 10") + # TODO change tmp_slice to cl arrays + if mode == "average": + output_cl = output_cl + (tmp_slice - output_cl) / (f + 1) + elif mode == "std": + delta = tmp_slice - output_cl + output_cl = output_cl + (delta) / (f + 1) + delta_2 = tmp_slice - output_cl + output_cl = output_cl + (delta * delta_2) + print("Here 11") + + cl.enqueue_copy(cl_queue, output_image, output_cl).wait() + print("Here 12") + if mode == "std": + output_image = np.sqrt(np.asarray(output_image) / image.shape[0]) + return output_image else: - return self._gradients_c_interpolated, self._gradients_r_interpolated, self._gradients_s_interpolated - - def get_interpolated_image(self): - return self._img_interpolated \ No newline at end of file + return np.asarray(output_image) \ No newline at end of file diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl new file mode 100644 index 00000000..44ccb093 --- /dev/null +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -0,0 +1,238 @@ +void _c_gradient_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int slices, + int rows, int cols) { + float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; + + int z_i, y_i, x_i, z_1, y_1, x_1; + + for (z_i = 0; z_i < slices; z_i++) { + for (y_i = 0; y_i < rows; y_i++) { + for (x_i = 0; x_i < cols; x_i++) { + + z_1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; + y_1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; + x_1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; + ip0 = image[z_i * rows * cols + y_i * cols + x_i]; + ip1 = image[z_i * rows * cols + y_i * cols + x_1]; + ip2 = image[z_i * rows * cols + y_1 * cols + x_i]; + ip3 = image[z_i * rows * cols + y_1 * cols + x_1]; + ip4 = image[z_1 * rows * cols + y_i * cols + x_i]; + ip5 = image[z_1 * rows * cols + y_i * cols + x_1]; + ip6 = image[z_1 * rows * cols + y_1 * cols + x_i]; + ip7 = image[z_1 * rows * cols + y_1 * cols + x_1]; + imGc[z_i * rows* cols + y_i * cols + x_i] = + (ip1 + ip3 + ip5 + ip7 - ip0 - ip2 - ip4 - ip6) / 4; + imGr[z_i * rows* cols + y_i * cols + x_i] = + (ip2 + ip3 + ip6 + ip7 - ip0 - ip1 - ip4 - ip5) / 4; + imGs[z_i * rows* cols + y_i * cols + x_i] = + (ip4 + ip5 + ip6 + ip7 - ip0 - ip1 - ip2 - ip3) / 4; + } + } + } +} + +double _c_calculate_dw3D_isotropic(double distance, double tSS) { + return pow((distance * exp(-(distance * distance) / tSS)), 4); +} + +double _c_calculate_dw3D(double distance, double distance_xy, double distance_z,double tSS, double tSS_z) { + float D_weight_xy, D_weight_z, D_weight; + D_weight_xy = (exp((-distance_xy * distance_xy) / tSS)); + D_weight_z = (exp((-distance_z * distance_z) / tSS_z)); + D_weight = distance * (D_weight_xy * D_weight_z); + return pow(D_weight, 4); +} + +double _c_calculate_dw_xy(double distance_xy, double tSS) { + return pow((distance_xy * exp((-distance_xy * distance_xy) / tSS)), 4); +} + +double _c_calculate_dw_z(double distance_z, double tSS_z) { + return pow((distance_z * exp((-distance_z * distance_z) / tSS_z)), 4); +} + +double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { + float Dk = sqrt((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); + if (isnan(Dk)) { + Dk = distance; + } + Dk = 1 - Dk / distance; + return Dk; +} + +float _c_get_bound_value(float* im, int slices, int rows, int cols, int s, int r, int c){ + int _s = s > 0 ? s : 0; + _s = _s < slices - 1 ? _s : slices - 1; + int _r = r > 0 ? r : 0; + _r = _r < rows - 1 ? _r : rows - 1; + int _c = c > 0 ? c : 0; + _c = _c < cols - 1 ? _c : cols - 1; + + return im[_s * rows * cols + _r * cols + _c]; +} + +float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __global float* imIntGy, __global float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { + + float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, distanceWeight_xy, distanceWeight_z, GdotR, Dk; + + float xc = (xM) / magnification_xy; + float yc = (yM) / magnification_xy; + float zc = (sliceM) / magnification_z; + + float RGC = 0; + float distanceWeightSum = 0; + float distanceWeightSum_xy = 0; + float distanceWeightSum_z = 0; + + int _start = -(int)(2 * fwhm); + int _end = (int)(2 * fwhm + 1); + + int _start_z = -(int)(2 * fwhm); + int _end_z = (int)(2 * fwhm + 1); + + for (int j = _start; j <= _end; j++) { + vy = yc + j; + + if (0 < vy && vy < rowsM/magnification_xy - 1) { + for (int i = _start; i <= _end; i++) { + vx = xc + i; + + if (0 < vx && vx < colsM/magnification_xy - 1) { + for (int k = _start_z; k <= _end_z; k++) { + vz = zc + k; + + if (0 < vz && vz < slicesM/magnification_z - 1) { + dx = vx - xc; + dy = vy - yc; + dz = vz - zc; + dz_real = dz * ratio_px; // This has been already divided by magnification_z + distance = sqrt(dx * dx + dy * dy + dz_real * dz_real); + distance_xy = sqrt(dx * dx + dy * dy); + distance_z = dz_real; + + if (distance != 0 && distance_xy <= tSO && distance_z <= tSO_z) { + int linear_index = (int)(vz * magnification_z) * rowsM * colsM + + (int)(magnification_xy * vy) * colsM + + (int)(magnification_xy * vx); + + Gx = imIntGx[linear_index]; + Gy = imIntGy[linear_index]; + Gz = imIntGz[linear_index]; + + // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); + distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); + + // distanceWeight_xy = _c_calculate_dw_xy(distance_xy, tSS); + // distanceWeight_z = _c_calculate_dw_z(distance_z, tSS_z); + + distanceWeightSum += distanceWeight; + // distanceWeightSum_xy += distanceWeight_xy; + // distanceWeightSum_z += distanceWeight_z; + GdotR = Gx*dx + Gy*dy + Gz*dz_real; + + if (GdotR < 0) { + Dk = _c_calculate_dk3D(Gx, Gy, Gz, dx, dy, dz_real, distance); + RGC += (Dk * distanceWeight); + } + } + } + } + } + } + } + } + + RGC /= distanceWeightSum; + + if (RGC >= 0) { + RGC = pow(RGC, sensitivity); + } else { + RGC = 0; + } + + return RGC; + //return imIntGy[(int)(zc * rowsM * colsM) + (int)(yc * colsM) + (int)(xc)]; +} + +float catmull_rom_weight(float t, int offset) { + // Catmull-Rom spline weight calculation + t = fabs(t - offset); + if (t < 1.0f) { + return 1.0f - 2.0f * t * t + t * t * t; + } else if (t < 2.0f) { + return 4.0f - 8.0f * t + 5.0f * t * t - t * t * t; + } + return 0.0f; +} + + +__kernel void interpolate_3d(__global float* image, __global float* magnified_image, int magnification_xy, int magnification_z, int f) { + int s = get_global_id(0); + int row = get_global_id(1); + int col = get_global_id(2); + + int slices = get_global_size(0) / magnification_z; + int rows = get_global_size(1) / magnification_xy; + int cols = get_global_size(2) / magnification_xy; + + // Linear interpolation in z + float z_ratio = (float)s / magnification_z; + int z0 = (int)floor(z_ratio); + int z1 = z0 + 1; + z1 = z1 < slices ? z1 : slices - 1; + float z_weight = z_ratio - z0; + + // Bicubic interpolation in xy + float y_ratio = (float)row / magnification_xy; + float x_ratio = (float)col / magnification_xy; + int y0 = (int)floor(y_ratio); + int x0 = (int)floor(x_ratio); + float y_weight = y_ratio - y0; + float x_weight = x_ratio - x0; + + float result = 0.0f; + for (int dz = 0; dz <= 1; dz++) { + int z = dz == 0 ? z0 : z1; + float z_contrib = dz == 0 ? (1 - z_weight) : z_weight; + + for (int dy = -1; dy <= 2; dy++) { + int y = y0 + dy; + y = y > 0 ? (y < rows ? y : rows - 1) : 0; + float y_contrib = catmull_rom_weight(y_weight, dy); + + for (int dx = -1; dx <= 2; dx++) { + int x = x0 + dx; + x = x > 0 ? (x < cols ? x : cols - 1) : 0; + float x_contrib = catmull_rom_weight(x_weight, dx); + + float pixel_value = image[f * slices * rows * cols + z * rows * cols + y * cols + x]; + result += pixel_value * z_contrib * y_contrib * x_contrib; + } + } + } + + magnified_image[s * get_global_size(1) * get_global_size(2) + row * get_global_size(2) + col] = result; +} + +__kernel void gradients_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int f, int slices, int rows, int cols) { + _c_gradient_3d(&image[f * slices * rows * cols], &imGc[f * slices * rows * cols], &imGr[f * slices * rows * cols], &imGs[f * slices * rows * cols], slices, rows, cols); +} + +__kernel void calculate_rgc3D(__global float* imIntGx, __global float* imIntGy, __global float* imIntGz, __global float* imM, __global float* tmp_slice, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSS, float tSO_z, float tSS_z, int sensitivity, int doIntensityWeighting, int f) { + + // Index of the current pixel + int s = get_global_id(0); + int row = get_global_id(1); + int col = get_global_id(2); + + // Output image dimensions + int nPixels_out = slicesM * rowsM * colsM; + + row = row + fwhm*2*magnification_xy; + col = col + fwhm*2*magnification_xy; + s = s + fwhm*2*magnification_z; + if (doIntensityWeighting == 1) { + tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[slicesM * rowsM * colsM], &imIntGy[slicesM * rowsM * colsM], &imIntGz[slicesM * rowsM * colsM], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[f * nPixels_out + s * rowsM * colsM + row * colsM + col]; + } else { + tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[slicesM * rowsM * colsM], &imIntGy[slicesM * rowsM * colsM], &imIntGz[slicesM * rowsM * colsM], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity); + } +} \ No newline at end of file diff --git a/src/nanopyx/methods/esrrf_3d/run.py b/src/nanopyx/methods/esrrf_3d/run.py index 4ac718b7..0d3dd4b3 100644 --- a/src/nanopyx/methods/esrrf_3d/run.py +++ b/src/nanopyx/methods/esrrf_3d/run.py @@ -5,33 +5,37 @@ def run_esrrf3d( - img, correlation="AVG", framewindow=5, rollingoverlap=2, **kwargs + img, + mode="average", + magnification_xy=2, + magnification_z=2, + radius=1.5, + sensitivity=1, + voxel_ratio=4, + doIntensityWeighting=True, + **kwargs, ): """ - Calculate the eSRRF3D temporal correlations for the given image. + Calculate the eSRRF3D temporal correlations for the given 3D image. - Args: - img: The input 3D image. - magnification_xy: The magnification factor for the x and y axes. - magnification_z: The magnification factor for the z axis. - radius: The radius for the xy plane. - radius_z: The radius for the z axis. - sensitivity: The sensitivity for the calculation. - doIntensityWeighting: Whether to perform intensity weighting. - run_type: The type of the run. - keep_gradients: Whether to keep the gradients. - keep_interpolated: Whether to keep the interpolated values. - correlation: The type of correlation to use. - framewindow: The window size for frame. - rollingoverlap: The overlap size for rolling. + img (ndarray): The input 4D image. + mode (str, optional): The mode of time projection, default is "average", other option is "std". + magnification_xy (float, optional): The magnification factor for the x and y axes. Default is 2. + magnification_z (float, optional): The magnification factor for the z axis. Default is 2. + radius (float, optional): The radius for the xy plane. Default is 1.5. + sensitivity (float, optional): The sensitivity for the calculation. Default is 1. + voxel_ratio (float, optional): The ratio of voxel dimensions (z to xy). Default is 4. + doIntensityWeighting (bool, optional): Whether to perform intensity weighting. Default is True. + **kwargs: Additional parameters for the eSRRF3D calculation, including: + - radius_z (float, optional): The radius for the z axis. + - run_type (str, optional): The type of the run. + - keep_gradients (bool, optional): Whether to keep the gradients. + - keep_interpolated (bool, optional): Whether to keep the interpolated values. + - correlation (str, optional): The type of correlation to use. + - framewindow (int, optional): The window size for frames. + - rollingoverlap (int, optional): The overlap size for rolling. - Returns: - The calculated eSRRF3D temporal correlations. + ndarray: The calculated eSRRF3D temporal correlations. """ esrrf_calculator = eSRRF3D() - return calculate_eSRRF3d_temporal_correlations( - esrrf_calculator.run(img, **kwargs), - correlation=correlation, - framewindow=framewindow, - rollingoverlap=rollingoverlap, - ) + return esrrf_calculator.run(img, **kwargs) From 0aaf732ef2182c038716a3945507b7edb2701708 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:01:41 +0100 Subject: [PATCH 04/82] added 3.13 support --- .../workflows/buildtest_mac_arm_wheels.yml | 2 +- .github/workflows/buildtest_mac_x86.yml | 2 +- .github/workflows/buildtest_mac_x86_local.yml | 2 +- .../workflows/buildtest_manylinux_wheels.yml | 2 +- .github/workflows/buildtest_windows_x86.yml | 3 +- .github/workflows/nanopyx_night_mechanic.yml | 4 +- .github/workflows/nanopyx_oncall_all_os.yml | 3 +- README.md | 2 +- noxfile.py | 2 +- pyproject.toml | 8 +- src/nanopyx/core/transform/_le_esrrf3d.pyx | 684 +++++++++++------- 11 files changed, 452 insertions(+), 262 deletions(-) diff --git a/.github/workflows/buildtest_mac_arm_wheels.yml b/.github/workflows/buildtest_mac_arm_wheels.yml index 6329c7e4..4be1da32 100644 --- a/.github/workflows/buildtest_mac_arm_wheels.yml +++ b/.github/workflows/buildtest_mac_arm_wheels.yml @@ -23,7 +23,7 @@ jobs: run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: - CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 + CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 CIBW_ARCHS: arm64 - name: Test wheels diff --git a/.github/workflows/buildtest_mac_x86.yml b/.github/workflows/buildtest_mac_x86.yml index cf1cf4d0..5e82ff92 100644 --- a/.github/workflows/buildtest_mac_x86.yml +++ b/.github/workflows/buildtest_mac_x86.yml @@ -22,7 +22,7 @@ jobs: - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: cp39-macosx_x86_64 cp310-macosx_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 + CIBW_BUILD: cp39-macosx_x86_64 cp310-macosx_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 cp313-macosx_x86_64 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/.github/workflows/buildtest_mac_x86_local.yml b/.github/workflows/buildtest_mac_x86_local.yml index d6805645..479a913d 100644 --- a/.github/workflows/buildtest_mac_x86_local.yml +++ b/.github/workflows/buildtest_mac_x86_local.yml @@ -22,7 +22,7 @@ jobs: - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: cp39-macosx_x86_64 cp310-macosx_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 + CIBW_BUILD: cp39-macosx_x86_64 cp310-macosx_x86_64 cp311-macosx_x86_64 cp312-macosx_x86_64 cp313-macosx_x86_64 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/.github/workflows/buildtest_manylinux_wheels.yml b/.github/workflows/buildtest_manylinux_wheels.yml index 1aec375f..04e1144a 100644 --- a/.github/workflows/buildtest_manylinux_wheels.yml +++ b/.github/workflows/buildtest_manylinux_wheels.yml @@ -21,7 +21,7 @@ jobs: - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 + CIBW_BUILD: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/.github/workflows/buildtest_windows_x86.yml b/.github/workflows/buildtest_windows_x86.yml index ebb52294..2fef6529 100644 --- a/.github/workflows/buildtest_windows_x86.yml +++ b/.github/workflows/buildtest_windows_x86.yml @@ -37,6 +37,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Install cibuildwheel run: python -m pip install cibuildwheel==2.16.2 @@ -44,7 +45,7 @@ jobs: - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 + CIBW_BUILD: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index 4b74883e..de4f5534 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -33,6 +33,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -61,6 +62,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -99,7 +101,7 @@ jobs: run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: - CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 + CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 CIBW_ARCHS: arm64 CIBW_BUILD_VERBOSITY: 1 CIBW_TEST_COMMAND: "pytest {project}/tests" diff --git a/.github/workflows/nanopyx_oncall_all_os.yml b/.github/workflows/nanopyx_oncall_all_os.yml index 2eb74443..9cd0897f 100644 --- a/.github/workflows/nanopyx_oncall_all_os.yml +++ b/.github/workflows/nanopyx_oncall_all_os.yml @@ -84,7 +84,8 @@ jobs: 3.9 3.10 3.11 - 3.12 + + 3.13 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/README.md b/README.md index 43ad0d7d..905e422d 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ pip install napari-nanopyx ## Installation -`NanoPyx` is compatible and tested with Python 3.9, 3.10, 3.11 and 3.12 in MacOS, Windows and Linux. Installation time depends on your hardware and internet connection, but should take around 5 minutes. +`NanoPyx` is compatible and tested with Python 3.9, 3.10, 3.11, 3.12 and 3.13 in MacOS, Windows and Linux. Installation time depends on your hardware and internet connection, but should take around 5 minutes. You can install `NanoPyx` via [pip]: diff --git a/noxfile.py b/noxfile.py index f463dde8..32f433bb 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ import nox DIR = Path(__file__).parent.resolve() -PYTHON_ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12"] +PYTHON_ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13"] PYTHON_DEFAULT_VERSION = "3.10" # Platform logic diff --git a/pyproject.toml b/pyproject.toml index 7e0e8090..d928ef43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ build-backend = "setuptools.build_meta" name = "nanopyx" description = "Nanoscopy Python library (NanoPyx, the successor to NanoJ) - focused on light microscopy and super-resolution imaging" readme = "README.md" -requires-python = ">=3.9,<3.13" +requires-python = ">=3.9" license = { file = "LICENSE.txt" } keywords = [ "NanoJ", @@ -39,7 +39,7 @@ dependencies = [ "liquid_engine", "mako>=1.3.0", "cython>=0.29.32", - "numpy>=1.22,<2", + "numpy>=1.22", "scipy>=1.8", "tifffile>=2022.5.4", "scikit-image>=0.19.2", @@ -169,7 +169,7 @@ omit = [ [tool.cibuildwheel] # https://cibuildwheel.readthedocs.io/en/stable/options/ skip = ["pp*", "*musllinux*"] -build = ["cp39-*", "cp310-*", "cp311-*"] +build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] # it is not possible to test arm64 and the arm64 part of a universal2 wheel on this CI platform test-skip = ["*arm64"] # build-frontend = "pip" @@ -198,7 +198,7 @@ manylinux-x86_64-image = "manylinux2014" # repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}" [tool.cibuildwheel.macos] -archs = ["x86_64", "arm64"] +archs = ["arm64"] before-all = ["brew install llvm libomp"] test-requires = ["nanopyx[test]"] #test-command = "pytest -n=1 --timeout=1200 {project}/tests" diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 78cb801c..345a6155 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -1,6 +1,7 @@ -# cython: infer_types=True, wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3, profile=False, autogen_pxd=False +# cython: infer_types=True, wraparound=True, nonecheck=False, boundscheck=False, cdivision=True, language_level=3, profile=False, autogen_pxd=False import numpy as np import math +import time cimport numpy as np from libc.math cimport floor @@ -12,12 +13,13 @@ from .sr_temporal_correlations import calculate_eSRRF_temporal_correlations from ._interpolation import interpolate_3d, interpolate_3d_zlinear from ._le_interpolation_catmull_rom import ShiftAndMagnify from ...__liquid_engine__ import LiquidEngine +from ...__opencl__ import cl, cl_array, _fastest_device cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil cdef extern from "_c_sr_radial_gradient_convergence.h": - float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float PSF_voxel_ratio, float Gx_Gy_MAGNIFICATION, float Gz_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil + float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil class eSRRF3D(LiquidEngine): """ @@ -27,379 +29,563 @@ class eSRRF3D(LiquidEngine): def __init__(self, clear_benchmarks=False, testing=False, verbose=True): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - self._gradients_s_interpolated = None - self._gradients_r_interpolated = None - self._gradients_c_interpolated = None - self.keep_gradients = False - self.keep_interpolated = False - self._img_interpolated = None - - def run(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True, keep_gradients=False, keep_interpolated = False, run_type=None): - self.keep_gradients = keep_gradients - self.keep_interpolated = keep_interpolated + + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio,sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, PSF_voxel_ratio=PSF_voxel_ratio, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) - def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @cython """ + + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + for rM in prange(margin, n_rows_mag-margin): + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std + else: + return np.asarray(rgc_avg) + def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @cython """ + + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) - - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="guided"): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + for rM in prange(margin, n_rows_mag-margin, schedule="guided"): + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std + else: + return np.asarray(rgc_avg) + def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @cython """ + + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="dynamic"): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + for rM in prange(margin, n_rows_mag-margin, schedule="dynamic"): + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std + else: + return np.asarray(rgc_avg) + def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @cython """ + + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] - - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) - - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in prange(0, n_rows_mag, schedule="static"): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + for rM in prange(margin, n_rows_mag-margin, schedule="static"): + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, PSF_voxel_ratio: float = 4.0, sensitivity: float = 1, doIntensityWeighting: bool = True): + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std + else: + return np.asarray(rgc_avg) + def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @cython """ + + time_start = time.time() + # calculate all constants cdef float sigma = radius / 2.355 cdef float fwhm = radius + cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * PSF_voxel_ratio / 2.355 # Taking voxel size into account - cdef float fwhm_z = radius * PSF_voxel_ratio + cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 - cdef int Gx_Gy_MAGNIFICATION = 2 cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z + cdef float _voxel_ratio = voxel_ratio cdef int _doIntensityWeighting = doIntensityWeighting - cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum + cdef int n_frames, n_slices, n_rows, n_cols, n_slices_mag, n_rows_mag, n_cols_mag n_frames, n_slices, n_rows, n_cols = image.shape[0], image.shape[1], image.shape[2], image.shape[3] - - cdef float[:, :, :, :] rgc_map = np.zeros((n_frames, n_slices*magnification_z, n_rows*magnification_xy, n_cols*magnification_xy), dtype=np.float32) + n_slices_mag = n_slices * _magnification_z + n_rows_mag = n_rows * _magnification_xy + n_cols_mag = n_cols * _magnification_xy - cdef float[:, :, :] image_interpolated, gradients_s, gradients_r, gradients_c, gradients_s_interpolated, gradients_r_interpolated, gradients_c_interpolated, padded, img_dum - - cdef int f, n_slices_mag, n_rows_mag, n_cols_mag, sM, rM, cM, z0 - - cdef float rgc_val, zcof - - for f in range(n_frames): - image_interpolated = interpolate_3d_zlinear(image[f,:,:,:], _magnification_xy, _magnification_z) - - n_slices_mag, n_rows_mag, n_cols_mag = image_interpolated.shape[0], image_interpolated.shape[1], image_interpolated.shape[2] - - img_dum = interpolate_3d_zlinear(image[f], Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION) - n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum = img_dum.shape[0], img_dum.shape[1], img_dum.shape[2] + # create all necessary arrays + cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_std + if mode == "std": + rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_slices = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) + cdef float[:, :, :] gradients_col_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_row_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] gradients_slices_mag = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float delta, delta_2, rgc_val + cdef int f_i, sM, rM, cM + + for f_i in range(n_frames): + # interpolate frame + image_interpolated = interpolate_3d_zlinear(image[f_i,:,:,:], _magnification_xy, _magnification_z) - gradients_c = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_r = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) - gradients_s = np.zeros((n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum), dtype=np.float32) with nogil: - _c_gradient_3d(&img_dum[0, 0, 0], &gradients_c[0, 0, 0], &gradients_r[0, 0, 0], &gradients_s[0, 0, 0], n_slices_mag_dum, n_rows_mag_dum, n_cols_mag_dum) + # calculate gradients + _c_gradient_3d(&image[f_i, 0, 0, 0], &gradients_col[0, 0, 0], &gradients_row[0, 0, 0], &gradients_slices[0, 0, 0], n_slices, n_rows, n_cols) - gradients_s_interpolated = interpolate_3d_zlinear(gradients_s, _magnification_xy, _magnification_z) - gradients_r_interpolated = interpolate_3d_zlinear(gradients_r, _magnification_xy, _magnification_z) - gradients_c_interpolated = interpolate_3d_zlinear(gradients_c, _magnification_xy, _magnification_z) - - if self.keep_gradients: - self._gradients_s_interpolated = gradients_s_interpolated.copy() - self._gradients_r_interpolated = gradients_r_interpolated.copy() - self._gradients_c_interpolated = gradients_c_interpolated.copy() - - if self.keep_interpolated: - self._img_interpolated = img_dum.copy() + # interpolate gradients + gradients_slices_mag = interpolate_3d_zlinear(gradients_slices, _magnification_xy, _magnification_z) + gradients_row_mag = interpolate_3d_zlinear(gradients_row, _magnification_xy, _magnification_z) + gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(0, n_slices_mag): - for rM in range(0, n_rows_mag): - for cM in range(0, n_cols_mag): + for sM in range(margin, n_slices_mag-margin): + for rM in range(margin, n_rows_mag-margin): + for cM in range(margin, n_cols_mag-margin): + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val * image_interpolated[sM, rM, cM] - else: - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_c_interpolated[0,0,0], &gradients_r_interpolated[0,0,0], &gradients_s_interpolated[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, PSF_voxel_ratio, Gx_Gy_MAGNIFICATION, Gx_Gy_MAGNIFICATION, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) - rgc_map[f, sM, rM, cM] = rgc_val - - return np.asarray(rgc_map) - - def get_gradients(self): - if self._gradients_c_interpolated is None or self._gradients_r_interpolated is None or self._gradients_s_interpolated is None: - print("Gradients not yet calculated") + rgc_val = rgc_val * image_interpolated[sM, rM, cM] + if mode == "average": + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + elif mode == "std": + delta = rgc_val - rgc_avg[sM, rM, cM] + rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_avg[sM, rM, cM] + rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + if mode == "std": + rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) + return rgc_std else: - return self._gradients_c_interpolated, self._gradients_r_interpolated, self._gradients_s_interpolated + return np.asarray(rgc_avg) - def get_interpolated_image(self): - return self._img_interpolated \ No newline at end of file + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): + """ + @gpu + """ + print("Here 1") + if device is None: + device = _fastest_device + cl_ctx = cl.Context([device['device']]) + dc = device['device'] + cl_queue = cl.CommandQueue(cl_ctx) + + output_shape = (image.shape[1] * magnification_z, image.shape[2] * magnification_xy, image.shape[3] * magnification_xy) + + output_image = np.zeros(output_shape, dtype=np.float32) + print("here before cl to array") + tmp_slice = cl_array.to_device(cl_queue, output_image) + + #max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) + #max_slices = self._check_max_slices(image, max_slices) + + max_slices = 1 + + mf = cl.mem_flags + print("Here 2") + # create cl buffer for input image + input_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(image[:, :, :, :].nbytes, dc, max_slices)) + cl.enqueue_copy(cl_queue, input_cl, image[:,:,:, :]).wait() + + # create magnified input image buffer + magnified_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + + # create gradients buffers + col_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + row_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + z_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) + print("Here 3") + # create magnified gradients buffers + col_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + row_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + + # create the output buffer + #output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + output_cl = cl_array.to_device(cl_queue, output_image) + + esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) + esrrf3d_cl_prg = cl.Program(cl_ctx, esrrf3d_cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) + interp_knl = esrrf3d_cl_prg.interpolate_3d + grads_knl = esrrf3d_cl_prg.gradients_3d + rgc_knl = esrrf3d_cl_prg.calculate_rgc3D + + margin = int(radius*2) * magnification_xy + margin_z = int(radius*2) * magnification_z + lowest_row = margin # TODO discuss edges calculation + highest_row = output_shape[1] - margin + lowest_col = margin + highest_col = output_shape[2] - margin + lowest_z = margin_z + highest_z = output_shape[0] - margin_z + print("Here 4") + for f in range(image.shape[0]): + + interp_knl( + cl_queue, + (image.shape[1], image.shape[2], image.shape[3]), + None, + input_cl, + magnified_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(f) + ).wait() + print("Here 5") + + grads_knl( + cl_queue, + (image.shape[1], image.shape[2], image.shape[3]), + None, + input_cl, + col_gradients_cl, + row_gradients_cl, + z_gradients_cl, + np.int32(f), + np.int32(image.shape[1]), + np.int32(image.shape[2]), + np.int32(image.shape[3]), + ).wait() + print("Here 6") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + col_gradients_cl, + col_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(0) + ).wait() + print("Here 7") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + row_gradients_cl, + row_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(0) + ).wait() + print("Here 8") + interp_knl( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + z_gradients_cl, + z_magnified_gradients_cl, + np.int32(magnification_xy), + np.int32(magnification_z), + np.int32(f) + ).wait() + print("Here 9") + rgc_knl( + cl_queue, + (highest_z-lowest_z, highest_row-lowest_row, highest_col-lowest_col), + None, + col_magnified_gradients_cl, + row_magnified_gradients_cl, + z_magnified_gradients_cl, + magnified_cl, + tmp_slice.data, + np.int32(output_shape[1]), + np.int32(output_shape[2]), + np.int32(output_shape[0]), + np.int32(magnification_xy), + np.int32(magnification_z), + np.float32(voxel_ratio), + np.float32(radius), + np.float32(2 * (radius/2.355) + 1), + np.float32(2 * (radius*voxel_ratio/2.355) + 1), + np.float32(2 * (radius/2.355) * (radius/2.355)), + np.float32(2 * (radius*voxel_ratio/2.355) * (radius*voxel_ratio/2.355)), + np.int32(sensitivity), + np.int32(doIntensityWeighting), + np.int32(f) + + ).wait() + + print("Here 10") + # TODO change tmp_slice to cl arrays + if mode == "average": + output_cl = output_cl + (tmp_slice - output_cl) / (f + 1) + elif mode == "std": + delta = tmp_slice - output_cl + output_cl = output_cl + (delta) / (f + 1) + delta_2 = tmp_slice - output_cl + output_cl = output_cl + (delta * delta_2) + print("Here 11") + + cl.enqueue_copy(cl_queue, output_image, output_cl).wait() + print("Here 12") + if mode == "std": + output_image = np.sqrt(np.asarray(output_image) / image.shape[0]) + return output_image + else: + return np.asarray(output_image) \ No newline at end of file From 58f1fb5d5861d8b19e200ca28c7fd0f13361f156 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:10:17 +0100 Subject: [PATCH 05/82] version bump to current --- .github/workflows/nanopyx_night_mechanic.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index de4f5534..95a05b64 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -92,6 +92,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Install dependencies run: | python -m pip install --upgrade pip From c76d6888a4113cae25ae10dbea234a7c51d651f1 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:10:26 +0100 Subject: [PATCH 06/82] version bum to current --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a0c99556..357b9926 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ ] EXTRA_LING_ARGS = [] -VERSION = "1.0.0" # sets version number for whole package +VERSION = "1.1.0" # sets version number for whole package def run_command(command: str) -> str: From 203b052e6a147b8492f39d57b6ea3270028fe1f7 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:13:54 +0100 Subject: [PATCH 07/82] updating to add 3.13 support --- .github/workflows/build_test_deploy_all_wheels.yml | 6 +++--- .github/workflows/nanopyx_oncall_all_os.yml | 4 +++- .github/workflows/nanopyx_oncall_mechanic.yml | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_test_deploy_all_wheels.yml b/.github/workflows/build_test_deploy_all_wheels.yml index dc2b37a7..cbf12e37 100644 --- a/.github/workflows/build_test_deploy_all_wheels.yml +++ b/.github/workflows/build_test_deploy_all_wheels.yml @@ -13,15 +13,15 @@ jobs: - os: macOS-ARM runner: [self-hosted, macOS, ARM64] archs: arm64 - cibw_build: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 + cibw_build: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 - os: manylinux runner: [self-hosted, Ubuntu, Native] archs: auto - cibw_build: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 + cibw_build: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 - os: windows runner: [self-hosted, Windows] archs: auto - cibw_build: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 + cibw_build: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/nanopyx_oncall_all_os.yml b/.github/workflows/nanopyx_oncall_all_os.yml index 9cd0897f..78922da7 100644 --- a/.github/workflows/nanopyx_oncall_all_os.yml +++ b/.github/workflows/nanopyx_oncall_all_os.yml @@ -29,6 +29,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -57,6 +58,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -84,7 +86,7 @@ jobs: 3.9 3.10 3.11 - + 3.12 3.13 - name: Install dependencies run: | diff --git a/.github/workflows/nanopyx_oncall_mechanic.yml b/.github/workflows/nanopyx_oncall_mechanic.yml index ad81563e..a1913a87 100644 --- a/.github/workflows/nanopyx_oncall_mechanic.yml +++ b/.github/workflows/nanopyx_oncall_mechanic.yml @@ -42,6 +42,7 @@ jobs: 3.10 3.11 3.12 + 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip From c33822f92c93a38ff1f57689cee0d338db6fd34c Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:23:51 +0100 Subject: [PATCH 08/82] notebooks update --- notebooks/ChannelRegistration.ipynb | 14 ++++----- notebooks/DriftCorrection.ipynb | 14 ++++----- notebooks/ExampleDataSRRFandQC.ipynb | 40 +++++++++++++------------- notebooks/NonLocalMeansDenoising.ipynb | 12 ++++---- notebooks/ParamSweepandeSRRF.ipynb | 34 +++++++++++----------- notebooks/SRMetrics.ipynb | 34 +++++++++++----------- notebooks/SRRFandQC.ipynb | 32 ++++++++++----------- notebooks/eSRRFandQC.ipynb | 32 ++++++++++----------- 8 files changed, 106 insertions(+), 106 deletions(-) diff --git a/notebooks/ChannelRegistration.ipynb b/notebooks/ChannelRegistration.ipynb index 805d70eb..edb0285b 100644 --- a/notebooks/ChannelRegistration.ipynb +++ b/notebooks/ChannelRegistration.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "32b72803", + "id": "987d17ec", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3ffbd338", + "id": "c9fa0882", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ef92de59", + "id": "b00c81de", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "037b9135", + "id": "2d33c8c7", "metadata": {}, "source": [ "# Channel Registration Parameters: \n", @@ -182,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "957674bc", + "id": "7c670165", "metadata": { "cellView": "form" }, @@ -250,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "a2790a79", + "id": "3161bf61", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated translation mask\n", @@ -260,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a3495a46", + "id": "ae29f67d", "metadata": { "cellView": "form" }, diff --git a/notebooks/DriftCorrection.ipynb b/notebooks/DriftCorrection.ipynb index 2c14530c..9998ea0c 100644 --- a/notebooks/DriftCorrection.ipynb +++ b/notebooks/DriftCorrection.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "5578943b", + "id": "d089d246", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1b387091", + "id": "ce77969c", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44ddfb89", + "id": "65efd170", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "efe0e892", + "id": "d9fd88e8", "metadata": {}, "source": [ "# Drift Correction Parameters: \n", @@ -181,7 +181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "935acd10", + "id": "aff3bb2c", "metadata": { "cellView": "form" }, @@ -274,7 +274,7 @@ }, { "cell_type": "markdown", - "id": "9fe2c429", + "id": "64984ecf", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated drift table\n", @@ -284,7 +284,7 @@ { "cell_type": "code", "execution_count": null, - "id": "406f16c8", + "id": "06356b7f", "metadata": { "cellView": "form" }, diff --git a/notebooks/ExampleDataSRRFandQC.ipynb b/notebooks/ExampleDataSRRFandQC.ipynb index 5df932f6..6b06ce62 100644 --- a/notebooks/ExampleDataSRRFandQC.ipynb +++ b/notebooks/ExampleDataSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "9c299982", + "id": "48884b92", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9542cc57", + "id": "032e6230", "metadata": { "cellView": "form" }, @@ -48,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7ef15b6c", + "id": "d90912be", "metadata": { "cellView": "form" }, @@ -105,7 +105,7 @@ }, { "cell_type": "markdown", - "id": "11d6fda3", + "id": "52ec6eca", "metadata": {}, "source": [ "## Next lets create the Data Loader GUI.\n", @@ -116,7 +116,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f6e2b5cc", + "id": "4bdbc4c7", "metadata": { "cellView": "form" }, @@ -200,7 +200,7 @@ }, { "cell_type": "markdown", - "id": "31745eeb", + "id": "4b5274aa", "metadata": {}, "source": [ "## Now let's use SRRF to generate a super-resolution image\n", @@ -209,7 +209,7 @@ }, { "cell_type": "markdown", - "id": "7531fbc0", + "id": "8810bfa3", "metadata": {}, "source": [ "# SRRF Parameters:\n", @@ -225,7 +225,7 @@ { "cell_type": "code", "execution_count": null, - "id": "99166cca", + "id": "229ddbdd", "metadata": { "cellView": "form" }, @@ -294,7 +294,7 @@ }, { "cell_type": "markdown", - "id": "4d33e8d7", + "id": "f40dda16", "metadata": {}, "source": [ "## Let's use NanoPyx to generate an error map of the SRRF image\n", @@ -304,7 +304,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4542056f", + "id": "c37f42be", "metadata": { "cellView": "form" }, @@ -459,7 +459,7 @@ }, { "cell_type": "markdown", - "id": "48478099", + "id": "f0aeaba7", "metadata": {}, "source": [ "## Let's compare the resolution of the raw data with the SRRF using FRC and DecorrelationAnalysis. Let's start with calculation the FRC resolution of the raw data (select frame 3 and 11).\n", @@ -468,7 +468,7 @@ }, { "cell_type": "markdown", - "id": "902d6e3b", + "id": "2535ddcd", "metadata": {}, "source": [ "# FRC Parameters:\n", @@ -483,7 +483,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e1a4424f", + "id": "0ea8ebe9", "metadata": { "cellView": "form" }, @@ -543,7 +543,7 @@ }, { "cell_type": "markdown", - "id": "674aab58", + "id": "742d698e", "metadata": {}, "source": [ "## Now do the same for the SRRF image\n", @@ -553,7 +553,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0f91e309", + "id": "62a44873", "metadata": { "cellView": "form" }, @@ -613,7 +613,7 @@ }, { "cell_type": "markdown", - "id": "d7791e40", + "id": "fa7761fd", "metadata": {}, "source": [ "## Let's do the same using Decorrelation analysis\n", @@ -622,7 +622,7 @@ }, { "cell_type": "markdown", - "id": "42a9e78a", + "id": "b4216b5b", "metadata": {}, "source": [ "# Image Decorrelation Analysis Parameters:\n", @@ -638,7 +638,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ad0481e0", + "id": "66041d04", "metadata": { "cellView": "form" }, @@ -699,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "f7155db8", + "id": "b13ba100", "metadata": {}, "source": [ "## Now let's measure the resolution of the generated SRRF image using Decorrelation analysis\n", @@ -709,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d59ea934", + "id": "ffa9cc07", "metadata": { "cellView": "form" }, diff --git a/notebooks/NonLocalMeansDenoising.ipynb b/notebooks/NonLocalMeansDenoising.ipynb index af7ab7c9..cef5544a 100644 --- a/notebooks/NonLocalMeansDenoising.ipynb +++ b/notebooks/NonLocalMeansDenoising.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "46d86532", + "id": "7234102b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -23,7 +23,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9a077380", + "id": "12e2ce1e", "metadata": { "cellView": "form" }, @@ -45,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2a440b08", + "id": "6d3591cd", "metadata": { "cellView": "form" }, @@ -103,7 +103,7 @@ { "cell_type": "code", "execution_count": null, - "id": "593b62b3", + "id": "cef83399", "metadata": { "cellView": "form" }, @@ -187,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "972df323", + "id": "29916cbb", "metadata": {}, "source": [ "# Use Non-local means denoising on selected data\n", @@ -204,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6efd880b", + "id": "fa448b9d", "metadata": { "cellView": "form" }, diff --git a/notebooks/ParamSweepandeSRRF.ipynb b/notebooks/ParamSweepandeSRRF.ipynb index 57c66329..b79abb4d 100644 --- a/notebooks/ParamSweepandeSRRF.ipynb +++ b/notebooks/ParamSweepandeSRRF.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "2cbaec97", + "id": "6345d1e8", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6699f205", + "id": "ba34a112", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e05acfd0", + "id": "ae512df5", "metadata": { "cellView": "form" }, @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d51549f2", + "id": "0827c922", "metadata": { "cellView": "form" }, @@ -192,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "1494882e", + "id": "0738884d", "metadata": {}, "source": [ "# Run a parameter sweep to find the best combination of parameters\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "032710fa", + "id": "52dcb045", "metadata": { "cellView": "form" }, @@ -274,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50e8028e", + "id": "400aa938", "metadata": { "cellView": "form" }, @@ -410,7 +410,7 @@ }, { "cell_type": "markdown", - "id": "dd812dab", + "id": "4b835280", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -420,7 +420,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ff9b71c0", + "id": "2f2ac1a9", "metadata": { "cellView": "form" }, @@ -575,7 +575,7 @@ }, { "cell_type": "markdown", - "id": "ff41b4ba", + "id": "651b8da0", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -591,7 +591,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7436cacf", + "id": "d10270f9", "metadata": { "cellView": "form" }, @@ -651,7 +651,7 @@ }, { "cell_type": "markdown", - "id": "a195e6ee", + "id": "f82ce04d", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -667,7 +667,7 @@ { "cell_type": "code", "execution_count": null, - "id": "519a5e35", + "id": "5ee8bfc3", "metadata": { "cellView": "form" }, @@ -727,7 +727,7 @@ }, { "cell_type": "markdown", - "id": "fc8d2f69", + "id": "308bbf59", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -744,7 +744,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3493d9ea", + "id": "68c0902f", "metadata": { "cellView": "form" }, @@ -805,7 +805,7 @@ }, { "cell_type": "markdown", - "id": "ed3ac546", + "id": "1a67553a", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -822,7 +822,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4bd3ddc6", + "id": "54ba26d2", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRMetrics.ipynb b/notebooks/SRMetrics.ipynb index 2275fd6f..060aa8a1 100644 --- a/notebooks/SRMetrics.ipynb +++ b/notebooks/SRMetrics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "dbb0862e", + "id": "b16c7e5a", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,7 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6bde70c9", + "id": "41c4bacf", "metadata": { "cellView": "form" }, @@ -48,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0457011e", + "id": "086275fe", "metadata": { "cellView": "form" }, @@ -105,7 +105,7 @@ }, { "cell_type": "markdown", - "id": "524271b9", + "id": "deead987", "metadata": {}, "source": [ "## Load difraction limited image (only needed if you want to use the FRC and Decorrelation analysis on this image or if you want to perform the Error Map analysis)\n", @@ -115,7 +115,7 @@ { "cell_type": "code", "execution_count": null, - "id": "efe54fbe", + "id": "3a5d0c58", "metadata": { "cellView": "form" }, @@ -199,7 +199,7 @@ }, { "cell_type": "markdown", - "id": "8a87a4ed", + "id": "c0150cad", "metadata": {}, "source": [ "## Load super-resolved image\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1e29e93c", + "id": "b60c80ec", "metadata": { "cellView": "form" }, @@ -293,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "f99f901b", + "id": "1525b21b", "metadata": {}, "source": [ "## Calculate Error Map\n", @@ -303,7 +303,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b8aa963b", + "id": "de7cddbf", "metadata": { "cellView": "form" }, @@ -458,7 +458,7 @@ }, { "cell_type": "markdown", - "id": "da9b0f27", + "id": "eec9934d", "metadata": {}, "source": [ "# Calculate FRC of the diffraction limited image\n", @@ -474,7 +474,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ca14daa8", + "id": "2ed0433f", "metadata": { "cellView": "form" }, @@ -534,7 +534,7 @@ }, { "cell_type": "markdown", - "id": "f13f62dd", + "id": "90527123", "metadata": {}, "source": [ "# Calculate FRC of the SR image\n", @@ -550,7 +550,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6752789b", + "id": "8d61026b", "metadata": { "cellView": "form" }, @@ -610,7 +610,7 @@ }, { "cell_type": "markdown", - "id": "ce9e8e84", + "id": "5613b634", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of diffraction limited image\n", @@ -627,7 +627,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22228608", + "id": "87ccb8cd", "metadata": { "cellView": "form" }, @@ -688,7 +688,7 @@ }, { "cell_type": "markdown", - "id": "6f865c27", + "id": "fa7f8ede", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of SR image\n", @@ -705,7 +705,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d06fc988", + "id": "d240adcc", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRRFandQC.ipynb b/notebooks/SRRFandQC.ipynb index df936f7c..fe1ce367 100644 --- a/notebooks/SRRFandQC.ipynb +++ b/notebooks/SRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "b57f313e", + "id": "7a5a3751", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37348e1e", + "id": "295553ae", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8e195be7", + "id": "b2bf612e", "metadata": { "cellView": "form" }, @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "659407d3", + "id": "9970d28c", "metadata": { "cellView": "form" }, @@ -192,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "6959dd9c", + "id": "7411f5e2", "metadata": {}, "source": [ "# Use SRRF to generate a super-resolved image\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26a4b201", + "id": "87ff388f", "metadata": { "cellView": "form" }, @@ -278,7 +278,7 @@ }, { "cell_type": "markdown", - "id": "43161cbf", + "id": "dbcb4e66", "metadata": {}, "source": [ "## Calculate error map for the SRRF image\n", @@ -288,7 +288,7 @@ { "cell_type": "code", "execution_count": null, - "id": "02a0c3bc", + "id": "b23b12d8", "metadata": { "cellView": "form" }, @@ -443,7 +443,7 @@ }, { "cell_type": "markdown", - "id": "32a7c1db", + "id": "8fc2cdee", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -459,7 +459,7 @@ { "cell_type": "code", "execution_count": null, - "id": "741255f4", + "id": "cc8076fc", "metadata": { "cellView": "form" }, @@ -519,7 +519,7 @@ }, { "cell_type": "markdown", - "id": "f7ef825f", + "id": "e4493745", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -535,7 +535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4399e6be", + "id": "53034293", "metadata": { "cellView": "form" }, @@ -595,7 +595,7 @@ }, { "cell_type": "markdown", - "id": "9b756cfb", + "id": "6878882b", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -612,7 +612,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6791e176", + "id": "34f5de86", "metadata": { "cellView": "form" }, @@ -673,7 +673,7 @@ }, { "cell_type": "markdown", - "id": "79fb17ab", + "id": "c315ff47", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -690,7 +690,7 @@ { "cell_type": "code", "execution_count": null, - "id": "378d681f", + "id": "b1df6e51", "metadata": { "cellView": "form" }, diff --git a/notebooks/eSRRFandQC.ipynb b/notebooks/eSRRFandQC.ipynb index e8d16583..2e2c7072 100644 --- a/notebooks/eSRRFandQC.ipynb +++ b/notebooks/eSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "cecd691b", + "id": "5e57974e", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,7 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a054905c", + "id": "a1c8cab9", "metadata": { "cellView": "form" }, @@ -50,7 +50,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b49cc966", + "id": "159be9db", "metadata": { "cellView": "form" }, @@ -108,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "98d87a68", + "id": "2a60be85", "metadata": { "cellView": "form" }, @@ -192,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "2e65c3fb", + "id": "2b5992a8", "metadata": {}, "source": [ "# Use eSRRF to generate a super-resolved image\n", @@ -209,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "770a96a0", + "id": "b2ca01b0", "metadata": { "cellView": "form" }, @@ -345,7 +345,7 @@ }, { "cell_type": "markdown", - "id": "ad48eada", + "id": "e81aa794", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -355,7 +355,7 @@ { "cell_type": "code", "execution_count": null, - "id": "90cf4ca6", + "id": "3f34c3e9", "metadata": { "cellView": "form" }, @@ -510,7 +510,7 @@ }, { "cell_type": "markdown", - "id": "649722a8", + "id": "6a862eb7", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -526,7 +526,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f5d013a2", + "id": "6cf8491e", "metadata": { "cellView": "form" }, @@ -586,7 +586,7 @@ }, { "cell_type": "markdown", - "id": "b9a9e784", + "id": "87ce0680", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -602,7 +602,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8f4af4d0", + "id": "7a12fae6", "metadata": { "cellView": "form" }, @@ -662,7 +662,7 @@ }, { "cell_type": "markdown", - "id": "b0519e68", + "id": "cb79dc4f", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -679,7 +679,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4a261831", + "id": "8be5e188", "metadata": { "cellView": "form" }, @@ -740,7 +740,7 @@ }, { "cell_type": "markdown", - "id": "9b48abb1", + "id": "11ef1523", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -757,7 +757,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0ca641ca", + "id": "fad0907a", "metadata": { "cellView": "form" }, From 588bd7d2882923e61034cb194419921d784829e6 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 11:25:39 +0100 Subject: [PATCH 09/82] version bump --- setup.py | 77 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/setup.py b/setup.py index 357b9926..389a3333 100644 --- a/setup.py +++ b/setup.py @@ -22,11 +22,13 @@ ] EXTRA_LING_ARGS = [] -VERSION = "1.1.0" # sets version number for whole package +VERSION = "1.2.0" # sets version number for whole package def run_command(command: str) -> str: - result = subprocess.run(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.run( + command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) return result.stdout.decode("utf-8").strip() @@ -44,7 +46,9 @@ def is_xcode_installed() -> bool: def is_homebrew_installed() -> bool: try: - result = subprocess.run(["which", "brew"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result = subprocess.run( + ["which", "brew"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) return result.returncode == 0 except Exception: return False @@ -131,7 +135,9 @@ def search_for_c_files_referrenced_in_pyx_text(text: str): # Lets check if homebrew is installed use_openmp_support = True - print("Checking for openmp support, to run code... ─=≡Σ((( つ◕ل͜◕)つ... blazing fast!!! ") + print( + "Checking for openmp support, to run code... ─=≡Σ((( つ◕ل͜◕)つ... blazing fast!!! " + ) BUILD_TOOLS_PATH = ( Path(os.path.dirname(__file__)) / "build_tools" / "libs_build" @@ -155,13 +161,19 @@ def search_for_c_files_referrenced_in_pyx_text(text: str): INCLUDE_DIRS += [os.path.join(libomp_path, "include")] LIBRARY_DIRS += [os.path.join(libomp_path, "lib")] else: - print(f"\t - {packages} instalation not detected: consider running 'brew install {' '.join(packages)}'") + print( + f"\t - {packages} instalation not detected: consider running 'brew install {' '.join(packages)}'" + ) use_openmp_support = False else: - print("\t - brew instalation not detected, consider installing from https://brew.sh/") + print( + "\t - brew instalation not detected, consider installing from https://brew.sh/" + ) use_openmp_support = False else: - print("\t - xcode instalation not detected, consider installing from the App Store") + print( + "\t - xcode instalation not detected, consider installing from the App Store" + ) use_openmp_support = False if use_openmp_support: @@ -203,15 +215,17 @@ def collect_extensions(): path = os.path.join("src") # Compile mako template(s) - lookup = TemplateLookup(directories = ['src/mako_templates']) - for template_ in os.listdir('src/mako_templates'): - if 'base' in template_: + lookup = TemplateLookup(directories=["src/mako_templates"]) + for template_ in os.listdir("src/mako_templates"): + if "base" in template_: continue - path2out = os.path.join(path,template_.replace('.',os.sep,template_.count('.')-1)) + path2out = os.path.join( + path, template_.replace(".", os.sep, template_.count(".") - 1) + ) rendered_output = lookup.get_template(template_).render_unicode() enconded_rendered_output = rendered_output.encode() try: - with open(path2out, 'r+b') as outfile: + with open(path2out, "r+b") as outfile: current_content = outfile.read() if current_content != enconded_rendered_output: @@ -225,27 +239,30 @@ def collect_extensions(): except FileNotFoundError: # If the file doesn't exist, create it with the new content - with open(path2out, 'wb') as outfile: + with open(path2out, "wb") as outfile: outfile.write(enconded_rendered_output) - #with open(path2out, 'wb') as outfile: + # with open(path2out, 'wb') as outfile: # outfile.write(rendered_output.encode()) - - #template_obj = lookup.get_template(template_) - #with open(path2out,'w') as outfile: + + # template_obj = lookup.get_template(template_) + # with open(path2out,'w') as outfile: # outfile.write(template_obj.render()) - cython_files = [ os.path.join(dir, file) for (dir, dirs, files) in os.walk(path) for file in files if file.endswith(".pyx") - or (file.endswith(".py") and "# nanopyx-cythonize: True\n" in open(os.path.join(dir, file)).read()) + or ( + file.endswith(".py") + and "# nanopyx-cythonize: True\n" + in open(os.path.join(dir, file)).read() + ) ] - + # remove mako templates - cython_files = [c for c in cython_files if 'mako' not in c] + cython_files = [c for c in cython_files if "mako" not in c] cython_extensions = [] extra_c_files = [] @@ -262,13 +279,17 @@ def collect_extensions(): ) if os.path.exists(pxd_file): with open(pxd_file, "r") as f: - extra_c_files_candadates = search_for_c_files_referrenced_in_pyx_text(f.read()) + extra_c_files_candadates = ( + search_for_c_files_referrenced_in_pyx_text(f.read()) + ) sources += extra_c_files_candadates extra_c_files += extra_c_files_candadates # Now search in the pyx file with open(file, "r") as f: - extra_c_files_candadates = search_for_c_files_referrenced_in_pyx_text(f.read()) + extra_c_files_candadates = ( + search_for_c_files_referrenced_in_pyx_text(f.read()) + ) sources += extra_c_files_candadates extra_c_files += extra_c_files_candadates @@ -278,7 +299,9 @@ def collect_extensions(): # Remove files that don't exist sources = [file for file in sources if os.path.exists(file)] - extra_c_files = [file for file in extra_c_files if os.path.exists(file)] + extra_c_files = [ + file for file in extra_c_files if os.path.exists(file) + ] # Make sure we have all the include paths for path in extra_c_files: @@ -291,9 +314,13 @@ def collect_extensions(): print(f"Found following .pyx files to build:\n {'; '.join(cython_files)}") print(f"Found the extra c files to build:\n {'; '.join(extra_c_files)}") - collected_extensions = cythonize(cython_extensions, annotate=True, language_level="3") + collected_extensions = cythonize( + cython_extensions, annotate=True, language_level="3" + ) return collected_extensions + + # Show the logo print( r""" From 73741ef5a4bfde61dcc01b5e1073e545f16a04ff Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 22 Apr 2025 16:35:23 +0100 Subject: [PATCH 10/82] added new parameters --- .../_c_sr_radial_gradient_convergence.c | 8 +- .../_c_sr_radial_gradient_convergence.h | 2 +- .../nanopyx.core.transform._le_esrrf3d.pyx | 58 +++++----- src/nanopyx/core/transform/_le_esrrf3d.pyx | 106 ++++++++++-------- 4 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index 101ed0f2..a7ae8573 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -121,7 +121,7 @@ float _c_get_bound_value(float* im, int slices, int rows, int cols, int s, int r return im[_s * rows * cols + _r * cols + _c]; } -float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { +float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, distanceWeight_xy, distanceWeight_z, GdotR, Dk; @@ -137,8 +137,8 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn int _start = -(int)(2 * fwhm); int _end = (int)(2 * fwhm + 1); - int _start_z = -(int)(2 * fwhm); - int _end_z = (int)(2 * fwhm + 1); + int _start_z = -(int)(2 * fwhm_z); + int _end_z = (int)(2 * fwhm_z + 1); for (int j = _start; j <= _end; j++) { vy = yc + j; @@ -155,7 +155,7 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn dx = vx - xc; dy = vy - yc; dz = vz - zc; - dz_real = dz * ratio_px; // This has been already divided by magnification_z + dz_real = dz * voxel_ratio; distance = sqrt(dx * dx + dy * dy + dz_real * dz_real); distance_xy = sqrt(dx * dx + dy * dy); distance_z = dz_real; diff --git a/src/include/_c_sr_radial_gradient_convergence.h b/src/include/_c_sr_radial_gradient_convergence.h index 4d89e987..1a7abb2c 100644 --- a/src/include/_c_sr_radial_gradient_convergence.h +++ b/src/include/_c_sr_radial_gradient_convergence.h @@ -21,7 +21,7 @@ double _c_calculate_dw_z(double distance_z, double tSS_z); double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance); -float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity); +float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity); float _c_calculate_rgc_3d(int cM, int rM, int sM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float Gx_Gy_MAGNIFICATION, float fwhm, float fwhm_z, float tSO, float tSS, float sensitivity); diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 3c702e8a..b9d9f6c0 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -21,7 +21,7 @@ cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil cdef extern from "_c_sr_radial_gradient_convergence.h": - float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil + float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil class eSRRF3D(LiquidEngine): """ @@ -32,27 +32,27 @@ class eSRRF3D(LiquidEngine): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) % for sch in schedulers: - def _run_${sch}(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_${sch}(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu % if sch!='unthreaded': @@ -64,13 +64,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -111,7 +113,7 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): % if sch=="unthreaded": for rM in range(margin, n_rows_mag-margin): % elif sch=="threaded": @@ -120,7 +122,7 @@ class eSRRF3D(LiquidEngine): for rM in prange(margin, n_rows_mag-margin, schedule="${sch.split('_')[1]}"): % endif for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -137,7 +139,7 @@ class eSRRF3D(LiquidEngine): return np.asarray(rgc_avg) % endfor - def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ @@ -154,8 +156,8 @@ class eSRRF3D(LiquidEngine): print("here before cl to array") tmp_slice = cl_array.to_device(cl_queue, output_image) - #max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) - #max_slices = self._check_max_slices(image, max_slices) + # max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) + # max_slices = self._check_max_slices(image, max_slices) max_slices = 1 @@ -179,7 +181,7 @@ class eSRRF3D(LiquidEngine): z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) # create the output buffer - #output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + # output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) output_cl = cl_array.to_device(cl_queue, output_image) esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) @@ -189,7 +191,7 @@ class eSRRF3D(LiquidEngine): rgc_knl = esrrf3d_cl_prg.calculate_rgc3D margin = int(radius*2) * magnification_xy - margin_z = int(radius*2) * magnification_z + margin_z = int(radius_z*2) * magnification_z lowest_row = margin # TODO discuss edges calculation highest_row = output_shape[1] - margin lowest_col = margin @@ -283,17 +285,17 @@ class eSRRF3D(LiquidEngine): np.int32(f) ).wait() - - print("Here 10") - # TODO change tmp_slice to cl arrays - if mode == "average": - output_cl = output_cl + (tmp_slice - output_cl) / (f + 1) - elif mode == "std": - delta = tmp_slice - output_cl - output_cl = output_cl + (delta) / (f + 1) - delta_2 = tmp_slice - output_cl - output_cl = output_cl + (delta * delta_2) - print("Here 11") + # cl_queue.finish() + # print("Here 10") + # # TODO change tmp_slice to cl arrays + # if mode == "average": + # output_cl += (tmp_slice - output_cl) / (f + 1) + # elif mode == "std": + # delta = tmp_slice - output_cl + # output_cl = output_cl + (delta) / (f + 1) + # delta_2 = tmp_slice - output_cl + # output_cl = output_cl + (delta * delta_2) + # print("Here 11") cl.enqueue_copy(cl_queue, output_image, output_cl).wait() print("Here 12") diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 345a6155..d3ec46c2 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -19,7 +19,7 @@ cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil cdef extern from "_c_sr_radial_gradient_convergence.h": - float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil + float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIntGy, float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) nogil class eSRRF3D(LiquidEngine): """ @@ -30,26 +30,26 @@ class eSRRF3D(LiquidEngine): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio,sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) elif len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) + return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) - def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -59,13 +59,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -106,10 +108,10 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): for rM in prange(margin, n_rows_mag-margin): for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -124,7 +126,7 @@ class eSRRF3D(LiquidEngine): return rgc_std else: return np.asarray(rgc_avg) - def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -134,13 +136,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -181,10 +185,10 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): for rM in prange(margin, n_rows_mag-margin, schedule="guided"): for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -199,7 +203,7 @@ class eSRRF3D(LiquidEngine): return rgc_std else: return np.asarray(rgc_avg) - def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -209,13 +213,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -256,10 +262,10 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): for rM in prange(margin, n_rows_mag-margin, schedule="dynamic"): for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -274,7 +280,7 @@ class eSRRF3D(LiquidEngine): return rgc_std else: return np.asarray(rgc_avg) - def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -284,13 +290,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -331,10 +339,10 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): for rM in prange(margin, n_rows_mag-margin, schedule="static"): for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -349,7 +357,7 @@ class eSRRF3D(LiquidEngine): return rgc_std else: return np.asarray(rgc_avg) - def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @cython @@ -358,13 +366,15 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef float fwhm = radius cdef int margin = int(2 * radius) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef int margin_z = int(2 * radius_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 + cdef float fwhm = radius + cdef float fwhm_z = radius_z cdef int _magnification_xy = magnification_xy cdef int _magnification_z = magnification_z cdef float _voxel_ratio = voxel_ratio @@ -405,10 +415,10 @@ class eSRRF3D(LiquidEngine): gradients_col_mag = interpolate_3d_zlinear(gradients_col, _magnification_xy, _magnification_z) with nogil: - for sM in range(margin, n_slices_mag-margin): + for sM in range(margin_z, n_slices_mag-margin_z): for rM in range(margin, n_rows_mag-margin): for cM in range(margin, n_cols_mag-margin): - rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) + rgc_val = _c_calculate_rgc3D(cM, rM, sM, &gradients_col_mag[0,0,0], &gradients_row_mag[0,0,0], &gradients_slices_mag[0,0,0], n_cols_mag, n_rows_mag, n_slices_mag, _magnification_xy, _magnification_z, _voxel_ratio, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": @@ -424,7 +434,7 @@ class eSRRF3D(LiquidEngine): else: return np.asarray(rgc_avg) - def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ @@ -441,8 +451,8 @@ class eSRRF3D(LiquidEngine): print("here before cl to array") tmp_slice = cl_array.to_device(cl_queue, output_image) - #max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) - #max_slices = self._check_max_slices(image, max_slices) + # max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) + # max_slices = self._check_max_slices(image, max_slices) max_slices = 1 @@ -466,7 +476,7 @@ class eSRRF3D(LiquidEngine): z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) # create the output buffer - #output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) + # output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) output_cl = cl_array.to_device(cl_queue, output_image) esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) @@ -476,7 +486,7 @@ class eSRRF3D(LiquidEngine): rgc_knl = esrrf3d_cl_prg.calculate_rgc3D margin = int(radius*2) * magnification_xy - margin_z = int(radius*2) * magnification_z + margin_z = int(radius_z*2) * magnification_z lowest_row = margin # TODO discuss edges calculation highest_row = output_shape[1] - margin lowest_col = margin @@ -570,17 +580,17 @@ class eSRRF3D(LiquidEngine): np.int32(f) ).wait() - - print("Here 10") - # TODO change tmp_slice to cl arrays - if mode == "average": - output_cl = output_cl + (tmp_slice - output_cl) / (f + 1) - elif mode == "std": - delta = tmp_slice - output_cl - output_cl = output_cl + (delta) / (f + 1) - delta_2 = tmp_slice - output_cl - output_cl = output_cl + (delta * delta_2) - print("Here 11") + # cl_queue.finish() + # print("Here 10") + # # TODO change tmp_slice to cl arrays + # if mode == "average": + # output_cl += (tmp_slice - output_cl) / (f + 1) + # elif mode == "std": + # delta = tmp_slice - output_cl + # output_cl = output_cl + (delta) / (f + 1) + # delta_2 = tmp_slice - output_cl + # output_cl = output_cl + (delta * delta_2) + # print("Here 11") cl.enqueue_copy(cl_queue, output_image, output_cl).wait() print("Here 12") From b5464d6c873e8d8cf989236a2a48ecce69410f63 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 24 Apr 2025 17:29:46 +0100 Subject: [PATCH 11/82] further updates but cl time projection is still bugged --- .../_c_sr_radial_gradient_convergence.c | 7 +- .../nanopyx.core.transform._le_esrrf3d.pyx | 256 +++++++------ src/nanopyx/core/transform/_le_esrrf3d.pyx | 356 +++++++++--------- src/nanopyx/core/transform/_le_esrrf3d_.cl | 256 +++++++------ 4 files changed, 447 insertions(+), 428 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index a7ae8573..d39353f6 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -102,7 +102,7 @@ double _c_calculate_dw_z(double distance_z, double tSS_z) { } double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { - float Dk = sqrtf((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); + float Dk = sqrt((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); if (isnan(Dk)) { Dk = distance; } @@ -194,14 +194,13 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn RGC /= distanceWeightSum; - if (RGC >= 0) { + if (RGC >= 0 && sensitivity > 1) { RGC = pow(RGC, sensitivity); - } else { + } else if (RGC < 0) { RGC = 0; } return RGC; - //return imIntGy[(int)(zc * rowsM * colsM) + (int)(yc * colsM) + (int)(xc)]; } float _c_calculate_dk_3d(float dx, float dy, float dz, float Gx, float Gy, float Gz, float Gmag, float distance) { diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index b9d9f6c0..8d19fceb 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -64,11 +64,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -85,10 +85,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -126,181 +123,194 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) % endfor def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ - print("Here 1") + # select cl device if device is None: device = _fastest_device + + # create cl context and queue cl_ctx = cl.Context([device['device']]) dc = device['device'] cl_queue = cl.CommandQueue(cl_ctx) output_shape = (image.shape[1] * magnification_z, image.shape[2] * magnification_xy, image.shape[3] * magnification_xy) - - output_image = np.zeros(output_shape, dtype=np.float32) - print("here before cl to array") - tmp_slice = cl_array.to_device(cl_queue, output_image) - - # max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) - # max_slices = self._check_max_slices(image, max_slices) - - max_slices = 1 - - mf = cl.mem_flags - print("Here 2") - # create cl buffer for input image - input_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(image[:, :, :, :].nbytes, dc, max_slices)) - cl.enqueue_copy(cl_queue, input_cl, image[:,:,:, :]).wait() - - # create magnified input image buffer - magnified_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - - # create gradients buffers - col_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - row_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - z_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - print("Here 3") - # create magnified gradients buffers - col_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - row_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - - # create the output buffer - # output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - output_cl = cl_array.to_device(cl_queue, output_image) - - esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) - esrrf3d_cl_prg = cl.Program(cl_ctx, esrrf3d_cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) - interp_knl = esrrf3d_cl_prg.interpolate_3d - grads_knl = esrrf3d_cl_prg.gradients_3d - rgc_knl = esrrf3d_cl_prg.calculate_rgc3D - - margin = int(radius*2) * magnification_xy - margin_z = int(radius_z*2) * magnification_z - lowest_row = margin # TODO discuss edges calculation + output_array = np.zeros(output_shape, dtype=np.float32) + + # create input cl buffers + input_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=np.ascontiguousarray(image)) + input_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create gradient buffers + slices_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + rows_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + cols_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + slices_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + rows_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + cols_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create rgc cl buffer + rgc_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create output cl buffer + output_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create cl code, program and kernels + cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) + cl_prg = cl.Program(cl_ctx, cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) + interpolate_kernel = cl_prg.interpolate_3d + gradients_kernel = cl_prg.gradients_3d + rgc_kernel = cl_prg.calculate_rgc3D + if mode == "average": + time_projection_kernel = cl_prg.time_projection_average + elif mode == "std": + time_projection_kernel = cl_prg.time_projection_std + else: + raise ValueError("Invalid mode. Use 'average' or 'std'.") + + # set margins + margin = int(2 * radius) * magnification_xy + margin_z = int(2 * radius_z) * magnification_z + lowest_slice = margin_z + highest_slice = output_shape[0] - margin_z + lowest_row = margin highest_row = output_shape[1] - margin lowest_col = margin - highest_col = output_shape[2] - margin - lowest_z = margin_z - highest_z = output_shape[0] - margin_z - print("Here 4") - for f in range(image.shape[0]): + highest_col = output_shape[2] - margin + + # set constants + cdef float sigma = radius / 2.355 + cdef float tss = 2 * sigma * sigma + cdef float tso = 2 * sigma + 1 + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float tss_z = 2 * sigma_z * sigma_z + cdef float tso_z = 2 * sigma_z + 1 + + cdef float frame_div = 0.0 + + # loop over frames: + for frame_i in range(image.shape[0]): + # insert kernel code here - interp_knl( + # interpolate image + interpolate_kernel( cl_queue, - (image.shape[1], image.shape[2], image.shape[3]), + (output_shape[0], output_shape[1], output_shape[2]), None, - input_cl, - magnified_cl, + input_buffer, + input_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(f) + np.int32(frame_i), ).wait() - print("Here 5") - grads_knl( + + # calculate gradients + gradients_kernel( cl_queue, (image.shape[1], image.shape[2], image.shape[3]), None, - input_cl, - col_gradients_cl, - row_gradients_cl, - z_gradients_cl, - np.int32(f), + input_buffer, + slices_gradient_buffer, + cols_gradient_buffer, + rows_gradient_buffer, np.int32(image.shape[1]), np.int32(image.shape[2]), np.int32(image.shape[3]), + np.int32(frame_i), ).wait() - print("Here 6") - interp_knl( + + + + # interpolate gradients + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - col_gradients_cl, - col_magnified_gradients_cl, + slices_gradient_buffer, + slices_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(0) + np.int32(frame_i), ).wait() - print("Here 7") - interp_knl( + + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - row_gradients_cl, - row_magnified_gradients_cl, + rows_gradient_buffer, + rows_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(0) + np.int32(frame_i), ).wait() - print("Here 8") - interp_knl( + + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - z_gradients_cl, - z_magnified_gradients_cl, + cols_gradient_buffer, + cols_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(f) + np.int32(frame_i), ).wait() - print("Here 9") - rgc_knl( + + # calculate rgc + + rgc_kernel( cl_queue, - (highest_z-lowest_z, highest_row-lowest_row, highest_col-lowest_col), + (highest_slice - lowest_slice, highest_row - lowest_row, highest_col - lowest_col), None, - col_magnified_gradients_cl, - row_magnified_gradients_cl, - z_magnified_gradients_cl, - magnified_cl, - tmp_slice.data, - np.int32(output_shape[1]), - np.int32(output_shape[2]), - np.int32(output_shape[0]), + slices_gradient_magnified_buffer, + rows_gradient_magnified_buffer, + cols_gradient_magnified_buffer, + input_magnified_buffer, + rgc_buffer, + np.int32(output_array.shape[0]), + np.int32(output_array.shape[1]), + np.int32(output_array.shape[2]), np.int32(magnification_xy), np.int32(magnification_z), np.float32(voxel_ratio), np.float32(radius), - np.float32(2 * (radius/2.355) + 1), - np.float32(2 * (radius*voxel_ratio/2.355) + 1), - np.float32(2 * (radius/2.355) * (radius/2.355)), - np.float32(2 * (radius*voxel_ratio/2.355) * (radius*voxel_ratio/2.355)), - np.int32(sensitivity), + np.float32(radius_z), + np.float32(tso), + np.float32(tss), + np.float32(tso_z), + np.float32(tss_z), + np.float32(sensitivity), np.int32(doIntensityWeighting), - np.int32(f) + np.int32(frame_i), + ).wait() + time_projection_kernel( + cl_queue, + (output_array.shape[0], output_array.shape[1], output_array.shape[2]), + None, + rgc_buffer, + output_buffer, + np.int32(frame_i), ).wait() - # cl_queue.finish() - # print("Here 10") - # # TODO change tmp_slice to cl arrays - # if mode == "average": - # output_cl += (tmp_slice - output_cl) / (f + 1) - # elif mode == "std": - # delta = tmp_slice - output_cl - # output_cl = output_cl + (delta) / (f + 1) - # delta_2 = tmp_slice - output_cl - # output_cl = output_cl + (delta * delta_2) - # print("Here 11") - - cl.enqueue_copy(cl_queue, output_image, output_cl).wait() - print("Here 12") + cl_queue.finish() + cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() + if mode == "std": - output_image = np.sqrt(np.asarray(output_image) / image.shape[0]) - return output_image + return np.asarray(np.sqrt(output_array / image.shape[0])) else: - return np.asarray(output_image) \ No newline at end of file + return np.asarray(output_array / image.shape[0]) diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index d3ec46c2..fe416c2d 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -59,11 +59,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -80,10 +80,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -115,17 +112,17 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -136,11 +133,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -157,10 +154,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -192,17 +186,17 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -213,11 +207,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -234,10 +228,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -269,17 +260,17 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -290,11 +281,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -311,10 +302,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -346,17 +334,17 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -366,11 +354,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) + cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) + cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -387,10 +375,7 @@ class eSRRF3D(LiquidEngine): n_cols_mag = n_cols * _magnification_xy # create all necessary arrays - cdef float[:, :, :] rgc_avg = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) - cdef float[:, :, :] rgc_std - if mode == "std": - rgc_std = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -422,180 +407,193 @@ class eSRRF3D(LiquidEngine): if _doIntensityWeighting: rgc_val = rgc_val * image_interpolated[sM, rM, cM] if mode == "average": - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (rgc_val - rgc_avg[sM, rM, cM]) / (f_i + 1) + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_avg[sM, rM, cM] - rgc_avg[sM, rM, cM] = rgc_avg[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_avg[sM, rM, cM] - rgc_std[sM, rM, cM] = rgc_std[sM, rM, cM] + (delta * delta_2) + delta = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) + delta_2 = rgc_val - rgc_out[sM, rM, cM] + rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) if mode == "std": - rgc_std = np.sqrt(np.asarray(rgc_std) / n_frames) - return rgc_std + rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) + return rgc_out else: - return np.asarray(rgc_avg) + return np.asarray(rgc_out) def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ - print("Here 1") + # select cl device if device is None: device = _fastest_device + + # create cl context and queue cl_ctx = cl.Context([device['device']]) dc = device['device'] cl_queue = cl.CommandQueue(cl_ctx) output_shape = (image.shape[1] * magnification_z, image.shape[2] * magnification_xy, image.shape[3] * magnification_xy) - - output_image = np.zeros(output_shape, dtype=np.float32) - print("here before cl to array") - tmp_slice = cl_array.to_device(cl_queue, output_image) - - # max_slices = int((dc.global_mem_size - (3 * (image[0, :, :, :].nbytes) + 4 * (output_image.nbytes)) // (image[0, :, :, :].nbytes)) // mem_div) - # max_slices = self._check_max_slices(image, max_slices) - - max_slices = 1 - - mf = cl.mem_flags - print("Here 2") - # create cl buffer for input image - input_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(image[:, :, :, :].nbytes, dc, max_slices)) - cl.enqueue_copy(cl_queue, input_cl, image[:,:,:, :]).wait() - - # create magnified input image buffer - magnified_cl = cl.Buffer(cl_ctx, mf.READ_ONLY, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - - # create gradients buffers - col_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - row_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - z_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(image[0, :, :, :].nbytes, dc, max_slices)) - print("Here 3") - # create magnified gradients buffers - col_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - row_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - z_magnified_gradients_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - - # create the output buffer - # output_cl = cl.Buffer(cl_ctx, mf.READ_WRITE, self._check_max_buffer_size(output_image.nbytes, dc, max_slices)) - output_cl = cl_array.to_device(cl_queue, output_image) - - esrrf3d_cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) - esrrf3d_cl_prg = cl.Program(cl_ctx, esrrf3d_cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) - interp_knl = esrrf3d_cl_prg.interpolate_3d - grads_knl = esrrf3d_cl_prg.gradients_3d - rgc_knl = esrrf3d_cl_prg.calculate_rgc3D - - margin = int(radius*2) * magnification_xy - margin_z = int(radius_z*2) * magnification_z - lowest_row = margin # TODO discuss edges calculation + output_array = np.zeros(output_shape, dtype=np.float32) + + # create input cl buffers + input_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=np.ascontiguousarray(image)) + input_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create gradient buffers + slices_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + rows_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + cols_gradient_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize) + slices_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + rows_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + cols_gradient_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create rgc cl buffer + rgc_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create output cl buffer + output_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + + # create cl code, program and kernels + cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) + cl_prg = cl.Program(cl_ctx, cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) + interpolate_kernel = cl_prg.interpolate_3d + gradients_kernel = cl_prg.gradients_3d + rgc_kernel = cl_prg.calculate_rgc3D + if mode == "average": + time_projection_kernel = cl_prg.time_projection_average + elif mode == "std": + time_projection_kernel = cl_prg.time_projection_std + else: + raise ValueError("Invalid mode. Use 'average' or 'std'.") + + # set margins + margin = int(2 * radius) * magnification_xy + margin_z = int(2 * radius_z) * magnification_z + lowest_slice = margin_z + highest_slice = output_shape[0] - margin_z + lowest_row = margin highest_row = output_shape[1] - margin lowest_col = margin - highest_col = output_shape[2] - margin - lowest_z = margin_z - highest_z = output_shape[0] - margin_z - print("Here 4") - for f in range(image.shape[0]): + highest_col = output_shape[2] - margin + + # set constants + cdef float sigma = radius / 2.355 + cdef float tss = 2 * sigma * sigma + cdef float tso = 2 * sigma + 1 + cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float tss_z = 2 * sigma_z * sigma_z + cdef float tso_z = 2 * sigma_z + 1 - interp_knl( + cdef float frame_div = 0.0 + + # loop over frames: + for frame_i in range(image.shape[0]): + # insert kernel code here + + # interpolate image + interpolate_kernel( cl_queue, - (image.shape[1], image.shape[2], image.shape[3]), + (output_shape[0], output_shape[1], output_shape[2]), None, - input_cl, - magnified_cl, + input_buffer, + input_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(f) + np.int32(frame_i), ).wait() - print("Here 5") - grads_knl( + + # calculate gradients + gradients_kernel( cl_queue, (image.shape[1], image.shape[2], image.shape[3]), None, - input_cl, - col_gradients_cl, - row_gradients_cl, - z_gradients_cl, - np.int32(f), + input_buffer, + slices_gradient_buffer, + cols_gradient_buffer, + rows_gradient_buffer, np.int32(image.shape[1]), np.int32(image.shape[2]), np.int32(image.shape[3]), + np.int32(frame_i), ).wait() - print("Here 6") - interp_knl( + + + + # interpolate gradients + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - col_gradients_cl, - col_magnified_gradients_cl, + slices_gradient_buffer, + slices_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(0) + np.int32(frame_i), ).wait() - print("Here 7") - interp_knl( + + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - row_gradients_cl, - row_magnified_gradients_cl, + rows_gradient_buffer, + rows_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(0) + np.int32(frame_i), ).wait() - print("Here 8") - interp_knl( + + interpolate_kernel( cl_queue, (output_shape[0], output_shape[1], output_shape[2]), None, - z_gradients_cl, - z_magnified_gradients_cl, + cols_gradient_buffer, + cols_gradient_magnified_buffer, np.int32(magnification_xy), np.int32(magnification_z), - np.int32(f) + np.int32(frame_i), ).wait() - print("Here 9") - rgc_knl( + + # calculate rgc + + rgc_kernel( cl_queue, - (highest_z-lowest_z, highest_row-lowest_row, highest_col-lowest_col), + (highest_slice - lowest_slice, highest_row - lowest_row, highest_col - lowest_col), None, - col_magnified_gradients_cl, - row_magnified_gradients_cl, - z_magnified_gradients_cl, - magnified_cl, - tmp_slice.data, - np.int32(output_shape[1]), - np.int32(output_shape[2]), - np.int32(output_shape[0]), + slices_gradient_magnified_buffer, + rows_gradient_magnified_buffer, + cols_gradient_magnified_buffer, + input_magnified_buffer, + rgc_buffer, + np.int32(output_array.shape[0]), + np.int32(output_array.shape[1]), + np.int32(output_array.shape[2]), np.int32(magnification_xy), np.int32(magnification_z), np.float32(voxel_ratio), np.float32(radius), - np.float32(2 * (radius/2.355) + 1), - np.float32(2 * (radius*voxel_ratio/2.355) + 1), - np.float32(2 * (radius/2.355) * (radius/2.355)), - np.float32(2 * (radius*voxel_ratio/2.355) * (radius*voxel_ratio/2.355)), - np.int32(sensitivity), + np.float32(radius_z), + np.float32(tso), + np.float32(tss), + np.float32(tso_z), + np.float32(tss_z), + np.float32(sensitivity), np.int32(doIntensityWeighting), - np.int32(f) + np.int32(frame_i), + ).wait() + time_projection_kernel( + cl_queue, + (output_array.shape[0], output_array.shape[1], output_array.shape[2]), + None, + rgc_buffer, + output_buffer, + np.int32(frame_i), ).wait() - # cl_queue.finish() - # print("Here 10") - # # TODO change tmp_slice to cl arrays - # if mode == "average": - # output_cl += (tmp_slice - output_cl) / (f + 1) - # elif mode == "std": - # delta = tmp_slice - output_cl - # output_cl = output_cl + (delta) / (f + 1) - # delta_2 = tmp_slice - output_cl - # output_cl = output_cl + (delta * delta_2) - # print("Here 11") - - cl.enqueue_copy(cl_queue, output_image, output_cl).wait() - print("Here 12") + cl_queue.finish() + cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() + if mode == "std": - output_image = np.sqrt(np.asarray(output_image) / image.shape[0]) - return output_image + return np.asarray(np.sqrt(output_array / image.shape[0])) else: - return np.asarray(output_image) \ No newline at end of file + return np.asarray(output_array / image.shape[0]) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 44ccb093..1012861e 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -1,37 +1,96 @@ -void _c_gradient_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int slices, - int rows, int cols) { - float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; +float catmull_rom_weight(float t, int offset) { + // Catmull-Rom spline weight calculation + t = fabs(t - offset); + if (t < 1.0f) { + return 1.0f - 2.0f * t * t + t * t * t; + } else if (t < 2.0f) { + return 4.0f - 8.0f * t + 5.0f * t * t - t * t * t; + } + return 0.0f; +} + + +__kernel void interpolate_3d(__global float* image, __global float* magnified_image, int magnification_xy, int magnification_z, int f) { + int s = get_global_id(0); + int row = get_global_id(1); + int col = get_global_id(2); + + int slices = get_global_size(0) / magnification_z; + int rows = get_global_size(1) / magnification_xy; + int cols = get_global_size(2) / magnification_xy; + + // Linear interpolation in z + float z_ratio = (float)s / magnification_z; + int z0 = (int)floor(z_ratio); + int z1 = z0 + 1; + z1 = z1 < slices ? z1 : slices - 1; + float z_weight = z_ratio - z0; + + // Bicubic interpolation in xy + float y_ratio = (float)row / magnification_xy; + float x_ratio = (float)col / magnification_xy; + int y0 = (int)floor(y_ratio); + int x0 = (int)floor(x_ratio); + float y_weight = y_ratio - y0; + float x_weight = x_ratio - x0; - int z_i, y_i, x_i, z_1, y_1, x_1; - - for (z_i = 0; z_i < slices; z_i++) { - for (y_i = 0; y_i < rows; y_i++) { - for (x_i = 0; x_i < cols; x_i++) { - - z_1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; - y_1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; - x_1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; - ip0 = image[z_i * rows * cols + y_i * cols + x_i]; - ip1 = image[z_i * rows * cols + y_i * cols + x_1]; - ip2 = image[z_i * rows * cols + y_1 * cols + x_i]; - ip3 = image[z_i * rows * cols + y_1 * cols + x_1]; - ip4 = image[z_1 * rows * cols + y_i * cols + x_i]; - ip5 = image[z_1 * rows * cols + y_i * cols + x_1]; - ip6 = image[z_1 * rows * cols + y_1 * cols + x_i]; - ip7 = image[z_1 * rows * cols + y_1 * cols + x_1]; - imGc[z_i * rows* cols + y_i * cols + x_i] = - (ip1 + ip3 + ip5 + ip7 - ip0 - ip2 - ip4 - ip6) / 4; - imGr[z_i * rows* cols + y_i * cols + x_i] = - (ip2 + ip3 + ip6 + ip7 - ip0 - ip1 - ip4 - ip5) / 4; - imGs[z_i * rows* cols + y_i * cols + x_i] = - (ip4 + ip5 + ip6 + ip7 - ip0 - ip1 - ip2 - ip3) / 4; + float result = 0.0f; + for (int dz = 0; dz <= 1; dz++) { + int z = dz == 0 ? z0 : z1; + float z_contrib = dz == 0 ? (1 - z_weight) : z_weight; + + for (int dy = -1; dy <= 2; dy++) { + int y = y0 + dy; + y = y > 0 ? (y < rows ? y : rows - 1) : 0; + float y_contrib = catmull_rom_weight(y_weight, dy); + + for (int dx = -1; dx <= 2; dx++) { + int x = x0 + dx; + x = x > 0 ? (x < cols ? x : cols - 1) : 0; + float x_contrib = catmull_rom_weight(x_weight, dx); + + float pixel_value = image[f * slices * rows * cols + z * rows * cols + y * cols + x]; + result += pixel_value * z_contrib * y_contrib * x_contrib; } } } + + magnified_image[s * get_global_size(1) * get_global_size(2) + row * get_global_size(2) + col] = result; } -double _c_calculate_dw3D_isotropic(double distance, double tSS) { - return pow((distance * exp(-(distance * distance) / tSS)), 4); +void _c_gradient_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int slices, int rows, int cols, int z_i, int y_i, int x_i) { + float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; + + int z_1, y_1, x_1; + + z_1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; + y_1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; + x_1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; + ip0 = image[z_i * rows * cols + y_i * cols + x_i]; + ip1 = image[z_i * rows * cols + y_i * cols + x_1]; + ip2 = image[z_i * rows * cols + y_1 * cols + x_i]; + ip3 = image[z_i * rows * cols + y_1 * cols + x_1]; + ip4 = image[z_1 * rows * cols + y_i * cols + x_i]; + ip5 = image[z_1 * rows * cols + y_i * cols + x_1]; + ip6 = image[z_1 * rows * cols + y_1 * cols + x_i]; + ip7 = image[z_1 * rows * cols + y_1 * cols + x_1]; + imGc[z_i * rows* cols + y_i * cols + x_i] = + (ip1 + ip3 + ip5 + ip7 - ip0 - ip2 - ip4 - ip6) / 4; + imGr[z_i * rows* cols + y_i * cols + x_i] = + (ip2 + ip3 + ip6 + ip7 - ip0 - ip1 - ip4 - ip5) / 4; + imGs[z_i * rows* cols + y_i * cols + x_i] = + (ip4 + ip5 + ip6 + ip7 - ip0 - ip1 - ip2 - ip3) / 4; + +} + +__kernel void gradients_3d(__global float* image, __global float* imGs, __global float* imGc, __global float* imGr, int slices, int rows, int cols, int frame_index) { + int z_i = get_global_id(0); + int y_i = get_global_id(1); + int x_i = get_global_id(2); + + if (z_i < slices && y_i < rows && x_i < cols) { + _c_gradient_3d(&image[frame_index*slices*rows*cols], &imGc[0], &imGr[0], &imGs[0], slices, rows, cols, z_i, y_i, x_i); + } } double _c_calculate_dw3D(double distance, double distance_xy, double distance_z,double tSS, double tSS_z) { @@ -59,20 +118,9 @@ double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float return Dk; } -float _c_get_bound_value(float* im, int slices, int rows, int cols, int s, int r, int c){ - int _s = s > 0 ? s : 0; - _s = _s < slices - 1 ? _s : slices - 1; - int _r = r > 0 ? r : 0; - _r = _r < rows - 1 ? _r : rows - 1; - int _c = c > 0 ? c : 0; - _c = _c < cols - 1 ? _c : cols - 1; +float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __global float* imIntGy, __global float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { - return im[_s * rows * cols + _r * cols + _c]; -} - -float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __global float* imIntGy, __global float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { - - float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, distanceWeight_xy, distanceWeight_z, GdotR, Dk; + float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, GdotR, Dk; float xc = (xM) / magnification_xy; float yc = (yM) / magnification_xy; @@ -80,14 +128,12 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ float RGC = 0; float distanceWeightSum = 0; - float distanceWeightSum_xy = 0; - float distanceWeightSum_z = 0; int _start = -(int)(2 * fwhm); int _end = (int)(2 * fwhm + 1); - int _start_z = -(int)(2 * fwhm); - int _end_z = (int)(2 * fwhm + 1); + int _start_z = -(int)(2 * fwhm_z); + int _end_z = (int)(2 * fwhm_z + 1); for (int j = _start; j <= _end; j++) { vy = yc + j; @@ -104,7 +150,7 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ dx = vx - xc; dy = vy - yc; dz = vz - zc; - dz_real = dz * ratio_px; // This has been already divided by magnification_z + dz_real = dz * voxel_ratio; distance = sqrt(dx * dx + dy * dy + dz_real * dz_real); distance_xy = sqrt(dx * dx + dy * dy); distance_z = dz_real; @@ -118,15 +164,9 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ Gy = imIntGy[linear_index]; Gz = imIntGz[linear_index]; - // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); - - // distanceWeight_xy = _c_calculate_dw_xy(distance_xy, tSS); - // distanceWeight_z = _c_calculate_dw_z(distance_z, tSS_z); distanceWeightSum += distanceWeight; - // distanceWeightSum_xy += distanceWeight_xy; - // distanceWeightSum_z += distanceWeight_z; GdotR = Gx*dx + Gy*dy + Gz*dz_real; if (GdotR < 0) { @@ -143,96 +183,68 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ RGC /= distanceWeightSum; - if (RGC >= 0) { + if (RGC >= 0 && sensitivity > 1) { RGC = pow(RGC, sensitivity); - } else { + } else if (RGC < 0) { RGC = 0; } return RGC; - //return imIntGy[(int)(zc * rowsM * colsM) + (int)(yc * colsM) + (int)(xc)]; } -float catmull_rom_weight(float t, int offset) { - // Catmull-Rom spline weight calculation - t = fabs(t - offset); - if (t < 1.0f) { - return 1.0f - 2.0f * t * t + t * t * t; - } else if (t < 2.0f) { - return 4.0f - 8.0f * t + 5.0f * t * t - t * t * t; - } - return 0.0f; -} - - -__kernel void interpolate_3d(__global float* image, __global float* magnified_image, int magnification_xy, int magnification_z, int f) { +__kernel void calculate_rgc3D(__global float* imIntGz, __global float* imIntGy, __global float* imIntGx, __global float* imM, __global float* tmp_slice, int slicesM, int rowsM, int colsM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float fwhm_z, float tSO, float tSS, float tSO_z, float tSS_z, float sensitivity, int doIntensityWeighting, int f) { + + // Index of the current pixel int s = get_global_id(0); int row = get_global_id(1); int col = get_global_id(2); - int slices = get_global_size(0) / magnification_z; - int rows = get_global_size(1) / magnification_xy; - int cols = get_global_size(2) / magnification_xy; - - // Linear interpolation in z - float z_ratio = (float)s / magnification_z; - int z0 = (int)floor(z_ratio); - int z1 = z0 + 1; - z1 = z1 < slices ? z1 : slices - 1; - float z_weight = z_ratio - z0; + // Output image dimensions + int nPixels_out = slicesM * rowsM * colsM; - // Bicubic interpolation in xy - float y_ratio = (float)row / magnification_xy; - float x_ratio = (float)col / magnification_xy; - int y0 = (int)floor(y_ratio); - int x0 = (int)floor(x_ratio); - float y_weight = y_ratio - y0; - float x_weight = x_ratio - x0; + row = row + fwhm*2*magnification_xy; + col = col + fwhm*2*magnification_xy; + s = s + fwhm_z*2*magnification_z; - float result = 0.0f; - for (int dz = 0; dz <= 1; dz++) { - int z = dz == 0 ? z0 : z1; - float z_contrib = dz == 0 ? (1 - z_weight) : z_weight; + if (doIntensityWeighting == 1) { + tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[f * nPixels_out + s * rowsM * colsM + row * colsM + col]; + } else { + tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity); + } +} - for (int dy = -1; dy <= 2; dy++) { - int y = y0 + dy; - y = y > 0 ? (y < rows ? y : rows - 1) : 0; - float y_contrib = catmull_rom_weight(y_weight, dy); +__kernel void time_projection_average(__global float* current_slice, __global float* output, int frame_i) { + int s = get_global_id(0); + int row = get_global_id(1); + int col = get_global_id(2); - for (int dx = -1; dx <= 2; dx++) { - int x = x0 + dx; - x = x > 0 ? (x < cols ? x : cols - 1) : 0; - float x_contrib = catmull_rom_weight(x_weight, dx); + int rows = get_global_size(1); + int cols = get_global_size(2); - float pixel_value = image[f * slices * rows * cols + z * rows * cols + y * cols + x]; - result += pixel_value * z_contrib * y_contrib * x_contrib; - } - } - } + int current_index = s * rows * cols + row * cols + col; - magnified_image[s * get_global_size(1) * get_global_size(2) + row * get_global_size(2) + col] = result; + output[current_index] += current_slice[current_index]; } -__kernel void gradients_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int f, int slices, int rows, int cols) { - _c_gradient_3d(&image[f * slices * rows * cols], &imGc[f * slices * rows * cols], &imGr[f * slices * rows * cols], &imGs[f * slices * rows * cols], slices, rows, cols); -} +__kernel void time_projection_std( + __global float* current_slice, + __global float* output, + int frame_i +) { + int s = get_global_id(0); + int row = get_global_id(1); + int col = get_global_id(2); -__kernel void calculate_rgc3D(__global float* imIntGx, __global float* imIntGy, __global float* imIntGz, __global float* imM, __global float* tmp_slice, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float ratio_px, float fwhm, float tSO, float tSS, float tSO_z, float tSS_z, int sensitivity, int doIntensityWeighting, int f) { - - // Index of the current pixel - int s = get_global_id(0); - int row = get_global_id(1); - int col = get_global_id(2); + int rows = get_global_size(1); + int cols = get_global_size(2); - // Output image dimensions - int nPixels_out = slicesM * rowsM * colsM; + int current_index = s * rows * cols + row * cols + col; - row = row + fwhm*2*magnification_xy; - col = col + fwhm*2*magnification_xy; - s = s + fwhm*2*magnification_z; - if (doIntensityWeighting == 1) { - tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[slicesM * rowsM * colsM], &imIntGy[slicesM * rowsM * colsM], &imIntGz[slicesM * rowsM * colsM], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[f * nPixels_out + s * rowsM * colsM + row * colsM + col]; - } else { - tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[slicesM * rowsM * colsM], &imIntGy[slicesM * rowsM * colsM], &imIntGz[slicesM * rowsM * colsM], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, tSO, tSO_z, tSS, tSS_z, sensitivity); - } + // Calculate the running mean + float delta = current_slice[current_index] - output[current_index]; + output[current_index] += delta / (frame_i + 1); + + // Update the running variance + float delta2 = current_slice[current_index] - output[current_index]; + output[current_index] += delta * delta2 / (frame_i + 1); } \ No newline at end of file From a43a15070f47c040c689c9286cd5f731ea55dcc3 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 24 Apr 2025 18:03:02 +0100 Subject: [PATCH 12/82] adding fixes to cpu run_types --- .../nanopyx.core.transform._le_esrrf3d.pyx | 61 +++++++--- src/nanopyx/core/transform/_le_esrrf3d.pyx | 105 ++++++++++++------ src/nanopyx/core/transform/_le_esrrf3d_.cl | 30 +++-- 3 files changed, 137 insertions(+), 59 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 8d19fceb..8d0a0f38 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -84,8 +84,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -125,10 +128,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -169,6 +172,8 @@ class eSRRF3D(LiquidEngine): # create output cl buffer output_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + if mode == "std": + mean_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) # create cl code, program and kernels cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) @@ -205,7 +210,18 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): - # insert kernel code here + # fill all buffers with zeros + cl.enqueue_fill_buffer(cl_queue, input_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, slices_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, rows_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, cols_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, rgc_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, output_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, slices_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + cl.enqueue_fill_buffer(cl_queue, rows_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + cl.enqueue_fill_buffer(cl_queue, cols_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + if mode == "std": + cl.enqueue_fill_buffer(cl_queue, mean_buffer, np.float32(0), 0, output_array.nbytes).wait() # interpolate image interpolate_kernel( @@ -282,9 +298,9 @@ class eSRRF3D(LiquidEngine): cols_gradient_magnified_buffer, input_magnified_buffer, rgc_buffer, - np.int32(output_array.shape[0]), - np.int32(output_array.shape[1]), - np.int32(output_array.shape[2]), + np.int32(output_shape[0]), + np.int32(output_shape[1]), + np.int32(output_shape[2]), np.int32(magnification_xy), np.int32(magnification_z), np.float32(voxel_ratio), @@ -298,15 +314,26 @@ class eSRRF3D(LiquidEngine): np.int32(doIntensityWeighting), np.int32(frame_i), ).wait() - - time_projection_kernel( - cl_queue, - (output_array.shape[0], output_array.shape[1], output_array.shape[2]), - None, - rgc_buffer, - output_buffer, - np.int32(frame_i), - ).wait() + + if mode == "std": + time_projection_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + rgc_buffer, + mean_buffer, + output_buffer, + np.int32(frame_i), + ).wait() + else: + time_projection_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + rgc_buffer, + output_buffer, + np.int32(frame_i), + ).wait() cl_queue.finish() cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index fe416c2d..61556274 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -79,8 +79,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -114,10 +117,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -153,8 +156,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -188,10 +194,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -227,8 +233,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -262,10 +271,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -301,8 +310,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -336,10 +348,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -374,8 +386,11 @@ class eSRRF3D(LiquidEngine): n_rows_mag = n_rows * _magnification_xy n_cols_mag = n_cols * _magnification_xy + cdef float[:, :, :] rgc_mean # create all necessary arrays cdef float[:, :, :] rgc_out = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) + if mode == "std": + rgc_mean = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] image_interpolated = np.zeros((n_slices_mag, n_rows_mag, n_cols_mag), dtype=np.float32) cdef float[:, :, :] gradients_col = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) cdef float[:, :, :] gradients_row = np.zeros((n_slices, n_rows, n_cols), dtype=np.float32) @@ -409,10 +424,10 @@ class eSRRF3D(LiquidEngine): if mode == "average": rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (rgc_val - rgc_out[sM, rM, cM]) / (f_i + 1) elif mode == "std": - delta = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta) / (f_i + 1) - delta_2 = rgc_val - rgc_out[sM, rM, cM] - rgc_out[sM, rM, cM] = rgc_out[sM, rM, cM] + (delta * delta_2) / (f_i + 1) + delta = rgc_val - rgc_mean[sM, rM, cM] + rgc_mean[sM, rM, cM] += delta / (f_i + 1) + delta_2 = rgc_val - rgc_mean[sM, rM, cM] + rgc_out[sM, rM, cM] += delta * delta_2 if mode == "std": rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out @@ -452,6 +467,8 @@ class eSRRF3D(LiquidEngine): # create output cl buffer output_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) + if mode == "std": + mean_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) # create cl code, program and kernels cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) @@ -488,7 +505,18 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): - # insert kernel code here + # fill all buffers with zeros + cl.enqueue_fill_buffer(cl_queue, input_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, slices_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, rows_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, cols_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, rgc_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, output_buffer, np.float32(0), 0, output_array.nbytes).wait() + cl.enqueue_fill_buffer(cl_queue, slices_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + cl.enqueue_fill_buffer(cl_queue, rows_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + cl.enqueue_fill_buffer(cl_queue, cols_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() + if mode == "std": + cl.enqueue_fill_buffer(cl_queue, mean_buffer, np.float32(0), 0, output_array.nbytes).wait() # interpolate image interpolate_kernel( @@ -565,9 +593,9 @@ class eSRRF3D(LiquidEngine): cols_gradient_magnified_buffer, input_magnified_buffer, rgc_buffer, - np.int32(output_array.shape[0]), - np.int32(output_array.shape[1]), - np.int32(output_array.shape[2]), + np.int32(output_shape[0]), + np.int32(output_shape[1]), + np.int32(output_shape[2]), np.int32(magnification_xy), np.int32(magnification_z), np.float32(voxel_ratio), @@ -581,15 +609,26 @@ class eSRRF3D(LiquidEngine): np.int32(doIntensityWeighting), np.int32(frame_i), ).wait() - - time_projection_kernel( - cl_queue, - (output_array.shape[0], output_array.shape[1], output_array.shape[2]), - None, - rgc_buffer, - output_buffer, - np.int32(frame_i), - ).wait() + + if mode == "std": + time_projection_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + rgc_buffer, + mean_buffer, + output_buffer, + np.int32(frame_i), + ).wait() + else: + time_projection_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + rgc_buffer, + output_buffer, + np.int32(frame_i), + ).wait() cl_queue.finish() cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 1012861e..29288e8a 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -223,12 +223,19 @@ __kernel void time_projection_average(__global float* current_slice, __global fl int current_index = s * rows * cols + row * cols + col; - output[current_index] += current_slice[current_index]; + // Ensure proper initialization for the first frame + if (frame_i == 0) { + output[current_index] = current_slice[current_index]; + } else { + // Accumulate the average + output[current_index] += (current_slice[current_index] - output[current_index]) / (frame_i + 1); + } } __kernel void time_projection_std( __global float* current_slice, - __global float* output, + __global float* mean_output, + __global float* variance_output, int frame_i ) { int s = get_global_id(0); @@ -240,11 +247,16 @@ __kernel void time_projection_std( int current_index = s * rows * cols + row * cols + col; - // Calculate the running mean - float delta = current_slice[current_index] - output[current_index]; - output[current_index] += delta / (frame_i + 1); - - // Update the running variance - float delta2 = current_slice[current_index] - output[current_index]; - output[current_index] += delta * delta2 / (frame_i + 1); + // Ensure proper initialization for the first frame + if (frame_i == 0) { + mean_output[current_index] = current_slice[current_index]; + variance_output[current_index] = 0.0f; + } else { + // Update the running mean + float delta = current_slice[current_index] - mean_output[current_index]; + mean_output[current_index] += delta / (frame_i + 1); + + // Update the running variance + variance_output[current_index] += delta * (current_slice[current_index] - mean_output[current_index]); + } } \ No newline at end of file From a60ab43ce8598b001e0e2a59e9ce03ef1ef32cad Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 29 Apr 2025 18:19:48 +0100 Subject: [PATCH 13/82] time projection fixes, still some bugs in CL --- .../nanopyx.core.transform._le_esrrf3d.pyx | 15 +--------- src/nanopyx/core/transform/_le_esrrf3d.pyx | 21 +++----------- src/nanopyx/core/transform/_le_esrrf3d_.cl | 29 +++++++++---------- 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 8d0a0f38..5fdfb3e0 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -210,19 +210,6 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): - # fill all buffers with zeros - cl.enqueue_fill_buffer(cl_queue, input_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, slices_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, rows_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, cols_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, rgc_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, output_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, slices_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - cl.enqueue_fill_buffer(cl_queue, rows_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - cl.enqueue_fill_buffer(cl_queue, cols_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - if mode == "std": - cl.enqueue_fill_buffer(cl_queue, mean_buffer, np.float32(0), 0, output_array.nbytes).wait() - # interpolate image interpolate_kernel( cl_queue, @@ -340,4 +327,4 @@ class eSRRF3D(LiquidEngine): if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) else: - return np.asarray(output_array / image.shape[0]) + return np.asarray(output_array) diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 61556274..b2ae487c 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -505,19 +505,6 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): - # fill all buffers with zeros - cl.enqueue_fill_buffer(cl_queue, input_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, slices_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, rows_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, cols_gradient_magnified_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, rgc_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, output_buffer, np.float32(0), 0, output_array.nbytes).wait() - cl.enqueue_fill_buffer(cl_queue, slices_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - cl.enqueue_fill_buffer(cl_queue, rows_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - cl.enqueue_fill_buffer(cl_queue, cols_gradient_buffer, np.float32(0), 0, image.shape[1] * image.shape[2] * image.shape[3] * np.dtype(np.float32).itemsize).wait() - if mode == "std": - cl.enqueue_fill_buffer(cl_queue, mean_buffer, np.float32(0), 0, output_array.nbytes).wait() - # interpolate image interpolate_kernel( cl_queue, @@ -588,9 +575,9 @@ class eSRRF3D(LiquidEngine): cl_queue, (highest_slice - lowest_slice, highest_row - lowest_row, highest_col - lowest_col), None, - slices_gradient_magnified_buffer, - rows_gradient_magnified_buffer, - cols_gradient_magnified_buffer, + slices_gradient_buffer, + rows_gradient_buffer, + cols_gradient_buffer, input_magnified_buffer, rgc_buffer, np.int32(output_shape[0]), @@ -635,4 +622,4 @@ class eSRRF3D(LiquidEngine): if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) else: - return np.asarray(output_array / image.shape[0]) + return np.asarray(output_array) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 29288e8a..b7e54f34 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -101,14 +101,6 @@ double _c_calculate_dw3D(double distance, double distance_xy, double distance_z, return pow(D_weight, 4); } -double _c_calculate_dw_xy(double distance_xy, double tSS) { - return pow((distance_xy * exp((-distance_xy * distance_xy) / tSS)), 4); -} - -double _c_calculate_dw_z(double distance_z, double tSS_z) { - return pow((distance_z * exp((-distance_z * distance_z) / tSS_z)), 4); -} - double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { float Dk = sqrt((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); if (isnan(Dk)) { @@ -126,8 +118,8 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ float yc = (yM) / magnification_xy; float zc = (sliceM) / magnification_z; - float RGC = 0; - float distanceWeightSum = 0; + float RGC = 0.0f; + float distanceWeightSum = 0.0f; int _start = -(int)(2 * fwhm); int _end = (int)(2 * fwhm + 1); @@ -164,14 +156,20 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ Gy = imIntGy[linear_index]; Gz = imIntGz[linear_index]; + // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); + + // distanceWeight_xy = _c_calculate_dw_xy(distance_xy, tSS); + // distanceWeight_z = _c_calculate_dw_z(distance_z, tSS_z); - distanceWeightSum += distanceWeight; + distanceWeightSum = distanceWeightSum + distanceWeight; + // distanceWeightSum_xy += distanceWeight_xy; + // distanceWeightSum_z += distanceWeight_z; GdotR = Gx*dx + Gy*dy + Gz*dz_real; if (GdotR < 0) { Dk = _c_calculate_dk3D(Gx, Gy, Gz, dx, dy, dz_real, distance); - RGC += (Dk * distanceWeight); + RGC = RGC + (Dk * distanceWeight); } } } @@ -181,7 +179,7 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ } } - RGC /= distanceWeightSum; + RGC = RGC / distanceWeightSum; if (RGC >= 0 && sensitivity > 1) { RGC = pow(RGC, sensitivity); @@ -225,10 +223,11 @@ __kernel void time_projection_average(__global float* current_slice, __global fl // Ensure proper initialization for the first frame if (frame_i == 0) { - output[current_index] = current_slice[current_index]; + output[current_index] = current_slice[current_index] / (frame_i + 1); } else { // Accumulate the average - output[current_index] += (current_slice[current_index] - output[current_index]) / (frame_i + 1); + output[current_index] = output[current_index]; + output[current_index] = output[current_index] + ((current_slice[current_index] - output[current_index]) / (frame_i + 1)); } } From fd27189add7afd384449ff02808fcdab97c27bf8 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Wed, 30 Apr 2025 12:15:38 +0100 Subject: [PATCH 14/82] adding new interpolations --- .../nanopyx.core.transform._le_esrrf3d.pyx | 64 ++++++-- src/nanopyx/core/transform/_le_esrrf3d.pyx | 70 ++++++--- src/nanopyx/core/transform/_le_esrrf3d_.cl | 137 +++++++++++------- 3 files changed, 187 insertions(+), 84 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 5fdfb3e0..3c9aa0d8 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -157,6 +157,7 @@ class eSRRF3D(LiquidEngine): # create input cl buffers input_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=np.ascontiguousarray(image)) + intermediate_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * magnification_xy * image.shape[3] * magnification_xy * np.dtype(np.float32).itemsize) input_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) # create gradient buffers @@ -178,7 +179,8 @@ class eSRRF3D(LiquidEngine): # create cl code, program and kernels cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) cl_prg = cl.Program(cl_ctx, cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) - interpolate_kernel = cl_prg.interpolate_3d + interpolate_xy_kernel = cl_prg.interpolate_xy_2d + interpolate_z_kernel = cl_prg.interpolate_z_1d gradients_kernel = cl_prg.gradients_3d rgc_kernel = cl_prg.calculate_rgc3D if mode == "average": @@ -211,17 +213,25 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): # interpolate image - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, input_buffer, - input_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), - np.int32(magnification_z), np.int32(frame_i), ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + input_magnified_buffer, + np.int32(magnification_z), + np.int32(frame_i), + ).wait() # calculate gradients gradients_kernel( @@ -238,38 +248,60 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() - - # interpolate gradients - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, slices_gradient_buffer, - slices_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + slices_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, rows_gradient_buffer, - rows_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + rows_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, cols_gradient_buffer, - cols_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + cols_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index b2ae487c..ce42aaae 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -452,6 +452,7 @@ class eSRRF3D(LiquidEngine): # create input cl buffers input_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_ONLY | cl.mem_flags.COPY_HOST_PTR, hostbuf=np.ascontiguousarray(image)) + intermediate_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=image.shape[1] * image.shape[2] * magnification_xy * image.shape[3] * magnification_xy * np.dtype(np.float32).itemsize) input_magnified_buffer = cl.Buffer(cl_ctx, cl.mem_flags.READ_WRITE, size=output_array.nbytes) # create gradient buffers @@ -473,7 +474,8 @@ class eSRRF3D(LiquidEngine): # create cl code, program and kernels cl_code = self._get_cl_code("_le_esrrf3d_.cl", device["DP"]) cl_prg = cl.Program(cl_ctx, cl_code).build(options=["-cl-fast-relaxed-math", "-cl-mad-enable"]) - interpolate_kernel = cl_prg.interpolate_3d + interpolate_xy_kernel = cl_prg.interpolate_xy_2d + interpolate_z_kernel = cl_prg.interpolate_z_1d gradients_kernel = cl_prg.gradients_3d rgc_kernel = cl_prg.calculate_rgc3D if mode == "average": @@ -506,17 +508,25 @@ class eSRRF3D(LiquidEngine): # loop over frames: for frame_i in range(image.shape[0]): # interpolate image - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, input_buffer, - input_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), - np.int32(magnification_z), np.int32(frame_i), ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + input_magnified_buffer, + np.int32(magnification_z), + np.int32(frame_i), + ).wait() # calculate gradients gradients_kernel( @@ -533,38 +543,60 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() - - # interpolate gradients - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, slices_gradient_buffer, - slices_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + slices_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, rows_gradient_buffer, - rows_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + rows_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() - interpolate_kernel( + interpolate_xy_kernel( cl_queue, - (output_shape[0], output_shape[1], output_shape[2]), + (image.shape[1], output_shape[1], output_shape[2]), None, cols_gradient_buffer, - cols_gradient_magnified_buffer, + intermediate_buffer, np.int32(magnification_xy), + np.int32(frame_i), + ).wait() + interpolate_z_kernel( + cl_queue, + (output_shape[0], output_shape[1], output_shape[2]), + None, + intermediate_buffer, + cols_gradient_magnified_buffer, np.int32(magnification_z), np.int32(frame_i), ).wait() @@ -575,9 +607,9 @@ class eSRRF3D(LiquidEngine): cl_queue, (highest_slice - lowest_slice, highest_row - lowest_row, highest_col - lowest_col), None, - slices_gradient_buffer, - rows_gradient_buffer, - cols_gradient_buffer, + slices_gradient_magnified_buffer, + rows_gradient_magnified_buffer, + cols_gradient_magnified_buffer, input_magnified_buffer, rgc_buffer, np.int32(output_shape[0]), diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index b7e54f34..98f05bbf 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -1,63 +1,102 @@ -float catmull_rom_weight(float t, int offset) { - // Catmull-Rom spline weight calculation - t = fabs(t - offset); - if (t < 1.0f) { - return 1.0f - 2.0f * t * t + t * t * t; - } else if (t < 2.0f) { - return 4.0f - 8.0f * t + 5.0f * t * t - t * t * t; +__kernel void interpolate_z_1d(__global float* image, __global float* image_out, int magnification_z, int frame_i) { + int sM = get_global_id(0); + int r = get_global_id(1); + int c = get_global_id(2); + float slice = sM / magnification_z; + + int slicesM = get_global_size(0); + int rows = get_global_size(1); + int cols = get_global_size(2); + int slices = (int)(slicesM / magnification_z); + + int slice0 = (int)floor(slice); + int slice1 = slice0 + 1; + + float weight1 = slice - slice0; + float weight0 = 1.0f - weight1; + + if (slice0 >= 0 && slice1 < slices) { + image_out[sM * rows * cols + r * cols + c] = + weight0 * image[frame_i * slices * rows * cols + slice0 * rows * cols + r * cols + c] + + weight1 * image[frame_i * slices * rows * cols + slice1 * rows * cols + r * cols + c]; + } else if (slice0 >= 0) { + image_out[sM * rows * cols + r * cols + c] = + image[frame_i * slices * rows * cols + slice0 * rows * cols + r * cols + c]; + } else if (slice1 < slices) { + image_out[sM * rows * cols + r * cols + c] = + image[frame_i * slices * rows * cols + slice1 * rows * cols + r * cols + c]; + } else { + image_out[sM * rows * cols + r * cols + c] = 0.0f; } - return 0.0f; } +float _c_cubic(double v) { + double a = 0.5; + double z = 0; + if (v < 0) { + v = -v; + } + if (v < 1) { + z = v * v * (v * (-a + 2) + (a - 3)) + 1; + } else if (v < 2) { + z = -a * v * v * v + 5 * a * v * v - 8 * a * v + 4 * a; + } + return z; +} -__kernel void interpolate_3d(__global float* image, __global float* magnified_image, int magnification_xy, int magnification_z, int f) { - int s = get_global_id(0); - int row = get_global_id(1); - int col = get_global_id(2); +float _c_interpolate_cr(__global float *image, int s, int r, float c, int rows, int cols) { + // return 0 if r OR c positions do not exist in image + if (r < 0 || r >= rows || c < 0 || c >= cols) { + return 0; + } - int slices = get_global_size(0) / magnification_z; - int rows = get_global_size(1) / magnification_xy; - int cols = get_global_size(2) / magnification_xy; - - // Linear interpolation in z - float z_ratio = (float)s / magnification_z; - int z0 = (int)floor(z_ratio); - int z1 = z0 + 1; - z1 = z1 < slices ? z1 : slices - 1; - float z_weight = z_ratio - z0; - - // Bicubic interpolation in xy - float y_ratio = (float)row / magnification_xy; - float x_ratio = (float)col / magnification_xy; - int y0 = (int)floor(y_ratio); - int x0 = (int)floor(x_ratio); - float y_weight = y_ratio - y0; - float x_weight = x_ratio - x0; - - float result = 0.0f; - for (int dz = 0; dz <= 1; dz++) { - int z = dz == 0 ? z0 : z1; - float z_contrib = dz == 0 ? (1 - z_weight) : z_weight; - - for (int dy = -1; dy <= 2; dy++) { - int y = y0 + dy; - y = y > 0 ? (y < rows ? y : rows - 1) : 0; - float y_contrib = catmull_rom_weight(y_weight, dy); - - for (int dx = -1; dx <= 2; dx++) { - int x = x0 + dx; - x = x > 0 ? (x < cols ? x : cols - 1) : 0; - float x_contrib = catmull_rom_weight(x_weight, dx); - - float pixel_value = image[f * slices * rows * cols + z * rows * cols + y * cols + x]; - result += pixel_value * z_contrib * y_contrib * x_contrib; + const int r_int = (int)floor((float) (r - 0.5)); + const int c_int = (int)floor((float) (c - 0.5)); + double q = 0; + double p = 0; + + int r_neighbor, c_neighbor; + + for (int j = 0; j < 4; j++) { + c_neighbor = c_int - 1 + j; + p = 0; + if (c_neighbor < 0 || c_neighbor >= cols) { + continue; + } + + for (int i = 0; i < 4; i++) { + r_neighbor = r_int - 1 + i; + if (r_neighbor < 0 || r_neighbor >= rows) { + continue; } + p = p + image[s * rows * cols + r_neighbor * cols + c_neighbor] * + _c_cubic(r - (r_neighbor + 0.5)); } + q = q + p * _c_cubic(c - (c_neighbor + 0.5)); } + return image[s * rows * cols + r_neighbor * cols + c_neighbor]; //q; +} - magnified_image[s * get_global_size(1) * get_global_size(2) + row * get_global_size(2) + col] = result; +__kernel void interpolate_xy_2d(__global float* image, __global float* image_out, int magnification_xy, int frame_i) { + int s = get_global_id(0); + int rM = get_global_id(1); + int cM = get_global_id(2); + + float row = rM / magnification_xy; + float col = cM / magnification_xy; + + int slices = get_global_size(0); + int rowsM = get_global_size(1); + int colsM = get_global_size(2); + + int rows = (int)(rowsM / magnification_xy); + int cols = (int)(colsM / magnification_xy); + int nPixels_out = slices * rowsM * colsM; + + image_out[s * rowsM * colsM + rM * colsM + cM] = _c_interpolate_cr(&image[frame_i * slices * rows * cols], s, row, col, rows, cols); } + void _c_gradient_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int slices, int rows, int cols, int z_i, int y_i, int x_i) { float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; From 100b769193d6aba4478c5b40e4335749476fff5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Mon, 5 May 2025 14:26:10 +0100 Subject: [PATCH 15/82] Rewriting 3D gradient calculations for simple central differences --- src/include/_c_gradients.c | 53 +++++++++++++--------- src/nanopyx/core/transform/_le_esrrf3d_.cl | 53 +++++++++++++--------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/include/_c_gradients.c b/src/include/_c_gradients.c index a4b7b5a3..da58a31a 100644 --- a/src/include/_c_gradients.c +++ b/src/include/_c_gradients.c @@ -65,40 +65,51 @@ void _c_gradient_roberts_cross(float* image, float* imGc, float* imGr, int rows, } } -// as in https://www.nature.com/articles/s41592-022-01669-y#MOESM1 -// 3D Gradient calculation + void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) { - float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; + float z0x0y0, z0x0y1, z0x1y0, z0x_1y0, z0x0y_1; + float z1x0y0, z_1x0y0; - int z_i, y_i, x_i, z_1, y_1, x_1; + int z_plus1, y_plus1, x_plus1, + z_minus1, y_minus1, x_minus1; + int z_i, y_i, x_i; + for (z_i = 0; z_i < slices; z_i++) { for (y_i = 0; y_i < rows; y_i++) { for (x_i = 0; x_i < cols; x_i++) { - z_1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; - y_1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; - x_1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; - ip0 = image[z_i * rows * cols + y_i * cols + x_i]; - ip1 = image[z_i * rows * cols + y_i * cols + x_1]; - ip2 = image[z_i * rows * cols + y_1 * cols + x_i]; - ip3 = image[z_i * rows * cols + y_1 * cols + x_1]; - ip4 = image[z_1 * rows * cols + y_i * cols + x_i]; - ip5 = image[z_1 * rows * cols + y_i * cols + x_1]; - ip6 = image[z_1 * rows * cols + y_1 * cols + x_i]; - ip7 = image[z_1 * rows * cols + y_1 * cols + x_1]; - imGc[z_i * rows* cols + y_i * cols + x_i] = - (ip1 + ip3 + ip5 + ip7 - ip0 - ip2 - ip4 - ip6) / 4; - imGr[z_i * rows* cols + y_i * cols + x_i] = - (ip2 + ip3 + ip6 + ip7 - ip0 - ip1 - ip4 - ip5) / 4; - imGs[z_i * rows* cols + y_i * cols + x_i] = - (ip4 + ip5 + ip6 + ip7 - ip0 - ip1 - ip2 - ip3) / 4; + z_plus1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; + y_plus1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; + x_plus1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; + + z_minus1 = z_i <= 0 ? 0 : z_i - 1; + y_minus1 = y_i <= 0 ? 0 : y_i - 1; + x_minus1 = x_i <= 0 ? 0 : x_i - 1; + + // z=0 + z0x0y0 = image[z_i * rows * cols + y_i * cols + x_i]; // central pixel + z0x0y1 = image[z_i * rows * cols + y_plus1 * cols + x_i]; // y+1 + z0x1y0 = image[z_i * rows * cols + y_i * cols + x_plus1]; // x+1 + z0x0y_1 = image[z_i * rows * cols + y_minus1 * cols + x_i]; // y-1 + z0x_1y0 = image[z_i * rows * cols + y_i * cols + x_minus1]; // x-1 + + // z=1 + z1x0y0 = image[z_plus1 * rows * cols + y_i * cols + x_i]; // z+1 + + // z=-1 + z_1x0y0 = image[z_minus1 * rows * cols + y_i * cols + x_i]; // z-1 + + imGc[z_i * rows* cols + y_i * cols + x_i] = (z0x1y0 - z0x_1y0)/2; + imGr[z_i * rows* cols + y_i * cols + x_i] = (z0x0y1 - z0x0y_1)/2; + imGs[z_i * rows* cols + y_i * cols + x_i] = (z0x0y0 - z_1x0y0)/2; } } } } + void _c_gradient_2_point_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) { int s_i, y_i, x_i, s0, y0, x0; diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index b7e54f34..cae6edd8 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -59,27 +59,36 @@ __kernel void interpolate_3d(__global float* image, __global float* magnified_im } void _c_gradient_3d(__global float* image, __global float* imGc, __global float* imGr, __global float* imGs, int slices, int rows, int cols, int z_i, int y_i, int x_i) { - float ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7; - - int z_1, y_1, x_1; - - z_1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; - y_1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; - x_1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; - ip0 = image[z_i * rows * cols + y_i * cols + x_i]; - ip1 = image[z_i * rows * cols + y_i * cols + x_1]; - ip2 = image[z_i * rows * cols + y_1 * cols + x_i]; - ip3 = image[z_i * rows * cols + y_1 * cols + x_1]; - ip4 = image[z_1 * rows * cols + y_i * cols + x_i]; - ip5 = image[z_1 * rows * cols + y_i * cols + x_1]; - ip6 = image[z_1 * rows * cols + y_1 * cols + x_i]; - ip7 = image[z_1 * rows * cols + y_1 * cols + x_1]; - imGc[z_i * rows* cols + y_i * cols + x_i] = - (ip1 + ip3 + ip5 + ip7 - ip0 - ip2 - ip4 - ip6) / 4; - imGr[z_i * rows* cols + y_i * cols + x_i] = - (ip2 + ip3 + ip6 + ip7 - ip0 - ip1 - ip4 - ip5) / 4; - imGs[z_i * rows* cols + y_i * cols + x_i] = - (ip4 + ip5 + ip6 + ip7 - ip0 - ip1 - ip2 - ip3) / 4; + float z0x0y0, z0x0y1, z0x1y0, z0x_1y0, z0x0y_1; + float z1x0y0, z_1x0y0; + + int z_plus1, y_plus1, x_plus1, + z_minus1, y_minus1, x_minus1; + + z_plus1 = z_i >= slices - 1 ? slices - 1 : z_i + 1; + y_plus1 = y_i >= rows - 1 ? rows - 1 : y_i + 1; + x_plus1 = x_i >= cols - 1 ? cols - 1 : x_i + 1; + + z_minus1 = z_i <= 0 ? 0 : z_i - 1; + y_minus1 = y_i <= 0 ? 0 : y_i - 1; + x_minus1 = x_i <= 0 ? 0 : x_i - 1; + + // z=0 + z0x0y0 = image[z_i * rows * cols + y_i * cols + x_i]; // central pixel + z0x0y1 = image[z_i * rows * cols + y_plus1 * cols + x_i]; // y+1 + z0x1y0 = image[z_i * rows * cols + y_i * cols + x_plus1]; // x+1 + z0x0y_1 = image[z_i * rows * cols + y_minus1 * cols + x_i]; // y-1 + z0x_1y0 = image[z_i * rows * cols + y_i * cols + x_minus1]; // x-1 + + // z=1 + z1x0y0 = image[z_plus1 * rows * cols + y_i * cols + x_i]; // z+1 + + // z=-1 + z_1x0y0 = image[z_minus1 * rows * cols + y_i * cols + x_i]; // z-1 + + imGc[z_i * rows* cols + y_i * cols + x_i] = (z0x1y0 - z0x_1y0)/2; + imGr[z_i * rows* cols + y_i * cols + x_i] = (z0x0y1 - z0x0y_1)/2; + imGs[z_i * rows* cols + y_i * cols + x_i] = (z0x0y0 - z_1x0y0)/2; } @@ -226,7 +235,7 @@ __kernel void time_projection_average(__global float* current_slice, __global fl output[current_index] = current_slice[current_index] / (frame_i + 1); } else { // Accumulate the average - output[current_index] = output[current_index]; + // output[current_index] = output[current_index]; output[current_index] = output[current_index] + ((current_slice[current_index] - output[current_index]) / (frame_i + 1)); } } From d84a9a4fa62b1fee3cac5a73aa0fe04296c955c0 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 5 May 2025 16:34:50 +0100 Subject: [PATCH 16/82] new z interpolator --- src/nanopyx/core/transform/_interpolation.pyx | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/nanopyx/core/transform/_interpolation.pyx b/src/nanopyx/core/transform/_interpolation.pyx index ed9ab794..ee2d87dc 100644 --- a/src/nanopyx/core/transform/_interpolation.pyx +++ b/src/nanopyx/core/transform/_interpolation.pyx @@ -7,6 +7,8 @@ import cython from ._le_interpolation_catmull_rom import ShiftAndMagnify as ShiftMagnify_CR +from cython.parallel import prange + cdef extern from "_c_interpolation_catmull_rom.h": float _c_cr_interpolate "_c_interpolate" (float *image, float row, float col, int rows, int cols) @@ -46,11 +48,45 @@ def linear_interpolation_1D_z(image, magnification): return image_interpolated +cdef _linear_interpolation_1D_z(float[:, :, :] image, int magnification_z): + + cdef int slices = image.shape[0] + cdef int slicesM = int(image.shape[0] * magnification_z) + cdef int rows = image.shape[1] + cdef int cols = image.shape[2] + + cdef int sM, r, c + cdef int slice0, slice1, slc + cdef float weight0, weight1 + + cdef float[:, :, :] image_out = np.zeros((slicesM, rows, cols), dtype=np.float32) + + with nogil: + for sM in range(slicesM): + for r in prange(rows): + for c in prange(cols): + slice0 = sM / magnification_z; + slice1 = slice0 + 1; + + weight1 = sM % magnification_z; + weight0 = 1.0 - weight1; + + if slice0 >= 0 and slice1 < slices: + image_out[sM, r, c] = weight0 * image[slice0, r, c] + weight1 * image[slice1, r, c]; + elif slice0 >= 0: + image_out[sM, r, c] = image[slice0, r, c]; + elif slice1 < slices: + image_out[sM, r, c] = image[slice1, r, c]; + else: + image_out[sM, r, c] = 0.0; + + return image_out + def interpolate_3d_zlinear(image, magnification_xy: int = 5, magnification_z: int = 5): interpolator_xy = ShiftMagnify_CR(verbose=False) - xy_interpolated = interpolator_xy.run(np.ascontiguousarray(image), 0, 0, magnification_xy, magnification_xy) - z_interpolated = linear_interpolation_1D_z(xy_interpolated, magnification_z) + xy_interpolated = interpolator_xy.run(image, 0, 0, 1, 1) + z_interpolated = _linear_interpolation_1D_z(image, magnification_z) return z_interpolated From 271934da1ea8a20592fd6986f6c1d4f8e3407c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Mon, 5 May 2025 17:19:34 +0100 Subject: [PATCH 17/82] Fixing an offset in esrrf's CR interpolation --- src/nanopyx/core/transform/_le_esrrf3d_.cl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 685b901b..83e2eea7 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -44,7 +44,7 @@ float _c_cubic(double v) { return z; } -float _c_interpolate_cr(__global float *image, int s, int r, float c, int rows, int cols) { +float _c_interpolate_cr(__global float *image, int s, int r, int c, int rows, int cols) { // return 0 if r OR c positions do not exist in image if (r < 0 || r >= rows || c < 0 || c >= cols) { return 0; @@ -74,7 +74,7 @@ float _c_interpolate_cr(__global float *image, int s, int r, float c, int rows, } q = q + p * _c_cubic(c - (c_neighbor + 0.5)); } - return image[s * rows * cols + r_neighbor * cols + c_neighbor]; //q; + return q; } __kernel void interpolate_xy_2d(__global float* image, __global float* image_out, int magnification_xy, int frame_i) { From 6551d537b04614502cce24c1d7ec1bf19bd396f3 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 5 May 2025 18:07:07 +0100 Subject: [PATCH 18/82] readding interpolation --- src/nanopyx/core/transform/_interpolation.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/_interpolation.pyx b/src/nanopyx/core/transform/_interpolation.pyx index ee2d87dc..f437d8dc 100644 --- a/src/nanopyx/core/transform/_interpolation.pyx +++ b/src/nanopyx/core/transform/_interpolation.pyx @@ -87,6 +87,6 @@ def interpolate_3d_zlinear(image, magnification_xy: int = 5, magnification_z: in interpolator_xy = ShiftMagnify_CR(verbose=False) xy_interpolated = interpolator_xy.run(image, 0, 0, 1, 1) - z_interpolated = _linear_interpolation_1D_z(image, magnification_z) + z_interpolated = _linear_interpolation_1D_z(xy_interpolated, magnification_z) return z_interpolated From 387935ca17316f38729be919bf2b04fad459d929 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 6 May 2025 10:22:09 +0100 Subject: [PATCH 19/82] iterative changes --- .../nanopyx.core.transform._le_esrrf3d.pyx | 34 +++++++-------- src/nanopyx/core/transform/_interpolation.pyx | 10 ++++- src/nanopyx/core/transform/_le_esrrf3d.pyx | 42 +++++++++---------- src/nanopyx/core/transform/_le_esrrf3d_.cl | 32 ++++++++++---- 4 files changed, 71 insertions(+), 47 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 3c9aa0d8..897bfc65 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -136,7 +136,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) % endfor def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): @@ -219,7 +219,7 @@ class eSRRF3D(LiquidEngine): None, input_buffer, intermediate_buffer, - np.int32(magnification_xy), + np.float32(magnification_xy), np.int32(frame_i), ).wait() @@ -229,8 +229,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, input_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() # calculate gradients @@ -255,8 +255,8 @@ class eSRRF3D(LiquidEngine): None, slices_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -264,8 +264,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, slices_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() interpolate_xy_kernel( @@ -274,8 +274,8 @@ class eSRRF3D(LiquidEngine): None, rows_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -283,8 +283,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, rows_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() interpolate_xy_kernel( @@ -293,8 +293,8 @@ class eSRRF3D(LiquidEngine): None, cols_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -302,8 +302,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, cols_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() # calculate rgc @@ -354,7 +354,7 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() cl_queue.finish() - cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() + cl.enqueue_copy(cl_queue, output_array, rows_gradient_magnified_buffer).wait() if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) diff --git a/src/nanopyx/core/transform/_interpolation.pyx b/src/nanopyx/core/transform/_interpolation.pyx index f437d8dc..da7e1638 100644 --- a/src/nanopyx/core/transform/_interpolation.pyx +++ b/src/nanopyx/core/transform/_interpolation.pyx @@ -86,7 +86,13 @@ cdef _linear_interpolation_1D_z(float[:, :, :] image, int magnification_z): def interpolate_3d_zlinear(image, magnification_xy: int = 5, magnification_z: int = 5): interpolator_xy = ShiftMagnify_CR(verbose=False) - xy_interpolated = interpolator_xy.run(image, 0, 0, 1, 1) - z_interpolated = _linear_interpolation_1D_z(xy_interpolated, magnification_z) + if magnification_xy > 1: + xy_interpolated = np.asarray(interpolator_xy.run(np.ascontiguousarray(image), 0, 0, magnification_xy, magnification_xy)) + else: + xy_interpolated = np.asarray(image) + if magnification_z > 1: + z_interpolated = _linear_interpolation_1D_z(xy_interpolated, magnification_z) + else: + z_interpolated = np.asarray(xy_interpolated) return z_interpolated diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index ce42aaae..bb174408 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -125,7 +125,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -202,7 +202,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -279,7 +279,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -356,7 +356,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -432,7 +432,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(rgc_out) + return np.asarray(gradients_row_mag) def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @@ -514,7 +514,7 @@ class eSRRF3D(LiquidEngine): None, input_buffer, intermediate_buffer, - np.int32(magnification_xy), + np.float32(magnification_xy), np.int32(frame_i), ).wait() @@ -524,8 +524,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, input_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() # calculate gradients @@ -550,8 +550,8 @@ class eSRRF3D(LiquidEngine): None, slices_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -559,8 +559,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, slices_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() interpolate_xy_kernel( @@ -569,8 +569,8 @@ class eSRRF3D(LiquidEngine): None, rows_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -578,8 +578,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, rows_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() interpolate_xy_kernel( @@ -588,8 +588,8 @@ class eSRRF3D(LiquidEngine): None, cols_gradient_buffer, intermediate_buffer, - np.int32(magnification_xy), - np.int32(frame_i), + np.float32(magnification_xy), + np.int32(0), ).wait() interpolate_z_kernel( cl_queue, @@ -597,8 +597,8 @@ class eSRRF3D(LiquidEngine): None, intermediate_buffer, cols_gradient_magnified_buffer, - np.int32(magnification_z), - np.int32(frame_i), + np.float32(magnification_z), + np.int32(0), ).wait() # calculate rgc @@ -649,7 +649,7 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() cl_queue.finish() - cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() + cl.enqueue_copy(cl_queue, output_array, rows_gradient_magnified_buffer).wait() if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 83e2eea7..efbf8b9a 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -1,4 +1,4 @@ -__kernel void interpolate_z_1d(__global float* image, __global float* image_out, int magnification_z, int frame_i) { +__kernel void interpolate_z_1d(__global float* image, __global float* image_out, float magnification_z, int frame_i) { int sM = get_global_id(0); int r = get_global_id(1); int c = get_global_id(2); @@ -15,7 +15,11 @@ __kernel void interpolate_z_1d(__global float* image, __global float* image_out, float weight1 = slice - slice0; float weight0 = 1.0f - weight1; - if (slice0 >= 0 && slice1 < slices) { + if (magnification_z == 1) { + image_out[sM * rows * cols + r * cols + c] = + image[frame_i * slices * rows * cols + slice0 * rows * cols + r * cols + c]; + } + else if (slice0 >= 0 && slice1 < slices) { image_out[sM * rows * cols + r * cols + c] = weight0 * image[frame_i * slices * rows * cols + slice0 * rows * cols + r * cols + c] + weight1 * image[frame_i * slices * rows * cols + slice1 * rows * cols + r * cols + c]; @@ -44,7 +48,7 @@ float _c_cubic(double v) { return z; } -float _c_interpolate_cr(__global float *image, int s, int r, int c, int rows, int cols) { +float _c_interpolate_cr(__global float *image, float r, float c, int rows, int cols) { // return 0 if r OR c positions do not exist in image if (r < 0 || r >= rows || c < 0 || c >= cols) { return 0; @@ -69,7 +73,7 @@ float _c_interpolate_cr(__global float *image, int s, int r, int c, int rows, in if (r_neighbor < 0 || r_neighbor >= rows) { continue; } - p = p + image[s * rows * cols + r_neighbor * cols + c_neighbor] * + p = p + image[r_neighbor * cols + c_neighbor] * _c_cubic(r - (r_neighbor + 0.5)); } q = q + p * _c_cubic(c - (c_neighbor + 0.5)); @@ -77,7 +81,7 @@ float _c_interpolate_cr(__global float *image, int s, int r, int c, int rows, in return q; } -__kernel void interpolate_xy_2d(__global float* image, __global float* image_out, int magnification_xy, int frame_i) { +__kernel void interpolate_xy_2d(__global float* image, __global float* image_out, float magnification_xy, int frame_i) { int s = get_global_id(0); int rM = get_global_id(1); int cM = get_global_id(2); @@ -91,9 +95,15 @@ __kernel void interpolate_xy_2d(__global float* image, __global float* image_out int rows = (int)(rowsM / magnification_xy); int cols = (int)(colsM / magnification_xy); - int nPixels_out = slices * rowsM * colsM; - image_out[s * rowsM * colsM + rM * colsM + cM] = _c_interpolate_cr(&image[frame_i * slices * rows * cols], s, row, col, rows, cols); + if (magnification_xy == 1) { + image_out[s * rowsM * colsM + rM * colsM + cM] = + image[frame_i * slices * rows * cols + s * rows * cols + rM * cols + cM]; + } + else { + image_out[s * rowsM * colsM + rM * colsM + cM] = + _c_interpolate_cr(&image[frame_i * slices * rows * cols], row, col, rows, cols); + } } @@ -227,6 +237,10 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ } } + if (distanceWeightSum == 0) { + return 0; + } + RGC = RGC / distanceWeightSum; if (RGC >= 0 && sensitivity > 1) { @@ -235,6 +249,10 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ RGC = 0; } + if (isnan(RGC)) { + RGC = 0; + } + return RGC; } From f6a4c2afd063dad8cd4b295099603442b1921c20 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 6 May 2025 13:49:18 +0100 Subject: [PATCH 20/82] interpolation fixes --- .../nanopyx.core.transform._le_esrrf3d.pyx | 4 +-- src/nanopyx/core/transform/_interpolation.pyx | 20 +++++++++----- src/nanopyx/core/transform/_le_esrrf3d.pyx | 12 ++++----- src/nanopyx/core/transform/_le_esrrf3d_.cl | 27 ++++++++++++------- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 897bfc65..74f996e5 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -136,7 +136,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) % endfor def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): @@ -354,7 +354,7 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() cl_queue.finish() - cl.enqueue_copy(cl_queue, output_array, rows_gradient_magnified_buffer).wait() + cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) diff --git a/src/nanopyx/core/transform/_interpolation.pyx b/src/nanopyx/core/transform/_interpolation.pyx index da7e1638..b09c040b 100644 --- a/src/nanopyx/core/transform/_interpolation.pyx +++ b/src/nanopyx/core/transform/_interpolation.pyx @@ -4,8 +4,12 @@ import numpy as np cimport numpy as np import cython +from math import floor from ._le_interpolation_catmull_rom import ShiftAndMagnify as ShiftMagnify_CR +from ._le_interpolation_bicubic import ShiftAndMagnify as ShiftMagnify_BC +from ._le_interpolation_lanczos import ShiftAndMagnify as ShiftMagnify_Lanczos +from ._le_interpolation_nearest_neighbor import ShiftAndMagnify as ShiftMagnify_NN from cython.parallel import prange @@ -61,15 +65,17 @@ cdef _linear_interpolation_1D_z(float[:, :, :] image, int magnification_z): cdef float[:, :, :] image_out = np.zeros((slicesM, rows, cols), dtype=np.float32) - with nogil: - for sM in range(slicesM): + + for sM in range(slicesM): + slc = sM / magnification_z + slice0 = int(floor(slc)) + slice1 = slice0 + 1 + weight1 = slc - slice0 + weight0 = 1.0 - weight1 + with nogil: for r in prange(rows): for c in prange(cols): - slice0 = sM / magnification_z; - slice1 = slice0 + 1; - - weight1 = sM % magnification_z; - weight0 = 1.0 - weight1; + if slice0 >= 0 and slice1 < slices: image_out[sM, r, c] = weight0 * image[slice0, r, c] + weight1 * image[slice1, r, c]; diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index bb174408..f9ee80b4 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -125,7 +125,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -202,7 +202,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -279,7 +279,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -356,7 +356,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @@ -432,7 +432,7 @@ class eSRRF3D(LiquidEngine): rgc_out = np.sqrt(np.asarray(rgc_out) / n_frames) return rgc_out else: - return np.asarray(gradients_row_mag) + return np.asarray(rgc_out) def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @@ -649,7 +649,7 @@ class eSRRF3D(LiquidEngine): np.int32(frame_i), ).wait() cl_queue.finish() - cl.enqueue_copy(cl_queue, output_array, rows_gradient_magnified_buffer).wait() + cl.enqueue_copy(cl_queue, output_array, output_buffer).wait() if mode == "std": return np.asarray(np.sqrt(output_array / image.shape[0])) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index efbf8b9a..9dca4f2c 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -34,9 +34,9 @@ __kernel void interpolate_z_1d(__global float* image, __global float* image_out, } } -float _c_cubic(double v) { - double a = 0.5; - double z = 0; +float _c_cubic(float v) { + float a = 0.5; + float z = 0; if (v < 0) { v = -v; } @@ -48,6 +48,15 @@ float _c_cubic(double v) { return z; } +float _c_interpolate(__global float *image, float row, float col, int rows, int cols) { + int r = (int)row; + int c = (int)col; + if (r < 0 || r >= rows || c < 0 || c >= cols) { + return 0; + } + return image[r * cols + c]; +} + float _c_interpolate_cr(__global float *image, float r, float c, int rows, int cols) { // return 0 if r OR c positions do not exist in image if (r < 0 || r >= rows || c < 0 || c >= cols) { @@ -56,8 +65,8 @@ float _c_interpolate_cr(__global float *image, float r, float c, int rows, int c const int r_int = (int)floor((float) (r - 0.5)); const int c_int = (int)floor((float) (c - 0.5)); - double q = 0; - double p = 0; + float q = 0; + float p = 0; int r_neighbor, c_neighbor; @@ -151,7 +160,7 @@ __kernel void gradients_3d(__global float* image, __global float* imGs, __global } } -double _c_calculate_dw3D(double distance, double distance_xy, double distance_z,double tSS, double tSS_z) { +float _c_calculate_dw3D(float distance, float distance_xy, float distance_z,float tSS, float tSS_z) { float D_weight_xy, D_weight_z, D_weight; D_weight_xy = (exp((-distance_xy * distance_xy) / tSS)); D_weight_z = (exp((-distance_z * distance_z) / tSS_z)); @@ -159,7 +168,7 @@ double _c_calculate_dw3D(double distance, double distance_xy, double distance_z, return pow(D_weight, 4); } -double _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { +float _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { float Dk = sqrt((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); if (isnan(Dk)) { Dk = distance; @@ -320,8 +329,8 @@ __kernel void time_projection_std( // Update the running mean float delta = current_slice[current_index] - mean_output[current_index]; mean_output[current_index] += delta / (frame_i + 1); - + float delta2 = current_slice[current_index] - mean_output[current_index]; // Update the running variance - variance_output[current_index] += delta * (current_slice[current_index] - mean_output[current_index]); + variance_output[current_index] += delta * delta2; } } \ No newline at end of file From e53ba0d19dde4f656ce4d48e59f90224f78ab186 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 6 May 2025 15:13:43 +0100 Subject: [PATCH 21/82] reverting to catmull rom interpolation --- src/nanopyx/core/transform/_le_esrrf3d_.cl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 9dca4f2c..31a2bf96 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -111,7 +111,7 @@ __kernel void interpolate_xy_2d(__global float* image, __global float* image_out } else { image_out[s * rowsM * colsM + rM * colsM + cM] = - _c_interpolate_cr(&image[frame_i * slices * rows * cols], row, col, rows, cols); + _c_interpolate_cr(&image[frame_i * slices * rows * cols + s * rows * cols], row, col, rows, cols); } } From 5297d26a7f7a408da2294fcd2ed70819653bb7b8 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 6 May 2025 16:10:21 +0100 Subject: [PATCH 22/82] updated default benchmarks --- src/liquid_benchmarks/_le_esrrf3d/eSRRF3D.yml | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/liquid_benchmarks/_le_esrrf3d/eSRRF3D.yml b/src/liquid_benchmarks/_le_esrrf3d/eSRRF3D.yml index 8b5753be..cff089c5 100644 --- a/src/liquid_benchmarks/_le_esrrf3d/eSRRF3D.yml +++ b/src/liquid_benchmarks/_le_esrrf3d/eSRRF3D.yml @@ -1,36 +1,48 @@ -Threaded: - ? '([''shape(1, 10, 100, 100)''], {''magnification_xy'': 2, ''magnification_z'': - 2, ''radius'': 1.5, ''sensitivity'': 1.0, ''doIntensityWeighting'': True})' - : - 600000.0 - - 3.009124042 - - 3.0523655000000005 - - 3.0673777499999986 -Threaded_dynamic: - ? '([''shape(1, 10, 100, 100)''], {''magnification_xy'': 2, ''magnification_z'': - 2, ''radius'': 1.5, ''sensitivity'': 1.0, ''doIntensityWeighting'': True})' - : - 600000.0 - - 2.9377308749999997 - - 3.038488291 - - 2.9445285420000076 - - 3.01370875 -Threaded_guided: - ? '([''shape(1, 10, 100, 100)''], {''magnification_xy'': 2, ''magnification_z'': - 2, ''radius'': 1.5, ''sensitivity'': 1.0, ''doIntensityWeighting'': True})' - : - 600000.0 - - 2.9383910419999992 - - 3.179585459000002 - - 2.9465658749999903 -Threaded_static: - ? '([''shape(1, 10, 100, 100)''], {''magnification_xy'': 2, ''magnification_z'': - 2, ''radius'': 1.5, ''sensitivity'': 1.0, ''doIntensityWeighting'': True})' - : - 600000.0 - - 3.0556022499999997 - - 3.011091624999999 - - 3.0389940410000023 -Unthreaded: - ? '([''shape(1, 10, 100, 100)''], {''magnification_xy'': 2, ''magnification_z'': - 2, ''radius'': 1.5, ''sensitivity'': 1.0, ''doIntensityWeighting'': True})' - : - 600000.0 - - 10.075773250000001 - - 10.147714 - - 10.096515416999999 +opencl: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 0.534501292015193 + - 0.4527143339801114 + - 0.4526337919814978 +threaded: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 1.9790987920132466 + - 1.9240770420001354 + - 1.895047333004186 +threaded_dynamic: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 1.7660593329928815 + - 1.719053625012748 + - 1.7240022089972626 +threaded_guided: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 1.781112333002966 + - 1.8617710830003489 + - 2.0126889999955893 +threaded_static: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 1.8515684169833548 + - 1.8730584580043796 + - 1.8657334169838578 +unthreaded: + ? '([''shape(1, 101, 101, 101)''], {''magnification_xy'': 2, ''magnification_z'': + 2, ''radius'': 1.5, ''radius_z'': 0.5, ''voxel_ratio'': 4.0, ''sensitivity'': + 1.0, ''mode'': ''average'', ''doIntensityWeighting'': False})' + : - 12363612.0 + - 4.863078458001837 + - 4.9081600419885945 + - 4.920950291998452 From 36970733bdacdd13334a6117a647faa3497688c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 6 May 2025 16:51:18 +0100 Subject: [PATCH 23/82] Added esrrd3d to benchmark_all_le_methods --- src/nanopyx/core/utils/benchmark.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nanopyx/core/utils/benchmark.py b/src/nanopyx/core/utils/benchmark.py index be30ea57..a19a3af7 100644 --- a/src/nanopyx/core/utils/benchmark.py +++ b/src/nanopyx/core/utils/benchmark.py @@ -41,6 +41,7 @@ def benchmark_all_le_methods( rgc = nanopyx.core.transform._le_radial_gradient_convergence.RadialGradientConvergence() esrrf = nanopyx.core.transform._le_esrrf.eSRRF() + esrrf3d = nanopyx.core.transform._le_esrrf3d.eSRRF3D() nlm = nanopyx.core.transform._le_nlm_denoising.NLMDenoising() @@ -78,6 +79,9 @@ def benchmark_all_le_methods( for i in range(n_benchmark_runs): esrrf.benchmark(img) + for i in range(n_benchmark_runs): + esrrf3d.benchmark(img[np.newaxis, ...]) + for i in range(n_benchmark_runs): nlm.benchmark(img) From 015c9c4baf5c00ae4b63135b8525808cd1fe918e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 6 May 2025 16:51:41 +0100 Subject: [PATCH 24/82] Deleted old file --- src/nanopyx/methods/esrrf_3d/run.py | 41 ----------------------------- 1 file changed, 41 deletions(-) delete mode 100644 src/nanopyx/methods/esrrf_3d/run.py diff --git a/src/nanopyx/methods/esrrf_3d/run.py b/src/nanopyx/methods/esrrf_3d/run.py deleted file mode 100644 index 0d3dd4b3..00000000 --- a/src/nanopyx/methods/esrrf_3d/run.py +++ /dev/null @@ -1,41 +0,0 @@ -from ...core.transform._le_esrrf3d import eSRRF3D -from ...core.transform.sr_temporal_correlations import ( - calculate_eSRRF3d_temporal_correlations, -) - - -def run_esrrf3d( - img, - mode="average", - magnification_xy=2, - magnification_z=2, - radius=1.5, - sensitivity=1, - voxel_ratio=4, - doIntensityWeighting=True, - **kwargs, -): - """ - Calculate the eSRRF3D temporal correlations for the given 3D image. - - img (ndarray): The input 4D image. - mode (str, optional): The mode of time projection, default is "average", other option is "std". - magnification_xy (float, optional): The magnification factor for the x and y axes. Default is 2. - magnification_z (float, optional): The magnification factor for the z axis. Default is 2. - radius (float, optional): The radius for the xy plane. Default is 1.5. - sensitivity (float, optional): The sensitivity for the calculation. Default is 1. - voxel_ratio (float, optional): The ratio of voxel dimensions (z to xy). Default is 4. - doIntensityWeighting (bool, optional): Whether to perform intensity weighting. Default is True. - **kwargs: Additional parameters for the eSRRF3D calculation, including: - - radius_z (float, optional): The radius for the z axis. - - run_type (str, optional): The type of the run. - - keep_gradients (bool, optional): Whether to keep the gradients. - - keep_interpolated (bool, optional): Whether to keep the interpolated values. - - correlation (str, optional): The type of correlation to use. - - framewindow (int, optional): The window size for frames. - - rollingoverlap (int, optional): The overlap size for rolling. - - ndarray: The calculated eSRRF3D temporal correlations. - """ - esrrf_calculator = eSRRF3D() - return esrrf_calculator.run(img, **kwargs) From 742cdb5308e3782268b8ddf0bbc3aa116b69b426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 6 May 2025 16:52:00 +0100 Subject: [PATCH 25/82] Added a eSRRF3D workflow function --- src/nanopyx/methods/__init__.py | 1 + .../methods/esrrf_3d/eSRRF3D_workflow.py | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py diff --git a/src/nanopyx/methods/__init__.py b/src/nanopyx/methods/__init__.py index fe53a7f6..df619b24 100644 --- a/src/nanopyx/methods/__init__.py +++ b/src/nanopyx/methods/__init__.py @@ -3,6 +3,7 @@ from .restoration import non_local_means_denoising from .esrrf.eSRRF_workflow import eSRRF +from .esrrf_3d.eSRRF3D_workflow import eSRRF3D from .esrrf import run_esrrf_parameter_sweep from .srrf.SRRF_workflow import SRRF from .squirrel import calculate_frc, calculate_decorr_analysis, calculate_error_map diff --git a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py new file mode 100644 index 00000000..07ac66d0 --- /dev/null +++ b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py @@ -0,0 +1,41 @@ +from ..workflow import Workflow +from ...core.transform._le_esrrf3d import eSRRF3D as eSRRF3D_ST + +import numpy as np + + +def eSRRF3D( + img, + magnification_xy = 2, + magnification_z = 2, + radius: float = 1.5, + radius_z: float = 0.5, + voxel_ratio: float = 4.0, + sensitivity: float = 1, + mode: str = "average", + doIntensityWeighting: bool = True, + _force_run_type=None): + + """ + #TODO + """ + + _eSRRF3D = Workflow( + ( + eSRRF3D_ST(verbose=False), + (img,), + { + "magnification_xy": magnification_xy, + "magnification_z": magnification_z, + "radius": radius, + "radius_z": radius_z, + "voxel_ratio": voxel_ratio, + "sensitivity": sensitivity, + "mode": mode, + "doIntensityWeighting": doIntensityWeighting, + }, + ) + ) + + return _eSRRF3D.calculate(_force_run_type=_force_run_type) + From f3f5d4ddb312dbac3bbce6ec926bc0dfe84a8dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 6 May 2025 16:52:17 +0100 Subject: [PATCH 26/82] Added eSRR3D to init of core.transform --- src/nanopyx/core/transform/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nanopyx/core/transform/__init__.py b/src/nanopyx/core/transform/__init__.py index f23a0806..bd34be2d 100644 --- a/src/nanopyx/core/transform/__init__.py +++ b/src/nanopyx/core/transform/__init__.py @@ -16,6 +16,7 @@ from ._le_radial_gradient_convergence import RadialGradientConvergence from ._le_roberts_cross_gradients import GradientRobertsCross from ._le_esrrf import eSRRF as eSRRF_ST +from ._le_esrrf3d import eSRRF3D as eSRRF3D_ST from ._le_convolution import Convolution as Convolution2D from ._le_nlm_denoising import NLMDenoising From fc35a44e7a28aaf3de4ef1937ca4c0d68e839937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 6 May 2025 16:52:31 +0100 Subject: [PATCH 27/82] Started adding esrrf3d unit tests --- tests/test_benchmarking.py | 3 ++ tests/test_esrrf3d.py | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/test_esrrf3d.py diff --git a/tests/test_benchmarking.py b/tests/test_benchmarking.py index c5a63013..3afe3bd3 100644 --- a/tests/test_benchmarking.py +++ b/tests/test_benchmarking.py @@ -45,6 +45,9 @@ def test_benchmark_esrrf(random_image_with_ramp): esrrf = nanopyx.core.transform._le_esrrf.eSRRF() esrrf.benchmark(random_image_with_ramp) +def test_benchmark_esrrf3d(): + esrrf = nanopyx.core.transform._le_esrrf3d.eSRRF3D() + esrrf.benchmark(np.random.random((1,10,10,10)).astype(np.float32)) def test_benchmark_nlm(random_image_with_ramp): nlm = nanopyx.core.transform._le_nlm_denoising.NLMDenoising() diff --git a/tests/test_esrrf3d.py b/tests/test_esrrf3d.py new file mode 100644 index 00000000..c2170ff1 --- /dev/null +++ b/tests/test_esrrf3d.py @@ -0,0 +1,63 @@ +import numpy as np +from nanopyx.methods import eSRRF3D as eSRRF3D_w +from nanopyx.core.transform import eSRRF3D_ST + +def test_esrrf3d_workflow(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) + small_dataset = dataset[:10,:20,:20] + + dataset = dataset[np.newaxis, ...] + + eSRRF3D_w(small_dataset) + +def test_esrrf3d_mag11(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) + small_dataset = dataset[:10,:20,:20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d.benchmark(small_dataset, magnification_xy=1, magnification_z=1) + +def test_esrrf3d_mag12(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) + small_dataset = dataset[:10,:20,:20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d.benchmark(small_dataset, magnification_xy=1, magnification_z=2) + +def test_esrrf3d_mag21(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) + small_dataset = dataset[:10,:20,:20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d.benchmark(small_dataset, magnification_xy=2, magnification_z=1) + + +def test_esrrf3d_mag22(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) + small_dataset = dataset[:10,:20,:20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d.benchmark(small_dataset, magnification_xy=2, magnification_z=2) + From d0ea7556d4a80c38a276311d70890a61122b12f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Wed, 7 May 2025 12:30:16 +0100 Subject: [PATCH 28/82] Overloading compare_runs on esrrf3D --- .../nanopyx.core.transform._le_esrrf3d.pyx | 25 +++++++++++++++++++ src/nanopyx/core/transform/_le_esrrf3d.pyx | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 74f996e5..5c6fb987 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -17,6 +17,8 @@ from ._le_interpolation_catmull_rom import ShiftAndMagnify from ...__liquid_engine__ import LiquidEngine from ...__opencl__ import cl, cl_array, _fastest_device +from scipy.stats import pearsonr as pearson_correlation + cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil @@ -360,3 +362,26 @@ class eSRRF3D(LiquidEngine): return np.asarray(np.sqrt(output_array / image.shape[0])) else: return np.asarray(output_array) + + + def _compare_runs(self, output_1, output_2): + """@public""" + if output_1.ndim > 2: + pcc = 0 + count = 0 + for i in range(output_1.shape[0]): + pccresult = pearson_correlation(output_1[i, :, :].flatten(), output_2[i, :, :].flatten()).statistic + + if np.isnan(pccresult): + continue + else: + count += 1 + # calculate pcc for each frame + pcc += (pccresult-pcc) / count + else: + pcc = pearson_correlation(output_1.flatten(), output_2.flatten()).statistic + + if pcc > 0.8: + return True + else: + return False \ No newline at end of file diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index f9ee80b4..f9f76c7b 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -15,6 +15,8 @@ from ._le_interpolation_catmull_rom import ShiftAndMagnify from ...__liquid_engine__ import LiquidEngine from ...__opencl__ import cl, cl_array, _fastest_device +from scipy.stats import pearsonr as pearson_correlation + cdef extern from "_c_gradients.h": void _c_gradient_3d(float* image, float* imGc, float* imGr, float* imGs, int slices, int rows, int cols) nogil @@ -655,3 +657,26 @@ class eSRRF3D(LiquidEngine): return np.asarray(np.sqrt(output_array / image.shape[0])) else: return np.asarray(output_array) + + + def _compare_runs(self, output_1, output_2): + """@public""" + if output_1.ndim > 2: + pcc = 0 + count = 0 + for i in range(output_1.shape[0]): + pccresult = pearson_correlation(output_1[i, :, :].flatten(), output_2[i, :, :].flatten()).statistic + + if np.isnan(pccresult): + continue + else: + count += 1 + # calculate pcc for each frame + pcc += (pccresult-pcc) / count + else: + pcc = pearson_correlation(output_1.flatten(), output_2.flatten()).statistic + + if pcc > 0.8: + return True + else: + return False \ No newline at end of file From f071869ae10beb17948e3cfa8c52297848abef23 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Wed, 7 May 2025 16:00:40 +0100 Subject: [PATCH 29/82] tests update --- .coveragerc | 6 ++++++ tests/test_conv2d.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..e96a482a --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +omit = + */convolution.py + */_transonic.py + */_le_template_simple_.py + */benchmark.py \ No newline at end of file diff --git a/tests/test_conv2d.py b/tests/test_conv2d.py index 145284d3..bcd0d61a 100644 --- a/tests/test_conv2d.py +++ b/tests/test_conv2d.py @@ -2,17 +2,25 @@ from nanopyx.core.transform._le_convolution import Convolution from nanopyx.core.transform.convolution import convolution2D_numba +import nanopyx.core.transform._transonic + + +def test_convolution2D(): + img = np.random.random((10, 100, 100)).astype(np.float32) + kernel = np.random.random((3, 3)).astype(np.float32) + + nanopyx.core.transform._transonic.convolution2D(img, kernel) def test_conv_small(): - small = np.random.random((10,100, 100)).astype(np.float32) + small = np.random.random((10, 100, 100)).astype(np.float32) kernel = np.ones((23, 23)).astype(np.float32) conv = Convolution() conv.benchmark(small, kernel) def test_convolution2D_numba(): - img = np.random.random((10,100, 100)).astype(np.float32) + img = np.random.random((10, 100, 100)).astype(np.float32) kernel = np.random.random((3, 3)).astype(np.float32) convolution2D_numba(img, kernel) @@ -23,4 +31,3 @@ def test_conv_small_2d(): kernel = np.ones((23, 23)).astype(np.float32) conv = Convolution() conv.benchmark(small, kernel) - From 54069537a5b1fe355b784cf41125addef6d3f0e2 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Wed, 7 May 2025 16:00:46 +0100 Subject: [PATCH 30/82] formatting --- src/nanopyx/core/transform/_transonic.py | 13 ++-- src/nanopyx/core/utils/benchmark.py | 75 ++++++++++++++++++------ 2 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/nanopyx/core/transform/_transonic.py b/src/nanopyx/core/transform/_transonic.py index 96212ed5..d7f4c373 100644 --- a/src/nanopyx/core/transform/_transonic.py +++ b/src/nanopyx/core/transform/_transonic.py @@ -11,8 +11,8 @@ def convolution2D(image, kernel): nRows_kernel = kernel.shape[0] nCols_kernel = kernel.shape[1] - center_r = (nRows_kernel-1) // 2 - center_c = (nCols_kernel-1) // 2 + center_r = (nRows_kernel - 1) // 2 + center_c = (nCols_kernel - 1) // 2 acc = 0.0 @@ -24,9 +24,12 @@ def convolution2D(image, kernel): acc = 0 for kr in range(nRows_kernel): for kc in range(nCols_kernel): - local_row = min(max(r+(kr-center_r), 0), nRows-1) - local_col = min(max(c+(kc-center_c), 0), nCols-1) - acc = acc + kernel[kr, kc] * image[f, local_row, local_col] + local_row = min(max(r + (kr - center_r), 0), nRows - 1) + local_col = min(max(c + (kc - center_c), 0), nCols - 1) + acc = ( + acc + + kernel[kr, kc] * image[f, local_row, local_col] + ) conv_out[f, r, c] = acc return conv_out diff --git a/src/nanopyx/core/utils/benchmark.py b/src/nanopyx/core/utils/benchmark.py index a19a3af7..dcfce019 100644 --- a/src/nanopyx/core/utils/benchmark.py +++ b/src/nanopyx/core/utils/benchmark.py @@ -2,10 +2,19 @@ import nanopyx import numpy as np -from ..generate.beads import generate_channel_misalignment, generate_timelapse_drift +from ..generate.beads import ( + generate_channel_misalignment, + generate_timelapse_drift, +) + def benchmark_all_le_methods( - n_benchmark_runs=3, img_dims=100, shift=1, magnification=2, rotation=math.radians(15), conv_kernel_dims=5 + n_benchmark_runs=3, + img_dims=100, + shift=1, + magnification=2, + rotation=math.radians(15), + conv_kernel_dims=5, ): """ Runs benchmark tests for all LE methods. @@ -21,24 +30,44 @@ def benchmark_all_le_methods( """ img = np.random.random((img_dims, img_dims)).astype(np.float32) - img_int = np.random.random((img_dims * magnification, img_dims * magnification)).astype(np.float32) + img_int = np.random.random( + (img_dims * magnification, img_dims * magnification) + ).astype(np.float32) kernel = np.ones((conv_kernel_dims, conv_kernel_dims)).astype(np.float32) - bicubic_sm = nanopyx.core.transform._le_interpolation_bicubic.ShiftAndMagnify() - bicubic_ssr = nanopyx.core.transform._le_interpolation_bicubic.ShiftScaleRotate() - cr_sm = nanopyx.core.transform._le_interpolation_catmull_rom.ShiftAndMagnify() - cr_ssr = nanopyx.core.transform._le_interpolation_catmull_rom.ShiftScaleRotate() + bicubic_sm = ( + nanopyx.core.transform._le_interpolation_bicubic.ShiftAndMagnify() + ) + bicubic_ssr = ( + nanopyx.core.transform._le_interpolation_bicubic.ShiftScaleRotate() + ) + cr_sm = ( + nanopyx.core.transform._le_interpolation_catmull_rom.ShiftAndMagnify() + ) + cr_ssr = ( + nanopyx.core.transform._le_interpolation_catmull_rom.ShiftScaleRotate() + ) l_sm = nanopyx.core.transform._le_interpolation_lanczos.ShiftAndMagnify() l_ssr = nanopyx.core.transform._le_interpolation_lanczos.ShiftScaleRotate() - nn_sm = nanopyx.core.transform._le_interpolation_nearest_neighbor.ShiftAndMagnify() - nn_ssr = nanopyx.core.transform._le_interpolation_nearest_neighbor.ShiftScaleRotate() - nn_pt = nanopyx.core.transform._le_interpolation_nearest_neighbor.PolarTransform() + nn_sm = ( + nanopyx.core.transform._le_interpolation_nearest_neighbor.ShiftAndMagnify() + ) + nn_ssr = ( + nanopyx.core.transform._le_interpolation_nearest_neighbor.ShiftScaleRotate() + ) + nn_pt = ( + nanopyx.core.transform._le_interpolation_nearest_neighbor.PolarTransform() + ) conv2d = nanopyx.core.transform._le_convolution.Convolution() rad = nanopyx.core.transform._le_radiality.Radiality() - rc = nanopyx.core.transform._le_roberts_cross_gradients.GradientRobertsCross() - rgc = nanopyx.core.transform._le_radial_gradient_convergence.RadialGradientConvergence() + rc = ( + nanopyx.core.transform._le_roberts_cross_gradients.GradientRobertsCross() + ) + rgc = ( + nanopyx.core.transform._le_radial_gradient_convergence.RadialGradientConvergence() + ) esrrf = nanopyx.core.transform._le_esrrf.eSRRF() esrrf3d = nanopyx.core.transform._le_esrrf3d.eSRRF3D() @@ -55,13 +84,21 @@ def benchmark_all_le_methods( nn_sm.benchmark(img, shift, shift, magnification, magnification) for i in range(n_benchmark_runs): - bicubic_ssr.benchmark(img, shift, shift, magnification, magnification, rotation) + bicubic_ssr.benchmark( + img, shift, shift, magnification, magnification, rotation + ) for i in range(n_benchmark_runs): - cr_ssr.benchmark(img, shift, shift, magnification, magnification, rotation) + cr_ssr.benchmark( + img, shift, shift, magnification, magnification, rotation + ) for i in range(n_benchmark_runs): - l_ssr.benchmark(img, shift, shift, magnification, magnification, rotation) + l_ssr.benchmark( + img, shift, shift, magnification, magnification, rotation + ) for i in range(n_benchmark_runs): - nn_ssr.benchmark(img, shift, shift, magnification, magnification, rotation) + nn_ssr.benchmark( + img, shift, shift, magnification, magnification, rotation + ) for i in range(n_benchmark_runs): nn_pt.benchmark(img, (img_dims, img_dims), "log") @@ -85,7 +122,9 @@ def benchmark_all_le_methods( for i in range(n_benchmark_runs): nlm.benchmark(img) - channel_reg = nanopyx.core.analysis._le_channel_registration.ChannelRegistrationEstimator() + channel_reg = ( + nanopyx.core.analysis._le_channel_registration.ChannelRegistrationEstimator() + ) drift_reg = nanopyx.core.analysis._le_drift_calculator.DriftEstimator() @@ -96,4 +135,4 @@ def benchmark_all_le_methods( channel_reg.benchmark(channels_img, 0, 10, 3, 0.5) for i in range(n_benchmark_runs): - drift_reg.benchmark(drift_img) \ No newline at end of file + drift_reg.benchmark(drift_img) From 81838c6849a977ea906b4ffdc0cbd47f132e33a1 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 8 May 2025 11:43:08 +0100 Subject: [PATCH 31/82] added deprecation warning to easy_gui --- src/nanopyx/core/utils/easy_gui.py | 65 +++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/nanopyx/core/utils/easy_gui.py b/src/nanopyx/core/utils/easy_gui.py index 9c9e739d..70bba1be 100644 --- a/src/nanopyx/core/utils/easy_gui.py +++ b/src/nanopyx/core/utils/easy_gui.py @@ -8,6 +8,7 @@ import numpy as np from ipyfilechooser import FileChooser from skimage.exposure import rescale_intensity +import warnings # import cache if python >= 3.9, otherwise import lru_cache if platform.python_version_tuple() >= ("3", "9"): @@ -15,13 +16,21 @@ else: from functools import lru_cache as cache +warnings.warn( + "The EasyGui class is deprecated.\nConsider using ezinput (the updated version of EasyGui) instead.\nYou can install ezinput with 'pip install ezinput'.\nThe API is similar to EasyGui, but with additional features and improvements.\nIt also allows to run the same widget code in both Jupyter Notebooks and on the terminal.\nFor more information: https://github.com/henriqueslab/ezinput", + DeprecationWarning, + stacklevel=2, +) + try: import ipywidgets as widgets from IPython import display as dp from IPython.display import display, clear_output from matplotlib import pyplot as plt except ImportError: - print("jupyter optional-dependencies not installed, conside installing with 'pip install nanopyx[jupyter]'") + print( + "jupyter optional-dependencies not installed, conside installing with 'pip install nanopyx[jupyter]'" + ) raise ImportError @@ -102,7 +111,9 @@ def add_label(self, *args, **kwargs): :param kwargs: kwargs for the widget """ self._nLabels += 1 - self._widgets[f"label_{self._nLabels}"] = widgets.Label(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[f"label_{self._nLabels}"] = widgets.Label( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_button(self, tag, *args, **kwargs): """ @@ -111,7 +122,9 @@ def add_button(self, tag, *args, **kwargs): :param args: args for the widget :param kwargs: kwargs for the widget """ - self._widgets[tag] = widgets.Button(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.Button( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_text(self, tag, *args, **kwargs): """ @@ -120,7 +133,9 @@ def add_text(self, tag, *args, **kwargs): :param args: args for the widget :param kwargs: kwargs for the widget """ - self._widgets[tag] = widgets.Text(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.Text( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_int_slider(self, tag, *args, remember_value=False, **kwargs): """ @@ -130,9 +145,15 @@ def add_int_slider(self, tag, *args, remember_value=False, **kwargs): :param remember_value: remember the last value :param kwargs: kwargs for the widget """ - if remember_value and tag in self.cfg and kwargs["min"] <= self.cfg[tag] <= kwargs["max"]: + if ( + remember_value + and tag in self.cfg + and kwargs["min"] <= self.cfg[tag] <= kwargs["max"] + ): kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.IntSlider(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.IntSlider( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_float_slider(self, tag, *args, remember_value=False, **kwargs): """ @@ -144,7 +165,9 @@ def add_float_slider(self, tag, *args, remember_value=False, **kwargs): """ if remember_value and tag in self.cfg: kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.FloatSlider(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.FloatSlider( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_checkbox(self, tag, *args, remember_value=False, **kwargs): """ @@ -156,7 +179,9 @@ def add_checkbox(self, tag, *args, remember_value=False, **kwargs): """ if remember_value and tag in self.cfg: kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.Checkbox(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.Checkbox( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_int_text(self, tag, *args, remember_value=False, **kwargs): """ @@ -169,7 +194,9 @@ def add_int_text(self, tag, *args, remember_value=False, **kwargs): if remember_value and tag in self.cfg: kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.IntText(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.IntText( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_float_text(self, tag, *args, remember_value=False, **kwargs): """ @@ -181,7 +208,9 @@ def add_float_text(self, tag, *args, remember_value=False, **kwargs): """ if remember_value and tag in self.cfg: kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.FloatText(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.FloatText( + *args, **kwargs, layout=self._layout, style=self._style + ) def add_dropdown(self, tag, *args, remember_value=False, **kwargs): """ @@ -191,11 +220,19 @@ def add_dropdown(self, tag, *args, remember_value=False, **kwargs): :param remember_value: remember the last value :param kwargs: kwargs for the widget """ - if remember_value and tag in self.cfg and self.cfg[tag] in kwargs["options"]: + if ( + remember_value + and tag in self.cfg + and self.cfg[tag] in kwargs["options"] + ): kwargs["value"] = self.cfg[tag] - self._widgets[tag] = widgets.Dropdown(*args, **kwargs, layout=self._layout, style=self._style) + self._widgets[tag] = widgets.Dropdown( + *args, **kwargs, layout=self._layout, style=self._style + ) - def add_file_upload(self, tag, *args, accept=None, multiple=False, **kwargs): + def add_file_upload( + self, tag, *args, accept=None, multiple=False, **kwargs + ): """ Add a file upload widget to the container. :param tag: tag to identify the widget @@ -224,7 +261,7 @@ def show(self): Show the widgets in the container. """ # display the widgets - self._main_display.children = (tuple(self._widgets.values())) + self._main_display.children = tuple(self._widgets.values()) clear_output() display(self._main_display) From 42fc64646b81cf3d41285a3feb4745c4544777c4 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 8 May 2025 13:54:02 +0100 Subject: [PATCH 32/82] bugfixing --- src/nanopyx/core/transform/_le_esrrf3d_.cl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 31a2bf96..203e674b 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -280,7 +280,7 @@ __kernel void calculate_rgc3D(__global float* imIntGz, __global float* imIntGy, s = s + fwhm_z*2*magnification_z; if (doIntensityWeighting == 1) { - tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[f * nPixels_out + s * rowsM * colsM + row * colsM + col]; + tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[s * rowsM * colsM + row * colsM + col]; } else { tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity); } From d346cfd22b267a7dfb6e0c0e3a5b91e4378324c9 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 10:59:12 +0100 Subject: [PATCH 33/82] added eSRRF3D as high level import --- src/nanopyx/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nanopyx/__init__.py b/src/nanopyx/__init__.py index 49baf1ca..77035398 100644 --- a/src/nanopyx/__init__.py +++ b/src/nanopyx/__init__.py @@ -17,7 +17,6 @@ from .__agent__ import Agent # noqa: E402 - # TODO: allow benchmarking of only specific implementations # TODO: provide parallelized batch processing @@ -42,7 +41,7 @@ # Section for imports of high-level functions from .core.utils.benchmark import benchmark_all_le_methods as benchmark from .methods import non_local_means_denoising -from .methods import eSRRF, run_esrrf_parameter_sweep +from .methods import eSRRF, run_esrrf_parameter_sweep, eSRRF3D from .methods import SRRF from .methods import calculate_frc, calculate_decorr_analysis from .methods import calculate_error_map From 5f3d14f4fd2e3e533b4152cdf0a79c6f0526e436 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 13:04:18 +0100 Subject: [PATCH 34/82] changed mac os to use nox --- .github/workflows/nanopyx_night_mechanic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index 5f24136a..2f7aa6c5 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -107,7 +107,7 @@ jobs: env: LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels - run: python -m cibuildwheel --output-dir wheelhouse + run: python -m nox --sessions test_source clear_wheelhouse build_wheel test_wheel # to supply options, put them in 'env', like: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 From 70ad99b50420655f7663e470d19274afd90decf8 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 13:08:06 +0100 Subject: [PATCH 35/82] actions updates --- .github/workflows/build_wheels.yml | 114 +++++++++++++++++++ .github/workflows/nanopyx_night_mechanic.yml | 5 +- 2 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/build_wheels.yml diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml new file mode 100644 index 00000000..74789645 --- /dev/null +++ b/.github/workflows/build_wheels.yml @@ -0,0 +1,114 @@ +name: Test - Nightly +# does a massive check, every night + +on: + schedule: + - cron: "0 23 * * *" + + # creates a button + workflow_dispatch: + inputs: + logLevel: + description: "Log level" + required: true + default: "warning" + type: choice + options: + - info + - warning + - debug + +jobs: + + test_on_ubuntu_container: + runs-on: [self-hosted, Ubuntu, Native] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: | + 3.9 + 3.10 + 3.11 + 3.12 + 3.13 + - name: Run Nox Quick-Tests + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade nox + python -m pip install cibuildwheel==2.16.2 + python -m nox --sessions clear_wheelhouse build_wheel build_sdist test_wheel + env: + LOG_LEVEL: ${{ github.event.inputs.logLevel }} + - name: Upload Coverage + uses: codecov/codecov-action@v3 + with: + files: coverage.xml # optional + flags: pytests # optional + fail_ci_if_error: false # optional (default = false) + verbose: true # optional (default = false) + + test_on_windows: + runs-on: [self-hosted, Windows, X64] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: | + 3.9 + 3.10 + 3.11 + 3.12 + 3.13 + - name: Run Nox Quick-Tests + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade nox + python -m pip install cibuildwheel==2.16.2 + python -m pip install setuptools==73.0.1 + python -m nox --sessions clear_wheelhouse build_wheel build_sdist test_wheel + env: + LOG_LEVEL: ${{ github.event.inputs.logLevel }} + - name: Upload Coverage + uses: codecov/codecov-action@v3 + with: + files: coverage.xml # optional + flags: pytests # optional + fail_ci_if_error: false # optional (default = false) + verbose: true # optional (default = false) + + test_on_macos: + runs-on: [self-hosted, macOS, ARM64] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: | + 3.9 + 3.10 + 3.11 + 3.12 + 3.13 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade nox + python -m pip install cibuildwheel==2.16.2 + python -m pip install setuptools==73.0.1 + - name: Run Nox Quick-Tests + run: | + python -m nox --sessions test_source clear_wheelhouse + env: + LOG_LEVEL: ${{ github.event.inputs.logLevel }} + - name: Build wheels + run: python -m nox --sessions clear_wheelhouse build_wheel test_wheel + # to supply options, put them in 'env', like: + env: + CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 + CIBW_ARCHS: arm64 diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index 2f7aa6c5..8771aa3c 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -111,7 +111,4 @@ jobs: # to supply options, put them in 'env', like: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 - CIBW_ARCHS: arm64 - - - name: Test wheels - run: python -m nox --sessions test_wheel \ No newline at end of file + CIBW_ARCHS: arm64 \ No newline at end of file From eb8856cb01f63ef01646ab26c764223d674784a7 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 13:11:02 +0100 Subject: [PATCH 36/82] name update --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 74789645..ff8c2340 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -1,4 +1,4 @@ -name: Test - Nightly +name: (self-hosted) Build wheels # does a massive check, every night on: From 6026eb9b36fbbd663ee3ae0dd708481d5b0473aa Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 13:14:59 +0100 Subject: [PATCH 37/82] trigger updates --- .github/workflows/build_wheels.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index ff8c2340..932f275c 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -1,11 +1,13 @@ name: (self-hosted) Build wheels -# does a massive check, every night on: + push: + branches: + - updates # Only run on commits to the 'updates' branch + schedule: - cron: "0 23 * * *" - # creates a button workflow_dispatch: inputs: logLevel: @@ -18,6 +20,7 @@ on: - warning - debug + jobs: test_on_ubuntu_container: From b0f762ad187de2d4bcdefb3aa57384bc6c0278f1 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 13:25:58 +0100 Subject: [PATCH 38/82] removed source testing --- .github/workflows/build_wheels.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 932f275c..15f286d5 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -104,11 +104,6 @@ jobs: python -m pip install --upgrade nox python -m pip install cibuildwheel==2.16.2 python -m pip install setuptools==73.0.1 - - name: Run Nox Quick-Tests - run: | - python -m nox --sessions test_source clear_wheelhouse - env: - LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels run: python -m nox --sessions clear_wheelhouse build_wheel test_wheel # to supply options, put them in 'env', like: From 705c50274738e1dbd664ca09ccc4141f820a2ab9 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 15:00:30 +0100 Subject: [PATCH 39/82] fix for nox wheel building on mac --- noxfile.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/noxfile.py b/noxfile.py index 32f433bb..371e177e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,21 +30,29 @@ @nox.session(python=PYTHON_ALL_VERSIONS) def build_wheel(session: nox.Session) -> None: """ - Build a wheel + Build a wheel for ARM64 on macOS or AMD64 on Linux/Windows """ - # if PLATFORM == "macos": # build libomp from source, better ARM compatibility - # path = Path(os.path.dirname(__file__)) / "build_tools" / "libs_build" - # if not path: # did we already build libomp? - # session.run("bash", "build_tools/build_libomp.sh") + import platform + + arch = platform.machine() + is_macos = sys.platform == "darwin" + is_linux = sys.platform.startswith("linux") + is_windows = sys.platform == "win32" + + # Skip if not on the right arch/platform combination + if is_macos and arch != "arm64": + session.skip(f"Skipping macOS build on non-arm64 architecture (found {arch})") + elif (is_linux or is_windows) and arch != "x86_64": + session.skip(f"Skipping {PLATFORM} build on non-x86_64 architecture (found {arch})") session.install("build") temp_path = session.create_tmp() - # session.run("python", "-m", "build", "--wheel", "-o", temp_path) session.run("pip", "wheel", "--no-deps", "--wheel-dir", temp_path, ".") - # get the produced wheel name + + # Get wheel name wheel_name = [name for name in os.listdir(temp_path) if name.endswith(".whl")][0] - if PLATFORM == "unix" and os.environ.get("NPX_LINUX_FIX_WHEELS", False): + if is_linux and os.environ.get("NPX_LINUX_FIX_WHEELS", False): session.install("auditwheel") session.run( "auditwheel", @@ -53,8 +61,7 @@ def build_wheel(session: nox.Session) -> None: "-w", DIR / "wheelhouse", ) - - elif PLATFORM == "macos": + elif is_macos: session.install("delocate==0.10.4") session.run( "delocate-wheel", @@ -63,8 +70,6 @@ def build_wheel(session: nox.Session) -> None: "-w", DIR / "wheelhouse", ) - pass - else: os.makedirs(DIR / "wheelhouse", exist_ok=True) for file in os.listdir(temp_path): @@ -72,6 +77,7 @@ def build_wheel(session: nox.Session) -> None: shutil.copy(os.path.join(temp_path, file), DIR / "wheelhouse") + @nox.session(python=PYTHON_DEFAULT_VERSION) def build_sdist(session: nox.Session) -> None: """ From bc6031f4d502c169697c3686b5f34c444d04937c Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 15:04:49 +0100 Subject: [PATCH 40/82] fix for windows --- noxfile.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/noxfile.py b/noxfile.py index 371e177e..0524a01a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,16 +41,22 @@ def build_wheel(session: nox.Session) -> None: # Skip if not on the right arch/platform combination if is_macos and arch != "arm64": - session.skip(f"Skipping macOS build on non-arm64 architecture (found {arch})") - elif (is_linux or is_windows) and arch != "x86_64": - session.skip(f"Skipping {PLATFORM} build on non-x86_64 architecture (found {arch})") + session.skip( + f"Skipping macOS build on non-arm64 architecture (found {arch})" + ) + elif (is_linux or is_windows) and arch != "x86_64" and arch != "AMD64": + session.skip( + f"Skipping {PLATFORM} build on non-x86_64 architecture (found {arch})" + ) session.install("build") temp_path = session.create_tmp() session.run("pip", "wheel", "--no-deps", "--wheel-dir", temp_path, ".") # Get wheel name - wheel_name = [name for name in os.listdir(temp_path) if name.endswith(".whl")][0] + wheel_name = [ + name for name in os.listdir(temp_path) if name.endswith(".whl") + ][0] if is_linux and os.environ.get("NPX_LINUX_FIX_WHEELS", False): session.install("auditwheel") @@ -77,7 +83,6 @@ def build_wheel(session: nox.Session) -> None: shutil.copy(os.path.join(temp_path, file), DIR / "wheelhouse") - @nox.session(python=PYTHON_DEFAULT_VERSION) def build_sdist(session: nox.Session) -> None: """ @@ -118,12 +123,16 @@ def test_wheel(session): python_version_str = f"cp{session.python.replace('.', '')}" # find the latest wheel wheel_names = [ - wheel for wheel in os.listdir("wheelhouse") if wheel.endswith(".whl") and python_version_str in wheel + wheel + for wheel in os.listdir("wheelhouse") + if wheel.endswith(".whl") and python_version_str in wheel ] wheel_names.sort() wheel_name = wheel_names[-1] - session.run("pip", "install", "-U", DIR / "wheelhouse" / f"{wheel_name}[test]") + session.run( + "pip", "install", "-U", DIR / "wheelhouse" / f"{wheel_name}[test]" + ) with session.chdir(".nox"): extra_args = os.environ.get("NPX_PYTEST_ARGS", "") if extra_args != "": @@ -135,7 +144,14 @@ def test_wheel(session): @nox.session(python=PYTHON_ALL_VERSIONS) def test_testpypi(session): - session.run("pip", "install", "-U", "--extra-index-url", "https://testpypi.python.org/pypi", "nanopyx[all]") + session.run( + "pip", + "install", + "-U", + "--extra-index-url", + "https://testpypi.python.org/pypi", + "nanopyx[all]", + ) with session.chdir(".nox"): extra_args = os.environ.get("NPX_PYTEST_ARGS", "") if extra_args != "": From 44f42bb58ebba24a225c5e963189192d20e466a5 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 15:11:32 +0100 Subject: [PATCH 41/82] new fix --- noxfile.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 0524a01a..e63f93ed 100644 --- a/noxfile.py +++ b/noxfile.py @@ -69,10 +69,37 @@ def build_wheel(session: nox.Session) -> None: ) elif is_macos: session.install("delocate==0.10.4") + + # Remove per-architecture x86_64 dylibs from the wheel + import zipfile + import tempfile + + wheel_path = os.path.join(temp_path, wheel_name) + with tempfile.TemporaryDirectory() as tmpdir: + tmp_whl_dir = Path(tmpdir) / "wheel" + tmp_whl_dir.mkdir() + + # Unzip wheel + with zipfile.ZipFile(wheel_path, "r") as zip_ref: + zip_ref.extractall(tmp_whl_dir) + + # Remove the problematic metadata file + for path in tmp_whl_dir.rglob("*.dylibs"): + if "x86_64" in path.name: + path.unlink() + + # Re-zip + new_wheel_path = Path(temp_path) / f"cleaned-{wheel_name}" + shutil.make_archive( + str(new_wheel_path).removesuffix(".whl"), "zip", tmp_whl_dir + ) + new_wheel_path.rename(Path(new_wheel_path).with_suffix(".whl")) + + # Now safely run delocate session.run( "delocate-wheel", "-v", - os.path.join(temp_path, wheel_name), + str(Path(temp_path) / new_wheel_path.name), "-w", DIR / "wheelhouse", ) From a4709f386030f9deba3ac7a76a8b1e23f34b3589 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 12 May 2025 15:16:38 +0100 Subject: [PATCH 42/82] anoda wane --- noxfile.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/noxfile.py b/noxfile.py index e63f93ed..324ef150 100644 --- a/noxfile.py +++ b/noxfile.py @@ -83,23 +83,24 @@ def build_wheel(session: nox.Session) -> None: with zipfile.ZipFile(wheel_path, "r") as zip_ref: zip_ref.extractall(tmp_whl_dir) - # Remove the problematic metadata file - for path in tmp_whl_dir.rglob("*.dylibs"): + # Remove any x86_64-specific .dylib files (adjust this if needed) + for path in tmp_whl_dir.rglob("*.dylib"): if "x86_64" in path.name: path.unlink() - # Re-zip - new_wheel_path = Path(temp_path) / f"cleaned-{wheel_name}" - shutil.make_archive( - str(new_wheel_path).removesuffix(".whl"), "zip", tmp_whl_dir + # Re-zip as a wheel + cleaned_base = Path(temp_path) / f"cleaned-{wheel_name}" + zip_path = shutil.make_archive( + str(cleaned_base).removesuffix(".whl"), "zip", tmp_whl_dir ) - new_wheel_path.rename(Path(new_wheel_path).with_suffix(".whl")) + fixed_wheel_path = Path(zip_path).with_suffix(".whl") + Path(zip_path).rename(fixed_wheel_path) # Now safely run delocate session.run( "delocate-wheel", "-v", - str(Path(temp_path) / new_wheel_path.name), + str(fixed_wheel_path), "-w", DIR / "wheelhouse", ) From af6ef7fb30bdcac64fa2f2d7599d0656b21b653a Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 10:30:19 +0100 Subject: [PATCH 43/82] added exception handling --- src/nanopyx/core/transform/_le_esrrf3d.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index f9f76c7b..83144bf4 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -34,6 +34,10 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? + if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: + raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + if radius_z * 2 > image.shape[1] / 2: + raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: From 02611f0b66bf26dad6a634cd5fbf98f27136110c Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 10:36:23 +0100 Subject: [PATCH 44/82] added compile esrrf3d file --- src/nanopyx/core/transform/_le_esrrf3d.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 83144bf4..f9f76c7b 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -34,10 +34,6 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? - if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: - raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") - if radius_z * 2 > image.shape[1] / 2: - raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: From 18f8e9c4936c11313e3d838300c8fd9ee5189ab7 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 10:43:19 +0100 Subject: [PATCH 45/82] fixing exceptions --- src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx | 4 ++++ src/nanopyx/core/transform/_le_esrrf3d.pyx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 5c6fb987..2593c43e 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -36,6 +36,10 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? + if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: + raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + if radius_z * 2 > image.shape[1] / 2: + raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index f9f76c7b..83144bf4 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -34,6 +34,10 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? + if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: + raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + if radius_z * 2 > image.shape[1] / 2: + raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: From c29af9d758de8f4e9bdca64bf656ff9322555814 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 10:50:59 +0100 Subject: [PATCH 46/82] updating actions for mac os wheel building --- .github/workflows/build_wheels.yml | 12 +++- .github/workflows/nanopyx_night_mechanic.yml | 9 ++- noxfile.py | 63 ++++---------------- 3 files changed, 29 insertions(+), 55 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 15f286d5..0eed40e0 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -102,11 +102,19 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade nox - python -m pip install cibuildwheel==2.16.2 + python -m pip install cibuildwheel python -m pip install setuptools==73.0.1 + - name: Clear Wheels + run: | + python -m nox --sessions clear_wheelhouse + env: + LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels - run: python -m nox --sessions clear_wheelhouse build_wheel test_wheel + run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 CIBW_ARCHS: arm64 + + - name: Test wheels + run: python -m nox --sessions test_wheel diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index 8771aa3c..cf32116f 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -99,7 +99,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade nox - python -m pip install cibuildwheel==2.16.2 + python -m pip install cibuildwheel python -m pip install setuptools==73.0.1 - name: Run Nox Quick-Tests run: | @@ -107,8 +107,11 @@ jobs: env: LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels - run: python -m nox --sessions test_source clear_wheelhouse build_wheel test_wheel + run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 - CIBW_ARCHS: arm64 \ No newline at end of file + CIBW_ARCHS: arm64 + + - name: Test wheels + run: python -m nox --sessions test_wheel \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index 324ef150..084ea27e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,35 +30,23 @@ @nox.session(python=PYTHON_ALL_VERSIONS) def build_wheel(session: nox.Session) -> None: """ - Build a wheel for ARM64 on macOS or AMD64 on Linux/Windows + Build a wheel """ - import platform - - arch = platform.machine() - is_macos = sys.platform == "darwin" - is_linux = sys.platform.startswith("linux") - is_windows = sys.platform == "win32" - - # Skip if not on the right arch/platform combination - if is_macos and arch != "arm64": - session.skip( - f"Skipping macOS build on non-arm64 architecture (found {arch})" - ) - elif (is_linux or is_windows) and arch != "x86_64" and arch != "AMD64": - session.skip( - f"Skipping {PLATFORM} build on non-x86_64 architecture (found {arch})" - ) + # if PLATFORM == "macos": # build libomp from source, better ARM compatibility + # path = Path(os.path.dirname(__file__)) / "build_tools" / "libs_build" + # if not path: # did we already build libomp? + # session.run("bash", "build_tools/build_libomp.sh") session.install("build") temp_path = session.create_tmp() + # session.run("python", "-m", "build", "--wheel", "-o", temp_path) session.run("pip", "wheel", "--no-deps", "--wheel-dir", temp_path, ".") - - # Get wheel name + # get the produced wheel name wheel_name = [ name for name in os.listdir(temp_path) if name.endswith(".whl") ][0] - if is_linux and os.environ.get("NPX_LINUX_FIX_WHEELS", False): + if PLATFORM == "unix" and os.environ.get("NPX_LINUX_FIX_WHEELS", False): session.install("auditwheel") session.run( "auditwheel", @@ -67,43 +55,18 @@ def build_wheel(session: nox.Session) -> None: "-w", DIR / "wheelhouse", ) - elif is_macos: - session.install("delocate==0.10.4") - # Remove per-architecture x86_64 dylibs from the wheel - import zipfile - import tempfile - - wheel_path = os.path.join(temp_path, wheel_name) - with tempfile.TemporaryDirectory() as tmpdir: - tmp_whl_dir = Path(tmpdir) / "wheel" - tmp_whl_dir.mkdir() - - # Unzip wheel - with zipfile.ZipFile(wheel_path, "r") as zip_ref: - zip_ref.extractall(tmp_whl_dir) - - # Remove any x86_64-specific .dylib files (adjust this if needed) - for path in tmp_whl_dir.rglob("*.dylib"): - if "x86_64" in path.name: - path.unlink() - - # Re-zip as a wheel - cleaned_base = Path(temp_path) / f"cleaned-{wheel_name}" - zip_path = shutil.make_archive( - str(cleaned_base).removesuffix(".whl"), "zip", tmp_whl_dir - ) - fixed_wheel_path = Path(zip_path).with_suffix(".whl") - Path(zip_path).rename(fixed_wheel_path) - - # Now safely run delocate + elif PLATFORM == "macos": + session.install("delocate==0.10.4") session.run( "delocate-wheel", "-v", - str(fixed_wheel_path), + os.path.join(temp_path, wheel_name), "-w", DIR / "wheelhouse", ) + pass + else: os.makedirs(DIR / "wheelhouse", exist_ok=True) for file in os.listdir(temp_path): From d9d12f59a01d2979d3255f5871e39fc0ab16ed0e Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 11:12:04 +0100 Subject: [PATCH 47/82] updated cibuildwheel version --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 0eed40e0..f95d5475 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -102,7 +102,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade nox - python -m pip install cibuildwheel + python -m pip install cibuildwheel==2.23.3 python -m pip install setuptools==73.0.1 - name: Clear Wheels run: | From 451136c171a25ea8ae6b5990729aa9a85f6bd4b9 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 11:15:17 +0100 Subject: [PATCH 48/82] another try --- .github/workflows/build_wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index f95d5475..2036e0b2 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -104,6 +104,7 @@ jobs: python -m pip install --upgrade nox python -m pip install cibuildwheel==2.23.3 python -m pip install setuptools==73.0.1 + brew install libomp # Ensure libomp is installed via Homebrew - name: Clear Wheels run: | python -m nox --sessions clear_wheelhouse @@ -111,10 +112,9 @@ jobs: LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse - # to supply options, put them in 'env', like: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 CIBW_ARCHS: arm64 - + MACOSX_DEPLOYMENT_TARGET: 11.0 # Set macOS deployment target - name: Test wheels run: python -m nox --sessions test_wheel From ae17ab5a561f45d832f4c4d12a9744de34bcd949 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 11:52:55 +0100 Subject: [PATCH 49/82] tentative fix --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 2036e0b2..516bd441 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -115,6 +115,6 @@ jobs: env: CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 CIBW_ARCHS: arm64 - MACOSX_DEPLOYMENT_TARGET: 11.0 # Set macOS deployment target + MACOSX_DEPLOYMENT_TARGET: 14.0 # Set macOS deployment target - name: Test wheels run: python -m nox --sessions test_wheel From 7d961c53fa1805476188d616b76e8fc174cd7d6e Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 12:21:48 +0100 Subject: [PATCH 50/82] fixing handling of bigger radius than allowed --- .../nanopyx.core.transform._le_esrrf3d.pyx | 20 +++-- src/nanopyx/core/transform/_le_esrrf3d.pyx | 20 +++-- tests/test_esrrf3d.py | 74 ++++++++++++++----- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 2593c43e..ec668e2e 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -36,17 +36,21 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? - if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: - raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + if len(image.shape) == 3: + image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) + if len(image.shape) != 4: + print("Warning:image must either be 3D or 4D. If 3D, it will be reshaped to 4D.") + return None + if radius * 2 > (image.shape[2]) / 2 or radius * 2 > (image.shape[3] / 2): + print("Warning: Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + return None if radius_z * 2 > image.shape[1] / 2: - raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") + print("Warning: Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes.") + return None if image.dtype != np.float32: image = image.astype(np.float32) - if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - elif len(image.shape) == 3: - image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 83144bf4..f8c430c7 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -34,17 +34,21 @@ class eSRRF3D(LiquidEngine): def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? - if radius * 2 > image.shape[2] / 2 or radius * 2 > image.shape[3] / 2: - raise ValueError("Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + if len(image.shape) == 3: + image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) + if len(image.shape) != 4: + print("Warning:image must either be 3D or 4D. If 3D, it will be reshaped to 4D.") + return None + if radius * 2 > (image.shape[2]) / 2 or radius * 2 > (image.shape[3] / 2): + print("Warning: Radius is too big for the image. Half the radius must be smaller than both half the number of columns and half number of rows of the image.") + return None if radius_z * 2 > image.shape[1] / 2: - raise ValueError("Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes") + print("Warning: Radius_z is too big for the image. Half the radius_z must be smaller than half of number of Z planes.") + return None if image.dtype != np.float32: image = image.astype(np.float32) - if len(image.shape) == 4: - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - elif len(image.shape) == 3: - image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) - return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) + + return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: diff --git a/tests/test_esrrf3d.py b/tests/test_esrrf3d.py index c2170ff1..69d46546 100644 --- a/tests/test_esrrf3d.py +++ b/tests/test_esrrf3d.py @@ -2,49 +2,57 @@ from nanopyx.methods import eSRRF3D as eSRRF3D_w from nanopyx.core.transform import eSRRF3D_ST + def test_esrrf3d_workflow(downloader): dataset = downloader.get_ZipTiffIterator( - "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) - small_dataset = dataset[:10,:20,:20] + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] dataset = dataset[np.newaxis, ...] - + eSRRF3D_w(small_dataset) + def test_esrrf3d_mag11(downloader): dataset = downloader.get_ZipTiffIterator( - "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) - small_dataset = dataset[:10,:20,:20] + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] dataset = dataset[np.newaxis, ...] - - e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) e3d.benchmark(small_dataset, magnification_xy=1, magnification_z=1) + def test_esrrf3d_mag12(downloader): dataset = downloader.get_ZipTiffIterator( - "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) - small_dataset = dataset[:10,:20,:20] + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] dataset = dataset[np.newaxis, ...] - - e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) e3d.benchmark(small_dataset, magnification_xy=1, magnification_z=2) + def test_esrrf3d_mag21(downloader): dataset = downloader.get_ZipTiffIterator( - "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) - small_dataset = dataset[:10,:20,:20] + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] dataset = dataset[np.newaxis, ...] - - e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) e3d.benchmark(small_dataset, magnification_xy=2, magnification_z=1) @@ -52,12 +60,40 @@ def test_esrrf3d_mag21(downloader): def test_esrrf3d_mag22(downloader): dataset = downloader.get_ZipTiffIterator( - "SMLMS2013_HDTubulinAlexa647", as_ndarray=True) - small_dataset = dataset[:10,:20,:20] + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] dataset = dataset[np.newaxis, ...] - - e3d = eSRRF3D_ST(testing=True,clear_benchmarks=True) + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) e3d.benchmark(small_dataset, magnification_xy=2, magnification_z=2) + +def test_esrrf3d_radius_too_high(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) + + assert e3d.run(small_dataset, radius=15) is None + + +def test_esrrf3d_radiusz_too_high(downloader): + + dataset = downloader.get_ZipTiffIterator( + "SMLMS2013_HDTubulinAlexa647", as_ndarray=True + ) + small_dataset = dataset[:10, :20, :20] + + dataset = dataset[np.newaxis, ...] + + e3d = eSRRF3D_ST(testing=True, clear_benchmarks=True) + + assert e3d.run(small_dataset, radius_z=15) is None From 5b76dc5f6a7b683d476ed51b401b3d8c4184257d Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 13 May 2025 12:30:49 +0100 Subject: [PATCH 51/82] actions update --- .github/workflows/build_wheels.yml | 7 ------- .github/workflows/nanopyx_night_mechanic.yml | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 516bd441..fd1b3457 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -1,13 +1,6 @@ name: (self-hosted) Build wheels on: - push: - branches: - - updates # Only run on commits to the 'updates' branch - - schedule: - - cron: "0 23 * * *" - workflow_dispatch: inputs: logLevel: diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index cf32116f..42e42b24 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -99,7 +99,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade nox - python -m pip install cibuildwheel + python -m pip install cibuildwheel==2.23.3 python -m pip install setuptools==73.0.1 - name: Run Nox Quick-Tests run: | From 938dd67bd80cd0ba9075dad6f088aab513db2fea Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Wed, 14 May 2025 11:26:31 +0100 Subject: [PATCH 52/82] fixed issues with macropixels --- .../_c_sr_radial_gradient_convergence.c | 18 +++----- src/nanopyx/core/transform/_le_esrrf3d_.cl | 44 ++++++++++++++++--- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index d39353f6..1d86bec5 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -125,9 +125,9 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, distanceWeight_xy, distanceWeight_z, GdotR, Dk; - float xc = (xM) / magnification_xy; - float yc = (yM) / magnification_xy; - float zc = (sliceM) / magnification_z; + float xc = (float)(xM) / magnification_xy; + float yc = (float)(yM) / magnification_xy; + float zc = (float)(sliceM) / magnification_z; float RGC = 0; float distanceWeightSum = 0; @@ -143,15 +143,15 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn for (int j = _start; j <= _end; j++) { vy = yc + j; - if (0 < vy && vy < rowsM/magnification_xy - 1) { + if (0 < vy && vy <= (rowsM/magnification_xy) - 1) { for (int i = _start; i <= _end; i++) { vx = xc + i; - if (0 < vx && vx < colsM/magnification_xy - 1) { + if (0 < vx && vx <= (colsM/magnification_xy) - 1) { for (int k = _start_z; k <= _end_z; k++) { vz = zc + k; - if (0 < vz && vz < slicesM/magnification_z - 1) { + if (0 < vz && vz <= (slicesM/magnification_z) - 1) { dx = vx - xc; dy = vy - yc; dz = vz - zc; @@ -169,15 +169,9 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn Gy = imIntGy[linear_index]; Gz = imIntGz[linear_index]; - // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); - - // distanceWeight_xy = _c_calculate_dw_xy(distance_xy, tSS); - // distanceWeight_z = _c_calculate_dw_z(distance_z, tSS_z); distanceWeightSum += distanceWeight; - // distanceWeightSum_xy += distanceWeight_xy; - // distanceWeightSum_z += distanceWeight_z; GdotR = Gx*dx + Gy*dy + Gz*dz_real; if (GdotR < 0) { diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index 203e674b..c182701a 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -168,6 +168,12 @@ float _c_calculate_dw3D(float distance, float distance_xy, float distance_z,floa return pow(D_weight, 4); } +double _c_calculate_dw_3d(double distance_xy, double distance_z, double tSS, double tSS_z) { + double weight_xy = distance_xy * exp((-distance_xy * distance_xy) / tSS); + double weight_z = distance_z * exp((-distance_z * distance_z) / tSS_z); + return pow((weight_xy * weight_z), 4); +} + float _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { float Dk = sqrt((Gy * dz - Gz * dy) * (Gy * dz - Gz * dy) + (Gz * dx - Gx * dz) * (Gz * dx - Gx * dz) + (Gx * dy - Gy * dx) * (Gx * dy - Gy * dx)) / sqrt(Gx * Gx + Gy * Gy + Gz * Gz); if (isnan(Dk)) { @@ -177,13 +183,38 @@ float _c_calculate_dk3D(float Gx, float Gy, float Gz, float dx, float dy, float return Dk; } +float _c_calculate_dk_3d(float Gx, float Gy, float Gz, float dx, float dy, float dz, float distance) { + // Compute the cross product magnitude in 3D + float cross_magnitude = sqrt( + pow(Gy * dz - Gz * dy, 2) + + pow(Gz * dx - Gx * dz, 2) + + pow(Gx * dy - Gy * dx, 2) + ); + + // Compute the magnitude of the gradient vector + float gradient_magnitude = sqrt(Gx * Gx + Gy * Gy + Gz * Gz); + + // Compute Dk + float Dk = cross_magnitude / gradient_magnitude; + + // Handle NaN case + if (isnan(Dk)) { + Dk = distance; + } + + // Normalize Dk + Dk = 1 - Dk / distance; + + return Dk; +} + float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __global float* imIntGy, __global float* imIntGz, int colsM, int rowsM, int slicesM, int magnification_xy, int magnification_z, float voxel_ratio, float fwhm, float fwhm_z, float tSO, float tSO_z, float tSS, float tSS_z, float sensitivity) { float vx, vy, vz, Gx, Gy, Gz, dx, dy, dz, dz_real, distance, distance_xy, distance_z, distanceWeight, GdotR, Dk; - float xc = (xM) / magnification_xy; - float yc = (yM) / magnification_xy; - float zc = (sliceM) / magnification_z; + float xc = (float)(xM) / magnification_xy; + float yc = (float)(yM) / magnification_xy; + float zc = (float)(sliceM) / magnification_z; float RGC = 0.0f; float distanceWeightSum = 0.0f; @@ -197,15 +228,15 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ for (int j = _start; j <= _end; j++) { vy = yc + j; - if (0 < vy && vy < rowsM/magnification_xy - 1) { + if (0 < vy && vy <= (rowsM/magnification_xy) - 1) { for (int i = _start; i <= _end; i++) { vx = xc + i; - if (0 < vx && vx < colsM/magnification_xy - 1) { + if (0 < vx && vx <= (colsM/magnification_xy) - 1) { for (int k = _start_z; k <= _end_z; k++) { vz = zc + k; - if (0 < vz && vz < slicesM/magnification_z - 1) { + if (0 < vz && vz <= (slicesM/magnification_z) - 1) { dx = vx - xc; dy = vy - yc; dz = vz - zc; @@ -225,6 +256,7 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ // distanceWeight = _c_calculate_dw3D_isotropic(distance, tSS); distanceWeight = _c_calculate_dw3D(distance, distance_xy, distance_z, tSS, tSS_z); + //distanceWeight = _c_calculate_dw_3d(distance_xy, distance_z, tSS, tSS_z); // distanceWeight_xy = _c_calculate_dw_xy(distance_xy, tSS); // distanceWeight_z = _c_calculate_dw_z(distance_z, tSS_z); From d9a950321c8480a503b7bdf58db1571d248badfc Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 15 May 2025 14:52:43 +0100 Subject: [PATCH 53/82] fixed a miscalculation of tSS_z --- .../nanopyx.core.transform._le_esrrf3d.pyx | 4 ++-- src/nanopyx/core/transform/_le_esrrf3d.pyx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index ec668e2e..e6376ff1 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -77,7 +77,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -214,7 +214,7 @@ class eSRRF3D(LiquidEngine): cdef float sigma = radius / 2.355 cdef float tss = 2 * sigma * sigma cdef float tso = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef float tss_z = 2 * sigma_z * sigma_z cdef float tso_z = 2 * sigma_z + 1 diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index f8c430c7..394dad0e 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -72,7 +72,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -149,7 +149,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -226,7 +226,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -303,7 +303,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -379,7 +379,7 @@ class eSRRF3D(LiquidEngine): cdef int margin = int(2 * radius) * magnification_xy cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef int margin_z = int(2 * radius_z) * magnification_z cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 @@ -509,7 +509,7 @@ class eSRRF3D(LiquidEngine): cdef float sigma = radius / 2.355 cdef float tss = 2 * sigma * sigma cdef float tso = 2 * sigma + 1 - cdef float sigma_z = radius_z * voxel_ratio / 2.355 # Taking voxel size into account + cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account cdef float tss_z = 2 * sigma_z * sigma_z cdef float tso_z = 2 * sigma_z + 1 From ace21d007477a729dfa1aae6e9af739d5fd8b3b4 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 15 May 2025 16:03:03 +0100 Subject: [PATCH 54/82] reducing search space to only the size of the radius --- src/include/_c_sr_radial_gradient_convergence.c | 12 ++++++------ .../nanopyx.core.transform._le_esrrf.pyx | 2 +- .../nanopyx.core.transform._le_esrrf3d.pyx | 4 ++-- ...e.transform._le_radial_gradient_convergence.pyx | 6 +++--- src/nanopyx/core/transform/_le_esrrf.pyx | 2 +- src/nanopyx/core/transform/_le_esrrf3d.pyx | 4 ++-- src/nanopyx/core/transform/_le_esrrf3d_.cl | 14 +++++++------- .../transform/_le_radial_gradient_convergence.cl | 8 ++++---- .../transform/_le_radial_gradient_convergence.pyx | 12 ++++++------ 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index 1d86bec5..dd269e5b 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -35,8 +35,8 @@ float _c_calculate_rgc(int xM, int yM, float* imIntGx, float* imIntGy, int colsM float RGC = 0; float distanceWeightSum = 0; - int _start = -(int)(2 * fwhm); - int _end = (int)(2 * fwhm + 1); + int _start = -(int)(fwhm); + int _end = (int)(fwhm + 1); for (int j = _start; j < _end; j++) { vy = yc + j; @@ -134,11 +134,11 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, float* imIntGx, float* imIn float distanceWeightSum_xy = 0; float distanceWeightSum_z = 0; - int _start = -(int)(2 * fwhm); - int _end = (int)(2 * fwhm + 1); + int _start = -(int)(fwhm); + int _end = (int)(fwhm + 1); - int _start_z = -(int)(2 * fwhm_z); - int _end_z = (int)(2 * fwhm_z + 1); + int _start_z = -(int)(fwhm_z); + int _end_z = (int)(fwhm_z + 1); for (int j = _start; j <= _end; j++) { vy = yc + j; diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx index d1c5b98e..95723951 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx @@ -92,7 +92,7 @@ class eSRRF(LiquidEngine): rgc_prg = cl.Program(cl_ctx, rgc_code).build(options=["-cl-mad-enable -cl-fast-relaxed-math"]) rgc_knl = rgc_prg.calculate_rgc - margin = int(radius*2*magnification) + margin = int(radius*magnification) lowest_row = margin # TODO discuss edges calculation highest_row = output_shape[1] - margin lowest_col = margin diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index e6376ff1..98b86ded 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -201,8 +201,8 @@ class eSRRF3D(LiquidEngine): raise ValueError("Invalid mode. Use 'average' or 'std'.") # set margins - margin = int(2 * radius) * magnification_xy - margin_z = int(2 * radius_z) * magnification_z + margin = int(radius) * magnification_xy + margin_z = int(radius_z) * magnification_z lowest_slice = margin_z highest_slice = output_shape[0] - margin_z lowest_row = margin diff --git a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx index fe64b25a..cce21dee 100644 --- a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx @@ -47,7 +47,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -82,7 +82,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -131,7 +131,7 @@ class RadialGradientConvergence(LiquidEngine): # Parameters cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification diff --git a/src/nanopyx/core/transform/_le_esrrf.pyx b/src/nanopyx/core/transform/_le_esrrf.pyx index 860ab942..0eb1c5d4 100644 --- a/src/nanopyx/core/transform/_le_esrrf.pyx +++ b/src/nanopyx/core/transform/_le_esrrf.pyx @@ -90,7 +90,7 @@ class eSRRF(LiquidEngine): rgc_prg = cl.Program(cl_ctx, rgc_code).build(options=["-cl-mad-enable -cl-fast-relaxed-math"]) rgc_knl = rgc_prg.calculate_rgc - margin = int(radius*2*magnification) + margin = int(radius*magnification) lowest_row = margin # TODO discuss edges calculation highest_row = output_shape[1] - margin lowest_col = margin diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index 394dad0e..aa0ba85b 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -496,8 +496,8 @@ class eSRRF3D(LiquidEngine): raise ValueError("Invalid mode. Use 'average' or 'std'.") # set margins - margin = int(2 * radius) * magnification_xy - margin_z = int(2 * radius_z) * magnification_z + margin = int(radius) * magnification_xy + margin_z = int(radius_z) * magnification_z lowest_slice = margin_z highest_slice = output_shape[0] - margin_z lowest_row = margin diff --git a/src/nanopyx/core/transform/_le_esrrf3d_.cl b/src/nanopyx/core/transform/_le_esrrf3d_.cl index c182701a..f5d65dfb 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d_.cl +++ b/src/nanopyx/core/transform/_le_esrrf3d_.cl @@ -219,11 +219,11 @@ float _c_calculate_rgc3D(int xM, int yM, int sliceM, __global float* imIntGx, __ float RGC = 0.0f; float distanceWeightSum = 0.0f; - int _start = -(int)(2 * fwhm); - int _end = (int)(2 * fwhm + 1); + int _start = -(int)(fwhm); + int _end = (int)(fwhm + 1); - int _start_z = -(int)(2 * fwhm_z); - int _end_z = (int)(2 * fwhm_z + 1); + int _start_z = -(int)(fwhm_z); + int _end_z = (int)(fwhm_z + 1); for (int j = _start; j <= _end; j++) { vy = yc + j; @@ -307,9 +307,9 @@ __kernel void calculate_rgc3D(__global float* imIntGz, __global float* imIntGy, // Output image dimensions int nPixels_out = slicesM * rowsM * colsM; - row = row + fwhm*2*magnification_xy; - col = col + fwhm*2*magnification_xy; - s = s + fwhm_z*2*magnification_z; + row = row + fwhm*magnification_xy; + col = col + fwhm*magnification_xy; + s = s + fwhm_z*magnification_z; if (doIntensityWeighting == 1) { tmp_slice[s * rowsM * colsM + row * colsM + col] = _c_calculate_rgc3D(col, row, s, &imIntGx[0], &imIntGy[0], &imIntGz[0], colsM, rowsM, slicesM, magnification_xy, magnification_z, ratio_px, fwhm, fwhm_z, tSO, tSO_z, tSS, tSS_z, sensitivity) * imM[s * rowsM * colsM + row * colsM + col]; diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl index 746b34ef..c1c259a4 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl @@ -54,8 +54,8 @@ float _c_calculate_rgc(int xM, int yM, __global float* imIntGx, __global float* float RGC = 0; float distanceWeightSum = 0; - int _start = -(int)(2 * fwhm); //changed to have "Factor Cagança" but with properly iterating over the desired range - int _end = (int)(2 * fwhm + 1); // TODO discuss with Ricardo + int _start = -(int)(fwhm); + int _end = (int)(fwhm + 1); for (int j = _start; j < _end; j++) { vy = yc + j; @@ -117,8 +117,8 @@ float _c_calculate_rgc(int xM, int yM, __global float* imIntGx, __global float* // gradient image dimensions int nPixels_grad = nRows*Gx_Gy_MAGNIFICATION * nCols*Gx_Gy_MAGNIFICATION; - row = row + fwhm*2*magnification; - col = col + fwhm*2*magnification; + row = row + fwhm*magnification; + col = col + fwhm*magnification; if (doIntensityWeighting == 1) { image_out[f * nPixels_out + row * nCols + col] = _c_calculate_rgc(col, row, &imIntGx[f * nPixels_grad], &imIntGy[f * nPixels_grad], nCols, nRows, magnification, Gx_Gy_MAGNIFICATION, fwhm, tSO, tSS, sensitivity, offset, xyoffset, angle) * imInt[f * nPixels_out + row * nCols + col]; diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx index f12e82d3..f7f0461e 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx @@ -45,7 +45,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -79,7 +79,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -111,7 +111,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -143,7 +143,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -175,7 +175,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -219,7 +219,7 @@ class RadialGradientConvergence(LiquidEngine): # Parameters cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm*2) * magnification + cdef int margin = int(fwhm) * magnification cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification From d0f55b31f24068b9fbb81b545b224f10622a68f1 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Thu, 15 May 2025 17:10:15 +0100 Subject: [PATCH 55/82] added 2D macropixel correction --- src/nanopyx/core/transform/mpcorrector.pyx | 76 +++++++++++++++++++ src/nanopyx/methods/esrrf/eSRRF_workflow.py | 11 ++- .../methods/esrrf_3d/eSRRF3D_workflow.py | 30 +++++--- 3 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 src/nanopyx/core/transform/mpcorrector.pyx diff --git a/src/nanopyx/core/transform/mpcorrector.pyx b/src/nanopyx/core/transform/mpcorrector.pyx new file mode 100644 index 00000000..32ca3df3 --- /dev/null +++ b/src/nanopyx/core/transform/mpcorrector.pyx @@ -0,0 +1,76 @@ +# cython: infer_types=True, wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3, profile=False, autogen_pxd=True + +from cython.parallel import prange +import numpy as np +cimport numpy as np + +def macro_pixel_corrector(img: np.ndarray, magnification: int) -> np.ndarray: + """ + Corrects the macro-pixel effect. + + Parameters + ---------- + img : np.ndarray + The input image to be corrected. + magnification : int + The magnification factor used. + + Returns + ------- + np.ndarray + The corrected image. + """ + + return np.asarray(_macro_pixel_corrector(img.astype(np.float32), magnification), dtype=np.float32) + +cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification): + """ + Cython implementation of the macro-pixel correction. + + Parameters + ---------- + img : float[:, :, :] + The input image to be corrected. + magnification : int + The magnification factor used. + + Returns + ------- + float[:, :, :] + The corrected image. + """ + cdef int slices = img.shape[0] + cdef int rowsM = img.shape[1] + cdef int colsM = img.shape[2] + cdef int rows = rowsM // magnification + cdef int cols = colsM // magnification + + cdef float map_value + cdef int x, y, offset, s, r, c + + cdef float[:, :] map + + for s in range(slices): + map = np.zeros((magnification, magnification), dtype=np.float32) + for ry in range(rows): + for rx in range(cols): + for y in range(magnification): + for x in range(magnification): + # global coordinates in original image + gx = rx * magnification + x + gy = ry * magnification + y + map[y, x] += img[s, gy, gx] + map = np.asarray(map) / rows*cols + + for yM in range(rowsM): + for xM in range(colsM): + y = yM // magnification + x = xM // magnification + y_offset = yM - y * magnification + x_offset = xM - x * magnification + correction = map[y_offset, x_offset] + if correction != 0: + img[s, yM, xM] /= correction + + return img + \ No newline at end of file diff --git a/src/nanopyx/methods/esrrf/eSRRF_workflow.py b/src/nanopyx/methods/esrrf/eSRRF_workflow.py index 42e8ea12..e7dd4afd 100644 --- a/src/nanopyx/methods/esrrf/eSRRF_workflow.py +++ b/src/nanopyx/methods/esrrf/eSRRF_workflow.py @@ -1,5 +1,6 @@ from ..workflow import Workflow from ...core.transform import eSRRF_ST +from ...core.transform.mpcorrector import macro_pixel_corrector import numpy as np # TODO check correlations and error map @@ -11,6 +12,7 @@ def eSRRF( radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, + macro_pixel_correction: bool = False, _force_run_type=None, ): """ @@ -53,5 +55,10 @@ def eSRRF( }, ) ) - - return _eSRRF.calculate(_force_run_type=_force_run_type) + if macro_pixel_correction: + return macro_pixel_corrector( + _eSRRF.calculate(_force_run_type=_force_run_type)[0], + magnification=magnification, + ) + else: + return _eSRRF.calculate(_force_run_type=_force_run_type)[0] diff --git a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py index 07ac66d0..47b23e2e 100644 --- a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py +++ b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py @@ -1,21 +1,23 @@ from ..workflow import Workflow from ...core.transform._le_esrrf3d import eSRRF3D as eSRRF3D_ST +from ...core.transform.mpcorrector import macro_pixel_corrector import numpy as np def eSRRF3D( img, - magnification_xy = 2, - magnification_z = 2, - radius: float = 1.5, - radius_z: float = 0.5, - voxel_ratio: float = 4.0, - sensitivity: float = 1, - mode: str = "average", + magnification_xy=2, + magnification_z=2, + radius: float = 1.5, + radius_z: float = 0.5, + voxel_ratio: float = 4.0, + sensitivity: float = 1, + mode: str = "average", doIntensityWeighting: bool = True, - _force_run_type=None): - + macro_pixel_correction: bool = False, + _force_run_type=None, +): """ #TODO """ @@ -36,6 +38,10 @@ def eSRRF3D( }, ) ) - - return _eSRRF3D.calculate(_force_run_type=_force_run_type) - + if macro_pixel_correction: + return macro_pixel_corrector( + _eSRRF3D.calculate(_force_run_type=_force_run_type)[0], + magnification=magnification_xy, + ) + else: + return _eSRRF3D.calculate(_force_run_type=_force_run_type)[0] From f2e09318aa98da8398de3feabd5841c5fbfa553d Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Fri, 16 May 2025 09:34:40 +0100 Subject: [PATCH 56/82] added macro pixel correction to SRRF --- src/nanopyx/methods/srrf/SRRF_workflow.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/nanopyx/methods/srrf/SRRF_workflow.py b/src/nanopyx/methods/srrf/SRRF_workflow.py index 09a7c59e..1e31081e 100644 --- a/src/nanopyx/methods/srrf/SRRF_workflow.py +++ b/src/nanopyx/methods/srrf/SRRF_workflow.py @@ -1,5 +1,6 @@ from ..workflow import Workflow from ...core.transform import Radiality, CRShiftAndMagnify +from ...core.transform.mpcorrector import macro_pixel_corrector import numpy as np @@ -12,6 +13,7 @@ def SRRF( border=0, radialityPositivityConstraint=True, doIntensityWeighting=True, + macro_pixel_correction=True, _force_run_type=None, ): """ @@ -45,7 +47,11 @@ def SRRF( """ _SRRF = Workflow( - (CRShiftAndMagnify(verbose=False), (image, 0, 0, magnification, magnification), {}), + ( + CRShiftAndMagnify(verbose=False), + (image, 0, 0, magnification, magnification), + {}, + ), ( Radiality(verbose=False), (image, "PREV_RETURN_VALUE_0_0"), @@ -59,4 +65,11 @@ def SRRF( ), ) - return _SRRF.calculate(_force_run_type=_force_run_type) + if macro_pixel_correction: + return macro_pixel_corrector( + _SRRF.calculate(_force_run_type=_force_run_type)[0], + magnification=magnification, + ) + + else: + return _SRRF.calculate(_force_run_type=_force_run_type)[0] From 17b96dfc74735a39edd34941a3c845740d8ef5e1 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Fri, 16 May 2025 09:35:10 +0100 Subject: [PATCH 57/82] made nanopyx.methods srrf and esrrf return the output array and not a tuple --- src/nanopyx/methods/esrrf/eSRRF_workflow.py | 2 +- src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nanopyx/methods/esrrf/eSRRF_workflow.py b/src/nanopyx/methods/esrrf/eSRRF_workflow.py index e7dd4afd..4cd8d3ea 100644 --- a/src/nanopyx/methods/esrrf/eSRRF_workflow.py +++ b/src/nanopyx/methods/esrrf/eSRRF_workflow.py @@ -12,7 +12,7 @@ def eSRRF( radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, - macro_pixel_correction: bool = False, + macro_pixel_correction: bool = True, _force_run_type=None, ): """ diff --git a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py index 47b23e2e..29e09f85 100644 --- a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py +++ b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py @@ -15,7 +15,7 @@ def eSRRF3D( sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, - macro_pixel_correction: bool = False, + macro_pixel_correction: bool = True, _force_run_type=None, ): """ From 75eb29c7bb1fca74d7bf07672a28aebd6e14d61b Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 13:53:28 +0100 Subject: [PATCH 58/82] fixed a bug in macro pixel correction --- src/nanopyx/core/transform/mpcorrector.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/mpcorrector.pyx b/src/nanopyx/core/transform/mpcorrector.pyx index 32ca3df3..43028c9d 100644 --- a/src/nanopyx/core/transform/mpcorrector.pyx +++ b/src/nanopyx/core/transform/mpcorrector.pyx @@ -60,7 +60,7 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification gx = rx * magnification + x gy = ry * magnification + y map[y, x] += img[s, gy, gx] - map = np.asarray(map) / rows*cols + map = np.asarray(map) / (rows*cols) for yM in range(rowsM): for xM in range(colsM): From f7a955f4d11a3fc76a7ddd418427e8ba715b47e7 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 16:08:00 +0100 Subject: [PATCH 59/82] notebook recipes update --- src/notebookchef/pantry/methods/esrrf.py | 2 +- src/notebookchef/pantry/methods/srrf.py | 72 ++++++++++++++----- .../recipes/ExampleDataSRRFandQC.txt | 3 - .../recipes/NonLocalMeansDenoising.txt | 2 - .../recipes/ParamSweepandeSRRF.txt | 3 - src/notebookchef/recipes/SRMetrics.txt | 3 - src/notebookchef/recipes/SRRFandQC.txt | 3 - src/notebookchef/recipes/eSRRFandQC.txt | 3 - 8 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/notebookchef/pantry/methods/esrrf.py b/src/notebookchef/pantry/methods/esrrf.py index bad4f1c3..74752d48 100644 --- a/src/notebookchef/pantry/methods/esrrf.py +++ b/src/notebookchef/pantry/methods/esrrf.py @@ -44,7 +44,7 @@ def run_esrrf(b): doIntensityWeighting=True, ) output.append( - calculate_eSRRF_temporal_correlations(result[0], esrrf_order) + calculate_eSRRF_temporal_correlations(result, esrrf_order) ) global dataset_esrrf diff --git a/src/notebookchef/pantry/methods/srrf.py b/src/notebookchef/pantry/methods/srrf.py index 1d3ab64d..cb5111d6 100644 --- a/src/notebookchef/pantry/methods/srrf.py +++ b/src/notebookchef/pantry/methods/srrf.py @@ -1,7 +1,10 @@ -#@title Create SRRF GUI +# @title Create SRRF GUI gui_srrf = EasyGui("srrf") from nanopyx.methods import SRRF -from nanopyx.core.transform.sr_temporal_correlations import calculate_SRRF_temporal_correlations +from nanopyx.core.transform.sr_temporal_correlations import ( + calculate_SRRF_temporal_correlations, +) + def run_srrf(b): clear_output() @@ -19,14 +22,20 @@ def run_srrf(b): elif frames_per_timepoint > dataset_original.shape[0]: frames_per_timepoint = dataset_original.shape[0] - output= [] + output = [] for i in range(dataset_original.shape[0] // frames_per_timepoint): - block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint] - result = SRRF(block, magnification=magnification, ringRadius=ring_radius, - radialityPositivityConstraint=True, - doIntensityWeighting=True) - output.append(calculate_SRRF_temporal_correlations(result[0], srrf_order)) + block = dataset_original[ + i * frames_per_timepoint : (i + 1) * frames_per_timepoint + ] + result = SRRF( + block, + magnification=magnification, + ringRadius=ring_radius, + radialityPositivityConstraint=True, + doIntensityWeighting=True, + ) + output.append(calculate_SRRF_temporal_correlations(result, srrf_order)) global dataset_srrf dataset_srrf = np.array(output) @@ -39,19 +48,44 @@ def run_srrf(b): name = gui_data["upload"].selected_filename.split(".")[0] tiff.imwrite(path + os.sep + name + "_srrf.tif", dataset_srrf) else: - name = gui_data["data_source"].value.replace("Example dataset: ", "") + name = gui_data["data_source"].value.replace( + "Example dataset: ", "" + ) tiff.imwrite(name + "_srrf.tif", dataset_srrf) - gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf["cmaps"].value, continuous_update=True),) + gui_srrf._main_display.children = gui_srrf._main_display.children + ( + stackview.slice( + dataset_srrf, + colormap=gui_srrf["cmaps"].value, + continuous_update=True, + ), + ) + -gui_srrf.add_float_slider("ring_radius", description="Ring Radius:", min=0.1, max=3.0, value=0.5) -gui_srrf.add_int_slider("magnification", description="Magnification:", min=1, max=10, value=5) -gui_srrf.add_int_slider("srrf_order", description="SRRF order:", min=-1, max=4, value=3) +gui_srrf.add_float_slider( + "ring_radius", description="Ring Radius:", min=0.1, max=3.0, value=0.5 +) +gui_srrf.add_int_slider( + "magnification", description="Magnification:", min=1, max=10, value=5 +) +gui_srrf.add_int_slider( + "srrf_order", description="SRRF order:", min=-1, max=4, value=3 +) gui_srrf.add_label(value="-=-= Time-Lapse =-=-") -gui_srrf.add_int_slider("frames_per_timepoint", description="Frames per time-point (0 - auto)", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2) +gui_srrf.add_int_slider( + "frames_per_timepoint", + description="Frames per time-point (0 - auto)", + min=1, + max=dataset_original.shape[0], + value=dataset_original.shape[0] // 2, +) gui_srrf.add_checkbox("save", description="Save Output", value=True) -gui_srrf.add_dropdown("cmaps", description="Colormap:", - options=sorted(list(mpl.colormaps)), - value="viridis", remember_value=True) +gui_srrf.add_dropdown( + "cmaps", + description="Colormap:", + options=sorted(list(mpl.colormaps)), + value="viridis", + remember_value=True, +) gui_srrf.add_button("run", description="Run") -gui_srrf['run'].on_click(run_srrf) -gui_srrf.show() \ No newline at end of file +gui_srrf["run"].on_click(run_srrf) +gui_srrf.show() diff --git a/src/notebookchef/recipes/ExampleDataSRRFandQC.txt b/src/notebookchef/recipes/ExampleDataSRRFandQC.txt index 6fa57b64..a695743b 100644 --- a/src/notebookchef/recipes/ExampleDataSRRFandQC.txt +++ b/src/notebookchef/recipes/ExampleDataSRRFandQC.txt @@ -9,9 +9,6 @@ $include: citations/frc.txt $include: citations/decorr.txt __cellbreak__ $code -$include: opencl_colab_fix.py -__cellbreak__ -$code $include: notebook_setup.py __cellbreak__ $markdown diff --git a/src/notebookchef/recipes/NonLocalMeansDenoising.txt b/src/notebookchef/recipes/NonLocalMeansDenoising.txt index 25f81402..e08046f3 100644 --- a/src/notebookchef/recipes/NonLocalMeansDenoising.txt +++ b/src/notebookchef/recipes/NonLocalMeansDenoising.txt @@ -6,8 +6,6 @@ This notebook allows you to perform denoising on either 2 or 3 dimensional array $include: notebook_intro.txt $include: citations/nlm_denoising.txt __cellbreak__ -$code -$include: opencl_colab_fix.py __cellbreak__ $code $include: notebook_setup.py diff --git a/src/notebookchef/recipes/ParamSweepandeSRRF.txt b/src/notebookchef/recipes/ParamSweepandeSRRF.txt index 09259839..d608dc2d 100644 --- a/src/notebookchef/recipes/ParamSweepandeSRRF.txt +++ b/src/notebookchef/recipes/ParamSweepandeSRRF.txt @@ -10,9 +10,6 @@ $include: citations/frc.txt $include: citations/decorr.txt __cellbreak__ $code -$include: opencl_colab_fix.py -__cellbreak__ -$code $include: notebook_setup.py __cellbreak__ $code diff --git a/src/notebookchef/recipes/SRMetrics.txt b/src/notebookchef/recipes/SRMetrics.txt index 60dc424f..52bd849d 100644 --- a/src/notebookchef/recipes/SRMetrics.txt +++ b/src/notebookchef/recipes/SRMetrics.txt @@ -9,9 +9,6 @@ $include: citations/frc.txt $include: citations/decorr.txt __cellbreak__ $code -$include: opencl_colab_fix.py -__cellbreak__ -$code $include: notebook_setup.py __cellbreak__ $markdown diff --git a/src/notebookchef/recipes/SRRFandQC.txt b/src/notebookchef/recipes/SRRFandQC.txt index 82a11123..8c7b22b2 100644 --- a/src/notebookchef/recipes/SRRFandQC.txt +++ b/src/notebookchef/recipes/SRRFandQC.txt @@ -10,9 +10,6 @@ $include: citations/frc.txt $include: citations/decorr.txt __cellbreak__ $code -$include: opencl_colab_fix.py -__cellbreak__ -$code $include: notebook_setup.py __cellbreak__ $code diff --git a/src/notebookchef/recipes/eSRRFandQC.txt b/src/notebookchef/recipes/eSRRFandQC.txt index f87b9c38..dacc4157 100644 --- a/src/notebookchef/recipes/eSRRFandQC.txt +++ b/src/notebookchef/recipes/eSRRFandQC.txt @@ -10,9 +10,6 @@ $include: citations/frc.txt $include: citations/decorr.txt __cellbreak__ $code -$include: opencl_colab_fix.py -__cellbreak__ -$code $include: notebook_setup.py __cellbreak__ $code From 5f3422b02df456f2888ffdafadc52d7b9500614d Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 16:19:00 +0100 Subject: [PATCH 60/82] updated recipes --- src/notebookchef/pantry/methods/esrrf.py | 6 +++++- src/notebookchef/pantry/methods/srrf.py | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/notebookchef/pantry/methods/esrrf.py b/src/notebookchef/pantry/methods/esrrf.py index 74752d48..76ead076 100644 --- a/src/notebookchef/pantry/methods/esrrf.py +++ b/src/notebookchef/pantry/methods/esrrf.py @@ -14,7 +14,7 @@ def run_esrrf(b): magnification = gui_esrrf["magnification"].value frames_per_timepoint = gui_esrrf["frames_per_timepoint"].value sensitivity = gui_esrrf["sensitivity"].value - + mpcorrection = gui_esrrf["mpcorrection"].value esrrf_order = gui_esrrf["esrrf_order"].value if esrrf_order == 1: esrrf_order = "AVG" @@ -42,6 +42,7 @@ def run_esrrf(b): radius=ring_radius, sensitivity=sensitivity, doIntensityWeighting=True, + macro_pixel_correction=mpcorrection, ) output.append( calculate_eSRRF_temporal_correlations(result, esrrf_order) @@ -111,6 +112,9 @@ def run_esrrf(b): max=dataset_original.shape[0], value=dataset_original.shape[0] // 2, ) +gui_esrrf.add_checkbox( + "mpcorrection", description="Macro Pixel Correction", value=True +) gui_esrrf.add_checkbox("save", description="Save Output", value=True) gui_esrrf.add_dropdown( "cmaps", diff --git a/src/notebookchef/pantry/methods/srrf.py b/src/notebookchef/pantry/methods/srrf.py index cb5111d6..aebbfc17 100644 --- a/src/notebookchef/pantry/methods/srrf.py +++ b/src/notebookchef/pantry/methods/srrf.py @@ -14,6 +14,7 @@ def run_srrf(b): magnification = gui_srrf["magnification"].value frames_per_timepoint = gui_srrf["frames_per_timepoint"].value srrf_order = gui_srrf["srrf_order"].value + mpcorrection = gui_srrf["mpcorrection"].value # disable button while running gui_srrf["run"].disabled = True gui_srrf["run"].description = "Running..." @@ -34,6 +35,7 @@ def run_srrf(b): ringRadius=ring_radius, radialityPositivityConstraint=True, doIntensityWeighting=True, + macro_pixel_correction=mpcorrection, ) output.append(calculate_SRRF_temporal_correlations(result, srrf_order)) @@ -78,6 +80,9 @@ def run_srrf(b): max=dataset_original.shape[0], value=dataset_original.shape[0] // 2, ) +gui_srrf.add_checkbox( + "mpcorrection", description="Macro Pixel Correction", value=True +) gui_srrf.add_checkbox("save", description="Save Output", value=True) gui_srrf.add_dropdown( "cmaps", From 44fa5a870f1e967d730a3e296062dcb28e70c1db Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 16:20:08 +0100 Subject: [PATCH 61/82] notebooks update --- notebooks/ChannelRegistration.ipynb | 14 +- notebooks/DriftCorrection.ipynb | 14 +- notebooks/ExampleDataSRRFandQC.ipynb | 135 ++++++++++-------- notebooks/NonLocalMeansDenoising.ipynb | 32 +---- notebooks/ParamSweepandeSRRF.ipynb | 62 +++----- notebooks/SRMetrics.ipynb | 54 +++---- notebooks/SRRFandQC.ipynb | 127 +++++++++------- notebooks/eSRRFandQC.ipynb | 60 +++----- .../recipes/NonLocalMeansDenoising.txt | 1 - 9 files changed, 226 insertions(+), 273 deletions(-) diff --git a/notebooks/ChannelRegistration.ipynb b/notebooks/ChannelRegistration.ipynb index edb0285b..b256fbac 100644 --- a/notebooks/ChannelRegistration.ipynb +++ b/notebooks/ChannelRegistration.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "987d17ec", + "id": "21d1e58b", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c9fa0882", + "id": "b3c2a12b", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b00c81de", + "id": "a5c09b00", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "2d33c8c7", + "id": "535b3d44", "metadata": {}, "source": [ "# Channel Registration Parameters: \n", @@ -182,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7c670165", + "id": "5c4e28f4", "metadata": { "cellView": "form" }, @@ -250,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "3161bf61", + "id": "ae281b8a", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated translation mask\n", @@ -260,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ae29f67d", + "id": "c0e91c8c", "metadata": { "cellView": "form" }, diff --git a/notebooks/DriftCorrection.ipynb b/notebooks/DriftCorrection.ipynb index 9998ea0c..5c942f05 100644 --- a/notebooks/DriftCorrection.ipynb +++ b/notebooks/DriftCorrection.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "d089d246", + "id": "4a8ab58f", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -22,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ce77969c", + "id": "80886110", "metadata": { "cellView": "form" }, @@ -80,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65efd170", + "id": "7d8288ff", "metadata": { "cellView": "form" }, @@ -164,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "d9fd88e8", + "id": "c800a0e9", "metadata": {}, "source": [ "# Drift Correction Parameters: \n", @@ -181,7 +181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "aff3bb2c", + "id": "e2d6671a", "metadata": { "cellView": "form" }, @@ -274,7 +274,7 @@ }, { "cell_type": "markdown", - "id": "64984ecf", + "id": "60fc5a28", "metadata": {}, "source": [ "## Use the following cell only if you have a previously calculated drift table\n", @@ -284,7 +284,7 @@ { "cell_type": "code", "execution_count": null, - "id": "06356b7f", + "id": "45dc3ea1", "metadata": { "cellView": "form" }, diff --git a/notebooks/ExampleDataSRRFandQC.ipynb b/notebooks/ExampleDataSRRFandQC.ipynb index 6b06ce62..1ec6d8e1 100644 --- a/notebooks/ExampleDataSRRFandQC.ipynb +++ b/notebooks/ExampleDataSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "48884b92", + "id": "ccf59fd6", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,29 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "032e6230", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d90912be", + "id": "ac16d462", "metadata": { "cellView": "form" }, @@ -105,7 +83,7 @@ }, { "cell_type": "markdown", - "id": "52ec6eca", + "id": "b589d195", "metadata": {}, "source": [ "## Next lets create the Data Loader GUI.\n", @@ -116,7 +94,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4bdbc4c7", + "id": "894a6951", "metadata": { "cellView": "form" }, @@ -200,7 +178,7 @@ }, { "cell_type": "markdown", - "id": "4b5274aa", + "id": "517e4a79", "metadata": {}, "source": [ "## Now let's use SRRF to generate a super-resolution image\n", @@ -209,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "8810bfa3", + "id": "b2b2ef21", "metadata": {}, "source": [ "# SRRF Parameters:\n", @@ -225,16 +203,19 @@ { "cell_type": "code", "execution_count": null, - "id": "229ddbdd", + "id": "c7fc2972", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create SRRF GUI\n", + "# @title Create SRRF GUI\n", "gui_srrf = EasyGui(\"srrf\")\n", "from nanopyx.methods import SRRF\n", - "from nanopyx.core.transform.sr_temporal_correlations import calculate_SRRF_temporal_correlations\n", + "from nanopyx.core.transform.sr_temporal_correlations import (\n", + " calculate_SRRF_temporal_correlations,\n", + ")\n", + "\n", "\n", "def run_srrf(b):\n", " clear_output()\n", @@ -244,6 +225,7 @@ " magnification = gui_srrf[\"magnification\"].value\n", " frames_per_timepoint = gui_srrf[\"frames_per_timepoint\"].value\n", " srrf_order = gui_srrf[\"srrf_order\"].value\n", + " mpcorrection = gui_srrf[\"mpcorrection\"].value\n", " # disable button while running\n", " gui_srrf[\"run\"].disabled = True\n", " gui_srrf[\"run\"].description = \"Running...\"\n", @@ -252,14 +234,21 @@ " elif frames_per_timepoint > dataset_original.shape[0]:\n", " frames_per_timepoint = dataset_original.shape[0]\n", "\n", - " output= []\n", + " output = []\n", "\n", " for i in range(dataset_original.shape[0] // frames_per_timepoint):\n", - " block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint]\n", - " result = SRRF(block, magnification=magnification, ringRadius=ring_radius,\n", - " radialityPositivityConstraint=True,\n", - " doIntensityWeighting=True)\n", - " output.append(calculate_SRRF_temporal_correlations(result[0], srrf_order))\n", + " block = dataset_original[\n", + " i * frames_per_timepoint : (i + 1) * frames_per_timepoint\n", + " ]\n", + " result = SRRF(\n", + " block,\n", + " magnification=magnification,\n", + " ringRadius=ring_radius,\n", + " radialityPositivityConstraint=True,\n", + " doIntensityWeighting=True,\n", + " macro_pixel_correction=mpcorrection,\n", + " )\n", + " output.append(calculate_SRRF_temporal_correlations(result, srrf_order))\n", "\n", " global dataset_srrf\n", " dataset_srrf = np.array(output)\n", @@ -272,21 +261,49 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_srrf.tif\", dataset_srrf)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_srrf.tif\", dataset_srrf)\n", - " gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf[\"cmaps\"].value, continuous_update=True),)\n", + " gui_srrf._main_display.children = gui_srrf._main_display.children + (\n", + " stackview.slice(\n", + " dataset_srrf,\n", + " colormap=gui_srrf[\"cmaps\"].value,\n", + " continuous_update=True,\n", + " ),\n", + " )\n", + "\n", "\n", - "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5)\n", - "gui_srrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=5)\n", - "gui_srrf.add_int_slider(\"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3)\n", + "gui_srrf.add_float_slider(\n", + " \"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5\n", + ")\n", + "gui_srrf.add_int_slider(\n", + " \"magnification\", description=\"Magnification:\", min=1, max=10, value=5\n", + ")\n", + "gui_srrf.add_int_slider(\n", + " \"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3\n", + ")\n", "gui_srrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", - "gui_srrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", + "gui_srrf.add_int_slider(\n", + " \"frames_per_timepoint\",\n", + " description=\"Frames per time-point (0 - auto)\",\n", + " min=1,\n", + " max=dataset_original.shape[0],\n", + " value=dataset_original.shape[0] // 2,\n", + ")\n", + "gui_srrf.add_checkbox(\n", + " \"mpcorrection\", description=\"Macro Pixel Correction\", value=True\n", + ")\n", "gui_srrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", - "gui_srrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_srrf.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_srrf.add_button(\"run\", description=\"Run\")\n", - "gui_srrf['run'].on_click(run_srrf)\n", + "gui_srrf[\"run\"].on_click(run_srrf)\n", "gui_srrf.show()\n", "\n", "\n" @@ -294,7 +311,7 @@ }, { "cell_type": "markdown", - "id": "f40dda16", + "id": "41180535", "metadata": {}, "source": [ "## Let's use NanoPyx to generate an error map of the SRRF image\n", @@ -304,7 +321,7 @@ { "cell_type": "code", "execution_count": null, - "id": "c37f42be", + "id": "ab02d527", "metadata": { "cellView": "form" }, @@ -459,7 +476,7 @@ }, { "cell_type": "markdown", - "id": "f0aeaba7", + "id": "40f389f4", "metadata": {}, "source": [ "## Let's compare the resolution of the raw data with the SRRF using FRC and DecorrelationAnalysis. Let's start with calculation the FRC resolution of the raw data (select frame 3 and 11).\n", @@ -468,7 +485,7 @@ }, { "cell_type": "markdown", - "id": "2535ddcd", + "id": "b3b9ce84", "metadata": {}, "source": [ "# FRC Parameters:\n", @@ -483,7 +500,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0ea8ebe9", + "id": "6ef9e176", "metadata": { "cellView": "form" }, @@ -543,7 +560,7 @@ }, { "cell_type": "markdown", - "id": "742d698e", + "id": "3be3aa0a", "metadata": {}, "source": [ "## Now do the same for the SRRF image\n", @@ -553,7 +570,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62a44873", + "id": "3c364a56", "metadata": { "cellView": "form" }, @@ -613,7 +630,7 @@ }, { "cell_type": "markdown", - "id": "fa7761fd", + "id": "611e7ff7", "metadata": {}, "source": [ "## Let's do the same using Decorrelation analysis\n", @@ -622,7 +639,7 @@ }, { "cell_type": "markdown", - "id": "b4216b5b", + "id": "19efd107", "metadata": {}, "source": [ "# Image Decorrelation Analysis Parameters:\n", @@ -638,7 +655,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66041d04", + "id": "72263010", "metadata": { "cellView": "form" }, @@ -699,7 +716,7 @@ }, { "cell_type": "markdown", - "id": "b13ba100", + "id": "4c0790e7", "metadata": {}, "source": [ "## Now let's measure the resolution of the generated SRRF image using Decorrelation analysis\n", @@ -709,7 +726,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ffa9cc07", + "id": "b87c1750", "metadata": { "cellView": "form" }, diff --git a/notebooks/NonLocalMeansDenoising.ipynb b/notebooks/NonLocalMeansDenoising.ipynb index cef5544a..4fe964b1 100644 --- a/notebooks/NonLocalMeansDenoising.ipynb +++ b/notebooks/NonLocalMeansDenoising.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "7234102b", + "id": "d6df60cd", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -23,29 +23,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12e2ce1e", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d3591cd", + "id": "a63bd5aa", "metadata": { "cellView": "form" }, @@ -103,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cef83399", + "id": "9d66588d", "metadata": { "cellView": "form" }, @@ -187,7 +165,7 @@ }, { "cell_type": "markdown", - "id": "29916cbb", + "id": "479327f7", "metadata": {}, "source": [ "# Use Non-local means denoising on selected data\n", @@ -204,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fa448b9d", + "id": "66b826f8", "metadata": { "cellView": "form" }, diff --git a/notebooks/ParamSweepandeSRRF.ipynb b/notebooks/ParamSweepandeSRRF.ipynb index b79abb4d..b6aab1fa 100644 --- a/notebooks/ParamSweepandeSRRF.ipynb +++ b/notebooks/ParamSweepandeSRRF.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "6345d1e8", + "id": "0c5fd2df", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,29 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ba34a112", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae512df5", + "id": "5ee4bab7", "metadata": { "cellView": "form" }, @@ -108,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0827c922", + "id": "ae1cf4a9", "metadata": { "cellView": "form" }, @@ -192,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "0738884d", + "id": "0c6ad675", "metadata": {}, "source": [ "# Run a parameter sweep to find the best combination of parameters\n", @@ -209,7 +187,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52dcb045", + "id": "b49653f3", "metadata": { "cellView": "form" }, @@ -274,7 +252,7 @@ { "cell_type": "code", "execution_count": null, - "id": "400aa938", + "id": "619081d0", "metadata": { "cellView": "form" }, @@ -296,7 +274,7 @@ " magnification = gui_esrrf[\"magnification\"].value\n", " frames_per_timepoint = gui_esrrf[\"frames_per_timepoint\"].value\n", " sensitivity = gui_esrrf[\"sensitivity\"].value\n", - "\n", + " mpcorrection = gui_esrrf[\"mpcorrection\"].value\n", " esrrf_order = gui_esrrf[\"esrrf_order\"].value\n", " if esrrf_order == 1:\n", " esrrf_order = \"AVG\"\n", @@ -324,9 +302,10 @@ " radius=ring_radius,\n", " sensitivity=sensitivity,\n", " doIntensityWeighting=True,\n", + " macro_pixel_correction=mpcorrection,\n", " )\n", " output.append(\n", - " calculate_eSRRF_temporal_correlations(result[0], esrrf_order)\n", + " calculate_eSRRF_temporal_correlations(result, esrrf_order)\n", " )\n", "\n", " global dataset_esrrf\n", @@ -393,6 +372,9 @@ " max=dataset_original.shape[0],\n", " value=dataset_original.shape[0] // 2,\n", ")\n", + "gui_esrrf.add_checkbox(\n", + " \"mpcorrection\", description=\"Macro Pixel Correction\", value=True\n", + ")\n", "gui_esrrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", "gui_esrrf.add_dropdown(\n", " \"cmaps\",\n", @@ -410,7 +392,7 @@ }, { "cell_type": "markdown", - "id": "4b835280", + "id": "a718a9a5", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -420,7 +402,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2f2ac1a9", + "id": "83d1042a", "metadata": { "cellView": "form" }, @@ -575,7 +557,7 @@ }, { "cell_type": "markdown", - "id": "651b8da0", + "id": "7acada4e", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -591,7 +573,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d10270f9", + "id": "5c44bfc2", "metadata": { "cellView": "form" }, @@ -651,7 +633,7 @@ }, { "cell_type": "markdown", - "id": "f82ce04d", + "id": "7a8c60d9", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -667,7 +649,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5ee8bfc3", + "id": "18f5b749", "metadata": { "cellView": "form" }, @@ -727,7 +709,7 @@ }, { "cell_type": "markdown", - "id": "308bbf59", + "id": "dfb690d0", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -744,7 +726,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68c0902f", + "id": "d6eccdf8", "metadata": { "cellView": "form" }, @@ -805,7 +787,7 @@ }, { "cell_type": "markdown", - "id": "1a67553a", + "id": "290a5fc0", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -822,7 +804,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54ba26d2", + "id": "60a23520", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRMetrics.ipynb b/notebooks/SRMetrics.ipynb index 060aa8a1..fc0034c6 100644 --- a/notebooks/SRMetrics.ipynb +++ b/notebooks/SRMetrics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "b16c7e5a", + "id": "9742f460", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -26,29 +26,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41c4bacf", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "086275fe", + "id": "ce41f905", "metadata": { "cellView": "form" }, @@ -105,7 +83,7 @@ }, { "cell_type": "markdown", - "id": "deead987", + "id": "5feab865", "metadata": {}, "source": [ "## Load difraction limited image (only needed if you want to use the FRC and Decorrelation analysis on this image or if you want to perform the Error Map analysis)\n", @@ -115,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3a5d0c58", + "id": "a5bb0805", "metadata": { "cellView": "form" }, @@ -199,7 +177,7 @@ }, { "cell_type": "markdown", - "id": "c0150cad", + "id": "f3e45bcc", "metadata": {}, "source": [ "## Load super-resolved image\n", @@ -209,7 +187,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b60c80ec", + "id": "79311067", "metadata": { "cellView": "form" }, @@ -293,7 +271,7 @@ }, { "cell_type": "markdown", - "id": "1525b21b", + "id": "830472cd", "metadata": {}, "source": [ "## Calculate Error Map\n", @@ -303,7 +281,7 @@ { "cell_type": "code", "execution_count": null, - "id": "de7cddbf", + "id": "10d68395", "metadata": { "cellView": "form" }, @@ -458,7 +436,7 @@ }, { "cell_type": "markdown", - "id": "eec9934d", + "id": "91e0077b", "metadata": {}, "source": [ "# Calculate FRC of the diffraction limited image\n", @@ -474,7 +452,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2ed0433f", + "id": "fd5f7912", "metadata": { "cellView": "form" }, @@ -534,7 +512,7 @@ }, { "cell_type": "markdown", - "id": "90527123", + "id": "6bd57aae", "metadata": {}, "source": [ "# Calculate FRC of the SR image\n", @@ -550,7 +528,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8d61026b", + "id": "688b043c", "metadata": { "cellView": "form" }, @@ -610,7 +588,7 @@ }, { "cell_type": "markdown", - "id": "5613b634", + "id": "e7fe12d3", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of diffraction limited image\n", @@ -627,7 +605,7 @@ { "cell_type": "code", "execution_count": null, - "id": "87ccb8cd", + "id": "2fc498d0", "metadata": { "cellView": "form" }, @@ -688,7 +666,7 @@ }, { "cell_type": "markdown", - "id": "fa7f8ede", + "id": "61efd725", "metadata": {}, "source": [ "# Calculate Decorrelation analysis of SR image\n", @@ -705,7 +683,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d240adcc", + "id": "812b3075", "metadata": { "cellView": "form" }, diff --git a/notebooks/SRRFandQC.ipynb b/notebooks/SRRFandQC.ipynb index fe1ce367..4c169f13 100644 --- a/notebooks/SRRFandQC.ipynb +++ b/notebooks/SRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "7a5a3751", + "id": "127bc22a", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,29 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "295553ae", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2bf612e", + "id": "f649e22a", "metadata": { "cellView": "form" }, @@ -108,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9970d28c", + "id": "ab45f6e3", "metadata": { "cellView": "form" }, @@ -192,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "7411f5e2", + "id": "988237bc", "metadata": {}, "source": [ "# Use SRRF to generate a super-resolved image\n", @@ -209,16 +187,19 @@ { "cell_type": "code", "execution_count": null, - "id": "87ff388f", + "id": "1efcc50d", "metadata": { "cellView": "form" }, "outputs": [], "source": [ - "#@title Create SRRF GUI\n", + "# @title Create SRRF GUI\n", "gui_srrf = EasyGui(\"srrf\")\n", "from nanopyx.methods import SRRF\n", - "from nanopyx.core.transform.sr_temporal_correlations import calculate_SRRF_temporal_correlations\n", + "from nanopyx.core.transform.sr_temporal_correlations import (\n", + " calculate_SRRF_temporal_correlations,\n", + ")\n", + "\n", "\n", "def run_srrf(b):\n", " clear_output()\n", @@ -228,6 +209,7 @@ " magnification = gui_srrf[\"magnification\"].value\n", " frames_per_timepoint = gui_srrf[\"frames_per_timepoint\"].value\n", " srrf_order = gui_srrf[\"srrf_order\"].value\n", + " mpcorrection = gui_srrf[\"mpcorrection\"].value\n", " # disable button while running\n", " gui_srrf[\"run\"].disabled = True\n", " gui_srrf[\"run\"].description = \"Running...\"\n", @@ -236,14 +218,21 @@ " elif frames_per_timepoint > dataset_original.shape[0]:\n", " frames_per_timepoint = dataset_original.shape[0]\n", "\n", - " output= []\n", + " output = []\n", "\n", " for i in range(dataset_original.shape[0] // frames_per_timepoint):\n", - " block = dataset_original[i*frames_per_timepoint:(i+1)*frames_per_timepoint]\n", - " result = SRRF(block, magnification=magnification, ringRadius=ring_radius,\n", - " radialityPositivityConstraint=True,\n", - " doIntensityWeighting=True)\n", - " output.append(calculate_SRRF_temporal_correlations(result[0], srrf_order))\n", + " block = dataset_original[\n", + " i * frames_per_timepoint : (i + 1) * frames_per_timepoint\n", + " ]\n", + " result = SRRF(\n", + " block,\n", + " magnification=magnification,\n", + " ringRadius=ring_radius,\n", + " radialityPositivityConstraint=True,\n", + " doIntensityWeighting=True,\n", + " macro_pixel_correction=mpcorrection,\n", + " )\n", + " output.append(calculate_SRRF_temporal_correlations(result, srrf_order))\n", "\n", " global dataset_srrf\n", " dataset_srrf = np.array(output)\n", @@ -256,21 +245,49 @@ " name = gui_data[\"upload\"].selected_filename.split(\".\")[0]\n", " tiff.imwrite(path + os.sep + name + \"_srrf.tif\", dataset_srrf)\n", " else:\n", - " name = gui_data[\"data_source\"].value.replace(\"Example dataset: \", \"\")\n", + " name = gui_data[\"data_source\"].value.replace(\n", + " \"Example dataset: \", \"\"\n", + " )\n", " tiff.imwrite(name + \"_srrf.tif\", dataset_srrf)\n", - " gui_srrf._main_display.children = gui_srrf._main_display.children + (stackview.slice(dataset_srrf, colormap=gui_srrf[\"cmaps\"].value, continuous_update=True),)\n", + " gui_srrf._main_display.children = gui_srrf._main_display.children + (\n", + " stackview.slice(\n", + " dataset_srrf,\n", + " colormap=gui_srrf[\"cmaps\"].value,\n", + " continuous_update=True,\n", + " ),\n", + " )\n", + "\n", "\n", - "gui_srrf.add_float_slider(\"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5)\n", - "gui_srrf.add_int_slider(\"magnification\", description=\"Magnification:\", min=1, max=10, value=5)\n", - "gui_srrf.add_int_slider(\"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3)\n", + "gui_srrf.add_float_slider(\n", + " \"ring_radius\", description=\"Ring Radius:\", min=0.1, max=3.0, value=0.5\n", + ")\n", + "gui_srrf.add_int_slider(\n", + " \"magnification\", description=\"Magnification:\", min=1, max=10, value=5\n", + ")\n", + "gui_srrf.add_int_slider(\n", + " \"srrf_order\", description=\"SRRF order:\", min=-1, max=4, value=3\n", + ")\n", "gui_srrf.add_label(value=\"-=-= Time-Lapse =-=-\")\n", - "gui_srrf.add_int_slider(\"frames_per_timepoint\", description=\"Frames per time-point (0 - auto)\", min=1, max=dataset_original.shape[0], value=dataset_original.shape[0]//2)\n", + "gui_srrf.add_int_slider(\n", + " \"frames_per_timepoint\",\n", + " description=\"Frames per time-point (0 - auto)\",\n", + " min=1,\n", + " max=dataset_original.shape[0],\n", + " value=dataset_original.shape[0] // 2,\n", + ")\n", + "gui_srrf.add_checkbox(\n", + " \"mpcorrection\", description=\"Macro Pixel Correction\", value=True\n", + ")\n", "gui_srrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", - "gui_srrf.add_dropdown(\"cmaps\", description=\"Colormap:\",\n", - " options=sorted(list(mpl.colormaps)),\n", - " value=\"viridis\", remember_value=True)\n", + "gui_srrf.add_dropdown(\n", + " \"cmaps\",\n", + " description=\"Colormap:\",\n", + " options=sorted(list(mpl.colormaps)),\n", + " value=\"viridis\",\n", + " remember_value=True,\n", + ")\n", "gui_srrf.add_button(\"run\", description=\"Run\")\n", - "gui_srrf['run'].on_click(run_srrf)\n", + "gui_srrf[\"run\"].on_click(run_srrf)\n", "gui_srrf.show()\n", "\n", "\n" @@ -278,7 +295,7 @@ }, { "cell_type": "markdown", - "id": "dbcb4e66", + "id": "e9c6c1cd", "metadata": {}, "source": [ "## Calculate error map for the SRRF image\n", @@ -288,7 +305,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b23b12d8", + "id": "b68a496e", "metadata": { "cellView": "form" }, @@ -443,7 +460,7 @@ }, { "cell_type": "markdown", - "id": "8fc2cdee", + "id": "d5cfb112", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -459,7 +476,7 @@ { "cell_type": "code", "execution_count": null, - "id": "cc8076fc", + "id": "7cd4c902", "metadata": { "cellView": "form" }, @@ -519,7 +536,7 @@ }, { "cell_type": "markdown", - "id": "e4493745", + "id": "0e2e01fb", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -535,7 +552,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53034293", + "id": "69644270", "metadata": { "cellView": "form" }, @@ -595,7 +612,7 @@ }, { "cell_type": "markdown", - "id": "6878882b", + "id": "76beec23", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -612,7 +629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34f5de86", + "id": "e47e802d", "metadata": { "cellView": "form" }, @@ -673,7 +690,7 @@ }, { "cell_type": "markdown", - "id": "c315ff47", + "id": "dc0f812e", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -690,7 +707,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b1df6e51", + "id": "12dd4e97", "metadata": { "cellView": "form" }, diff --git a/notebooks/eSRRFandQC.ipynb b/notebooks/eSRRFandQC.ipynb index 2e2c7072..1b8c0946 100644 --- a/notebooks/eSRRFandQC.ipynb +++ b/notebooks/eSRRFandQC.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "5e57974e", + "id": "91304c56", "metadata": {}, "source": [ "# NanoPyx \"Codeless\" Jupyter Notebook\n", @@ -28,29 +28,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a1c8cab9", - "metadata": { - "cellView": "form" - }, - "outputs": [], - "source": [ - "#@title Fix OpenCL if needed in Google Colab\n", - "import sys\n", - "\n", - "IN_COLAB = 'google.colab' in sys.modules\n", - "if IN_COLAB:\n", - " !sudo apt-get update -qq\n", - " !sudo apt-get purge -qq *nvidia* -y\n", - " !sudo DEBIAN_FRONTEND=noninteractive apt-get install -qq nvidia-driver-530 -y\n", - " exit(0)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "159be9db", + "id": "a4b967bf", "metadata": { "cellView": "form" }, @@ -108,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2a60be85", + "id": "28c5296b", "metadata": { "cellView": "form" }, @@ -192,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "2b5992a8", + "id": "cc7a9fd5", "metadata": {}, "source": [ "# Use eSRRF to generate a super-resolved image\n", @@ -209,7 +187,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b2ca01b0", + "id": "429e61b2", "metadata": { "cellView": "form" }, @@ -231,7 +209,7 @@ " magnification = gui_esrrf[\"magnification\"].value\n", " frames_per_timepoint = gui_esrrf[\"frames_per_timepoint\"].value\n", " sensitivity = gui_esrrf[\"sensitivity\"].value\n", - "\n", + " mpcorrection = gui_esrrf[\"mpcorrection\"].value\n", " esrrf_order = gui_esrrf[\"esrrf_order\"].value\n", " if esrrf_order == 1:\n", " esrrf_order = \"AVG\"\n", @@ -259,9 +237,10 @@ " radius=ring_radius,\n", " sensitivity=sensitivity,\n", " doIntensityWeighting=True,\n", + " macro_pixel_correction=mpcorrection,\n", " )\n", " output.append(\n", - " calculate_eSRRF_temporal_correlations(result[0], esrrf_order)\n", + " calculate_eSRRF_temporal_correlations(result, esrrf_order)\n", " )\n", "\n", " global dataset_esrrf\n", @@ -328,6 +307,9 @@ " max=dataset_original.shape[0],\n", " value=dataset_original.shape[0] // 2,\n", ")\n", + "gui_esrrf.add_checkbox(\n", + " \"mpcorrection\", description=\"Macro Pixel Correction\", value=True\n", + ")\n", "gui_esrrf.add_checkbox(\"save\", description=\"Save Output\", value=True)\n", "gui_esrrf.add_dropdown(\n", " \"cmaps\",\n", @@ -345,7 +327,7 @@ }, { "cell_type": "markdown", - "id": "e81aa794", + "id": "7ddaa874", "metadata": {}, "source": [ "## Calculate error map for the eSRRF image\n", @@ -355,7 +337,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3f34c3e9", + "id": "3cf50f12", "metadata": { "cellView": "form" }, @@ -510,7 +492,7 @@ }, { "cell_type": "markdown", - "id": "6a862eb7", + "id": "85b87835", "metadata": {}, "source": [ "## Calculate FRC resolution of the diffraction limited image\n", @@ -526,7 +508,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6cf8491e", + "id": "d116e1d7", "metadata": { "cellView": "form" }, @@ -586,7 +568,7 @@ }, { "cell_type": "markdown", - "id": "87ce0680", + "id": "a46f41b3", "metadata": {}, "source": [ "## Calculate FRC resolution of the SR image\n", @@ -602,7 +584,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7a12fae6", + "id": "989f8a2f", "metadata": { "cellView": "form" }, @@ -662,7 +644,7 @@ }, { "cell_type": "markdown", - "id": "cb79dc4f", + "id": "0099c8b9", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the diffraction limited image\n", @@ -679,7 +661,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8be5e188", + "id": "3fa7fe62", "metadata": { "cellView": "form" }, @@ -740,7 +722,7 @@ }, { "cell_type": "markdown", - "id": "11ef1523", + "id": "b602fb43", "metadata": {}, "source": [ "## Calculate Decorrelation analysis resolution of the SR image\n", @@ -757,7 +739,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fad0907a", + "id": "b3e04bb2", "metadata": { "cellView": "form" }, diff --git a/src/notebookchef/recipes/NonLocalMeansDenoising.txt b/src/notebookchef/recipes/NonLocalMeansDenoising.txt index e08046f3..cc1ab889 100644 --- a/src/notebookchef/recipes/NonLocalMeansDenoising.txt +++ b/src/notebookchef/recipes/NonLocalMeansDenoising.txt @@ -6,7 +6,6 @@ This notebook allows you to perform denoising on either 2 or 3 dimensional array $include: notebook_intro.txt $include: citations/nlm_denoising.txt __cellbreak__ -__cellbreak__ $code $include: notebook_setup.py __cellbreak__ From 6b348827a9fb5adc88d43c8923e0bcb73df1f4ce Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 17:30:48 +0100 Subject: [PATCH 62/82] disabling package level test for esrrf --- tests/test_package_level_calls.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_package_level_calls.py b/tests/test_package_level_calls.py index 9b3a5b39..d42e0957 100644 --- a/tests/test_package_level_calls.py +++ b/tests/test_package_level_calls.py @@ -6,22 +6,27 @@ def test_package_nlm(): img = np.random.random((100, 100)).astype(np.float32) nanopyx.non_local_means_denoising(img) + def test_package_SRRF(): img = np.random.random((10, 100, 100)).astype(np.float32) nanopyx.SRRF(img) -def test_package_eSRRF(): - img = np.random.random((10, 100, 100)).astype(np.float32) - nanopyx.eSRRF(img) + +# def test_package_eSRRF(): +# img = np.random.random((10, 100, 100)).astype(np.float32) +# nanopyx.eSRRF(img) + def test_package_frc(): img = np.random.random((2, 100, 100)).astype(np.float32) nanopyx.calculate_frc(img[0], img[1]) + def test_package_decorr(): img = np.random.random((100, 100)).astype(np.float32) nanopyx.calculate_decorr_analysis(img) + def test_package_error_map(): img = np.random.random((100, 100)).astype(np.float32) img_2 = np.random.random((500, 500)).astype(np.float32) From db6da2d4e0016f8eb6647602dccf520ca4961f7b Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 17:37:42 +0100 Subject: [PATCH 63/82] reverting --- tests/test_package_level_calls.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_package_level_calls.py b/tests/test_package_level_calls.py index d42e0957..147d9fb5 100644 --- a/tests/test_package_level_calls.py +++ b/tests/test_package_level_calls.py @@ -12,9 +12,9 @@ def test_package_SRRF(): nanopyx.SRRF(img) -# def test_package_eSRRF(): -# img = np.random.random((10, 100, 100)).astype(np.float32) -# nanopyx.eSRRF(img) +def test_package_eSRRF(): + img = np.random.random((10, 100, 100)).astype(np.float32) + nanopyx.eSRRF(img) def test_package_frc(): From 4cff2eee762e35fe51f8f78162a0849fe56b58b3 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 17:47:27 +0100 Subject: [PATCH 64/82] fixed margins --- .../nanopyx.core.transform._le_esrrf3d.pyx | 8 +++---- ...nsform._le_radial_gradient_convergence.pyx | 4 ++-- src/nanopyx/core/transform/_le_esrrf3d.pyx | 24 +++++++++---------- .../_le_radial_gradient_convergence.pyx | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index 98b86ded..e87f6dac 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -74,11 +74,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -201,8 +201,8 @@ class eSRRF3D(LiquidEngine): raise ValueError("Invalid mode. Use 'average' or 'std'.") # set margins - margin = int(radius) * magnification_xy - margin_z = int(radius_z) * magnification_z + margin = int(radius*magnification_xy) + margin_z = int(radius_z*magnification_z) lowest_slice = margin_z highest_slice = output_shape[0] - margin_z lowest_row = margin diff --git a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx index cce21dee..c1b960ee 100644 --- a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx @@ -47,7 +47,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm*magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -131,7 +131,7 @@ class RadialGradientConvergence(LiquidEngine): # Parameters cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm*magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index aa0ba85b..cf446ca3 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -69,11 +69,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -146,11 +146,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -223,11 +223,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -300,11 +300,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -376,11 +376,11 @@ class eSRRF3D(LiquidEngine): time_start = time.time() # calculate all constants cdef float sigma = radius / 2.355 - cdef int margin = int(2 * radius) * magnification_xy + cdef int margin = int(radius * magnification_xy) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float sigma_z = radius_z / 2.355 # Taking voxel size into account - cdef int margin_z = int(2 * radius_z) * magnification_z + cdef int margin_z = int(radius_z*magnification_z) cdef float tSS_z = 2 * sigma_z * sigma_z cdef float tSO_z = 2 * sigma_z + 1 cdef float fwhm = radius @@ -496,8 +496,8 @@ class eSRRF3D(LiquidEngine): raise ValueError("Invalid mode. Use 'average' or 'std'.") # set margins - margin = int(radius) * magnification_xy - margin_z = int(radius_z) * magnification_z + margin = int(radius*magnification_xy) + margin_z = int(radius_z*magnification_z) lowest_slice = margin_z highest_slice = output_shape[0] - margin_z lowest_row = margin diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx index f7f0461e..971a92d7 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx @@ -45,7 +45,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm*magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -219,7 +219,7 @@ class RadialGradientConvergence(LiquidEngine): # Parameters cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm*magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification From 4dff62fa11c4099fd51e39cc7af8f3464cf46065 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 18:10:51 +0100 Subject: [PATCH 65/82] added type casting --- .../core/transform/_le_radial_gradient_convergence.cl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl index c1c259a4..84fdb1c6 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl @@ -115,10 +115,10 @@ float _c_calculate_rgc(int xM, int yM, __global float* imIntGx, __global float* int nPixels_out = nRows * nCols; // gradient image dimensions - int nPixels_grad = nRows*Gx_Gy_MAGNIFICATION * nCols*Gx_Gy_MAGNIFICATION; + int nPixels_grad = (int) (nRows*Gx_Gy_MAGNIFICATION * nCols*Gx_Gy_MAGNIFICATION); - row = row + fwhm*magnification; - col = col + fwhm*magnification; + row = row + (int)(fwhm*magnification); + col = col + (int)(fwhm*magnification); if (doIntensityWeighting == 1) { image_out[f * nPixels_out + row * nCols + col] = _c_calculate_rgc(col, row, &imIntGx[f * nPixels_grad], &imIntGy[f * nPixels_grad], nCols, nRows, magnification, Gx_Gy_MAGNIFICATION, fwhm, tSO, tSS, sensitivity, offset, xyoffset, angle) * imInt[f * nPixels_out + row * nCols + col]; From 3ea41fba4513f544970452da8b57b3f788a86676 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 19:59:22 +0100 Subject: [PATCH 66/82] disabling macro pixel correction for test --- tests/test_package_level_calls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_package_level_calls.py b/tests/test_package_level_calls.py index 147d9fb5..99be11dd 100644 --- a/tests/test_package_level_calls.py +++ b/tests/test_package_level_calls.py @@ -14,7 +14,7 @@ def test_package_SRRF(): def test_package_eSRRF(): img = np.random.random((10, 100, 100)).astype(np.float32) - nanopyx.eSRRF(img) + nanopyx.eSRRF(img, macro_pixel_correction=False) def test_package_frc(): From 09ee6e0beba359948280c8323309b742429c9ba3 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Mon, 19 May 2025 20:46:47 +0100 Subject: [PATCH 67/82] adding missing fix --- ...pyx.core.transform._le_radial_gradient_convergence.pyx | 2 +- .../core/transform/_le_radial_gradient_convergence.pyx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx index c1b960ee..9302d6f8 100644 --- a/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_radial_gradient_convergence.pyx @@ -82,7 +82,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm * magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx index 971a92d7..ea41b027 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.pyx @@ -79,7 +79,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm * magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -111,7 +111,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm * magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -143,7 +143,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm * magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification @@ -175,7 +175,7 @@ class RadialGradientConvergence(LiquidEngine): """ cdef float sigma = radius / 2.355 cdef float fwhm = radius - cdef int margin = int(fwhm) * magnification + cdef int margin = int(fwhm * magnification) cdef float tSS = 2 * sigma * sigma cdef float tSO = 2 * sigma + 1 cdef float Gx_Gy_MAGNIFICATION = grad_magnification From 69a0fbc28964af61c7d5a5b00cafcc2279e9b66d Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 10:15:38 +0100 Subject: [PATCH 68/82] changing defaults --- src/mako_templates/nanopyx.core.transform._le_esrrf.pyx | 8 ++++---- src/nanopyx/methods/esrrf/eSRRF_workflow.py | 2 ++ tests/test_package_level_calls.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx index 95723951..ea8afd15 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf.pyx @@ -29,15 +29,15 @@ class eSRRF(LiquidEngine): self._designation = "eSRRF_ST" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification: int = 5, grad_magnification: int = 2, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification: int = 5, grad_magnification: int = 1, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, run_type=None): image = check_image(image) return self._run(image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification: int = 5, grad_magnification: int = 2, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True): + def benchmark(self, image, magnification: int = 5, grad_magnification: int = 1, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True): image = check_image(image) return super().benchmark(image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) - def _run_opencl(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True, device=None, mem_div=1): + def _run_opencl(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True, device=None, mem_div=1): """ @gpu """ @@ -181,7 +181,7 @@ class eSRRF(LiquidEngine): return output_image % for sch in schedulers: - def _run_${sch}(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True): + def _run_${sch}(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True): """ @cpu @threaded diff --git a/src/nanopyx/methods/esrrf/eSRRF_workflow.py b/src/nanopyx/methods/esrrf/eSRRF_workflow.py index 4cd8d3ea..d6f0f3b4 100644 --- a/src/nanopyx/methods/esrrf/eSRRF_workflow.py +++ b/src/nanopyx/methods/esrrf/eSRRF_workflow.py @@ -9,6 +9,7 @@ def eSRRF( image, magnification: int = 5, + grad_magnification: int = 1, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, @@ -49,6 +50,7 @@ def eSRRF( (image,), { "magnification": magnification, + "grad_magnification": grad_magnification, "radius": radius, "sensitivity": sensitivity, "doIntensityWeighting": doIntensityWeighting, diff --git a/tests/test_package_level_calls.py b/tests/test_package_level_calls.py index 99be11dd..147d9fb5 100644 --- a/tests/test_package_level_calls.py +++ b/tests/test_package_level_calls.py @@ -14,7 +14,7 @@ def test_package_SRRF(): def test_package_eSRRF(): img = np.random.random((10, 100, 100)).astype(np.float32) - nanopyx.eSRRF(img, macro_pixel_correction=False) + nanopyx.eSRRF(img) def test_package_frc(): From 445cbab002b8ff81228a57d9d3627d408677ffcf Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 10:42:44 +0100 Subject: [PATCH 69/82] type casting --- src/nanopyx/core/transform/mpcorrector.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/mpcorrector.pyx b/src/nanopyx/core/transform/mpcorrector.pyx index 43028c9d..10137ca0 100644 --- a/src/nanopyx/core/transform/mpcorrector.pyx +++ b/src/nanopyx/core/transform/mpcorrector.pyx @@ -72,5 +72,5 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification if correction != 0: img[s, yM, xM] /= correction - return img + return img.astype(np.float32) \ No newline at end of file From 8eb736f4f2b9e345a60d29c1e2f55b9050d473b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 20 May 2025 10:42:51 +0100 Subject: [PATCH 70/82] updated compiled file --- src/nanopyx/core/transform/_le_esrrf.pyx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/nanopyx/core/transform/_le_esrrf.pyx b/src/nanopyx/core/transform/_le_esrrf.pyx index 0eb1c5d4..20e92b38 100644 --- a/src/nanopyx/core/transform/_le_esrrf.pyx +++ b/src/nanopyx/core/transform/_le_esrrf.pyx @@ -27,15 +27,15 @@ class eSRRF(LiquidEngine): self._designation = "eSRRF_ST" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification: int = 5, grad_magnification: int = 2, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification: int = 5, grad_magnification: int = 1, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True, run_type=None): image = check_image(image) return self._run(image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification: int = 5, grad_magnification: int = 2, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True): + def benchmark(self, image, magnification: int = 5, grad_magnification: int = 1, radius: float = 1.5, sensitivity: float = 1, doIntensityWeighting: bool = True): image = check_image(image) return super().benchmark(image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting) - def _run_opencl(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True, device=None, mem_div=1): + def _run_opencl(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True, device=None, mem_div=1): """ @gpu """ @@ -178,7 +178,7 @@ class eSRRF(LiquidEngine): return output_image - def _run_threaded(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True): + def _run_threaded(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True): """ @cpu @threaded @@ -200,7 +200,7 @@ class eSRRF(LiquidEngine): radial_gradients = rgc.run(gradient_col_interp, gradient_row_interp, magnified_image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, offset=0, xyoffset=-0.5, angle=np.pi/4, run_type=runtype) return radial_gradients - def _run_threaded_guided(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True): + def _run_threaded_guided(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True): """ @cpu @threaded @@ -222,7 +222,7 @@ class eSRRF(LiquidEngine): radial_gradients = rgc.run(gradient_col_interp, gradient_row_interp, magnified_image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, offset=0, xyoffset=-0.5, angle=np.pi/4, run_type=runtype) return radial_gradients - def _run_threaded_dynamic(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True): + def _run_threaded_dynamic(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True): """ @cpu @threaded @@ -244,7 +244,7 @@ class eSRRF(LiquidEngine): radial_gradients = rgc.run(gradient_col_interp, gradient_row_interp, magnified_image, magnification=magnification, grad_magnification=grad_magnification, radius=radius, sensitivity=sensitivity, doIntensityWeighting=doIntensityWeighting, offset=0, xyoffset=-0.5, angle=np.pi/4, run_type=runtype) return radial_gradients - def _run_threaded_static(self, image, magnification=5, grad_magnification=2, radius=1.5, sensitivity=1, doIntensityWeighting=True): + def _run_threaded_static(self, image, magnification=5, grad_magnification=1, radius=1.5, sensitivity=1, doIntensityWeighting=True): """ @cpu @threaded From 04593a398329f7838cfc758dd84aed23ecd46a88 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 13:06:55 +0100 Subject: [PATCH 71/82] adding cast to array --- src/nanopyx/core/transform/mpcorrector.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nanopyx/core/transform/mpcorrector.pyx b/src/nanopyx/core/transform/mpcorrector.pyx index 10137ca0..08c186ae 100644 --- a/src/nanopyx/core/transform/mpcorrector.pyx +++ b/src/nanopyx/core/transform/mpcorrector.pyx @@ -72,5 +72,5 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification if correction != 0: img[s, yM, xM] /= correction - return img.astype(np.float32) + return np.asarray(img).astype(np.float32) \ No newline at end of file From 5fca0b8dd872d2c1fb6096af063c40d38ec14519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 20 May 2025 13:14:24 +0100 Subject: [PATCH 72/82] Clamping numpy version <2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0556ab16..a8ae98d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ dependencies = [ "liquid_engine", "mako>=1.3.0", "cython>=0.29.32", - "numpy>=1.22", + "numpy>=1.22,<2", "scipy>=1.8", "tifffile>=2022.5.4", "scikit-image>=0.19.2", From bd8e62b7572dc7eb6a9213e8e098c8f67866b750 Mon Sep 17 00:00:00 2001 From: brunomsaraiva Date: Tue, 20 May 2025 13:56:24 +0100 Subject: [PATCH 73/82] fix for mpcorrector --- src/nanopyx/core/transform/mpcorrector.pyx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/nanopyx/core/transform/mpcorrector.pyx b/src/nanopyx/core/transform/mpcorrector.pyx index 08c186ae..cf27a988 100644 --- a/src/nanopyx/core/transform/mpcorrector.pyx +++ b/src/nanopyx/core/transform/mpcorrector.pyx @@ -45,13 +45,12 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification cdef int rows = rowsM // magnification cdef int cols = colsM // magnification - cdef float map_value - cdef int x, y, offset, s, r, c - - cdef float[:, :] map + cdef float map_value, correction + cdef int x, y, offset, s, r, c, gx, gy, y_offset, x_offset + cdef float[:, :] correction_map for s in range(slices): - map = np.zeros((magnification, magnification), dtype=np.float32) + correction_map = np.zeros((magnification, magnification), dtype=np.float32) for ry in range(rows): for rx in range(cols): for y in range(magnification): @@ -59,8 +58,7 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification # global coordinates in original image gx = rx * magnification + x gy = ry * magnification + y - map[y, x] += img[s, gy, gx] - map = np.asarray(map) / (rows*cols) + correction_map[y, x] += img[s, gy, gx] / (rows * cols) for yM in range(rowsM): for xM in range(colsM): @@ -68,9 +66,9 @@ cdef float[:, :, :] _macro_pixel_corrector(float[:, :, :] img, int magnification x = xM // magnification y_offset = yM - y * magnification x_offset = xM - x * magnification - correction = map[y_offset, x_offset] + correction = correction_map[y_offset, x_offset] if correction != 0: img[s, yM, xM] /= correction - return np.asarray(img).astype(np.float32) + return np.asarray(img, dtype=np.float32) \ No newline at end of file From 3be0ec9d6bbc7638176053e4f186856673797890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Tue, 20 May 2025 18:09:23 +0100 Subject: [PATCH 74/82] Adding a clamp to the search space to make sure the xyoffset doesn't make it so we look out of bounds in the gradient images --- .../_c_sr_radial_gradient_convergence.c | 30 +++++++++++++++++-- .../_le_radial_gradient_convergence.cl | 21 +++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index dd269e5b..fcb9c0fe 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -28,6 +28,7 @@ void _rotate_vector(float* Gx, float* Gy, float angle) { float _c_calculate_rgc(int xM, int yM, float* imIntGx, float* imIntGy, int colsM, int rowsM, int magnification, float Gx_Gy_MAGNIFICATION, float fwhm, float tSO, float tSS, float sensitivity, float offset, float xyoffset, float angle) { float vx, vy, Gx, Gy, dx, dy, distance, distanceWeight, GdotR, Dk; + float correct_vx, correct_vy; float xc = (float)xM / magnification + offset; // offset in non-magnified space float yc = (float)yM / magnification + offset; @@ -51,8 +52,33 @@ float _c_calculate_rgc(int xM, int yM, float* imIntGx, float* imIntGy, int colsM distance = sqrt(dx * dx + dy * dy); if (distance != 0 && distance <= tSO) { - Gx = imIntGx[(int)((vy+xyoffset) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((vx+xyoffset) * magnification * Gx_Gy_MAGNIFICATION)]; - Gy = imIntGy[(int)((vy+xyoffset) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((vx+xyoffset) * magnification * Gx_Gy_MAGNIFICATION)]; + + correct_vx = vx+xyoffset; + correct_vy = vy+xyoffset; + + if (correct_vx= colsM * rowsM) { + // printf("magnification: %i, colsM: %i, xyoffset: %f\n", magnification, colsM, xyoffset); + // printf("Index out of bounds: %i, %f, %f\n", linear_index, vy, vx); + // continue; // Skip this iteration if the index is out of bounds + // } + + Gx = imIntGx[(int)((correct_vy) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((correct_vx) * magnification * Gx_Gy_MAGNIFICATION)]; + Gy = imIntGy[(int)((correct_vy) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((correct_vx) * magnification * Gx_Gy_MAGNIFICATION)]; _rotate_vector(&Gx, &Gy, angle); diff --git a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl index 84fdb1c6..c1caea2e 100644 --- a/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl +++ b/src/nanopyx/core/transform/_le_radial_gradient_convergence.cl @@ -47,6 +47,7 @@ float _c_calculate_rgc(int xM, int yM, __global float* imIntGx, __global float* float vx, vy, Gx, Gy, dx, dy, distance, distanceWeight, GdotR, Dk; float2 correctedv; float2 correctedd; + float correct_vx, correct_vy; float xc = (float)xM / magnification + offset; // offset in non-magnified space float yc = (float)yM / magnification + offset; @@ -70,8 +71,24 @@ float _c_calculate_rgc(int xM, int yM, __global float* imIntGx, __global float* distance = sqrt(dx * dx + dy * dy); if (distance != 0 && distance <= tSO) { - Gx = imIntGx[(int)((vy+xyoffset) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((vx+xyoffset) * magnification * Gx_Gy_MAGNIFICATION)]; - Gy = imIntGy[(int)((vy+xyoffset) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((vx+xyoffset) * magnification * Gx_Gy_MAGNIFICATION)]; + + correct_vx = vx+xyoffset; + correct_vy = vy+xyoffset; + + if (correct_vx Date: Tue, 20 May 2025 19:25:34 +0100 Subject: [PATCH 75/82] changing radius_z default value --- .../nanopyx.core.transform._le_esrrf3d.pyx | 8 ++++---- src/nanopyx/core/transform/_le_esrrf3d.pyx | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx index e87f6dac..d35aa728 100644 --- a/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx +++ b/src/mako_templates/nanopyx.core.transform._le_esrrf3d.pyx @@ -34,7 +34,7 @@ class eSRRF3D(LiquidEngine): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) @@ -52,7 +52,7 @@ class eSRRF3D(LiquidEngine): return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: @@ -62,7 +62,7 @@ class eSRRF3D(LiquidEngine): return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) % for sch in schedulers: - def _run_${sch}(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_${sch}(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu % if sch!='unthreaded': @@ -149,7 +149,7 @@ class eSRRF3D(LiquidEngine): return np.asarray(rgc_out) % endfor - def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ diff --git a/src/nanopyx/core/transform/_le_esrrf3d.pyx b/src/nanopyx/core/transform/_le_esrrf3d.pyx index cf446ca3..7af43854 100644 --- a/src/nanopyx/core/transform/_le_esrrf3d.pyx +++ b/src/nanopyx/core/transform/_le_esrrf3d.pyx @@ -32,7 +32,7 @@ class eSRRF3D(LiquidEngine): self._designation = "eSRRF_3D" super().__init__(clear_benchmarks=clear_benchmarks, testing=testing, verbose=verbose) - def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): + def run(self, image, magnification_xy: int = 2, magnification_z: int = 2, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, run_type=None): # TODO: complete and check _run inputs, need to complete variables? if len(image.shape) == 3: image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) @@ -50,7 +50,7 @@ class eSRRF3D(LiquidEngine): return self._run(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting, run_type=run_type) - def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def benchmark(self, image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): if image.dtype != np.float32: image = image.astype(np.float32) if len(image.shape) == 4: @@ -59,7 +59,7 @@ class eSRRF3D(LiquidEngine): image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2])) return super().benchmark(image, magnification_xy=magnification_xy, magnification_z=magnification_z, radius=radius, radius_z=radius_z, voxel_ratio=voxel_ratio, sensitivity=sensitivity, mode=mode, doIntensityWeighting=doIntensityWeighting) - def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -136,7 +136,7 @@ class eSRRF3D(LiquidEngine): return rgc_out else: return np.asarray(rgc_out) - def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_guided(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -213,7 +213,7 @@ class eSRRF3D(LiquidEngine): return rgc_out else: return np.asarray(rgc_out) - def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_dynamic(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -290,7 +290,7 @@ class eSRRF3D(LiquidEngine): return rgc_out else: return np.asarray(rgc_out) - def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_threaded_static(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @threaded @@ -367,7 +367,7 @@ class eSRRF3D(LiquidEngine): return rgc_out else: return np.asarray(rgc_out) - def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): + def _run_unthreaded(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True): """ @cpu @cython @@ -444,7 +444,7 @@ class eSRRF3D(LiquidEngine): else: return np.asarray(rgc_out) - def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 0.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): + def _run_opencl(self, float[:,:,:,:] image, magnification_xy: int = 5, magnification_z: int = 5, radius: float = 1.5, radius_z: float = 1.5, voxel_ratio: float = 4.0, sensitivity: float = 1, mode: str = "average", doIntensityWeighting: bool = True, device=None, mem_div=1): """ @gpu """ From 7710675dfcf6b7fac513ef6f0d99a49d98a3af7f Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 19:53:52 +0100 Subject: [PATCH 76/82] removing 3.13 from actions --- .../build_test_deploy_all_wheels.yml | 8 +- .github/workflows/build_wheels.yml | 135 +++++------------- .github/workflows/nanopyx_night_mechanic.yml | 7 +- 3 files changed, 45 insertions(+), 105 deletions(-) diff --git a/.github/workflows/build_test_deploy_all_wheels.yml b/.github/workflows/build_test_deploy_all_wheels.yml index cbf12e37..cbcd897d 100644 --- a/.github/workflows/build_test_deploy_all_wheels.yml +++ b/.github/workflows/build_test_deploy_all_wheels.yml @@ -13,15 +13,15 @@ jobs: - os: macOS-ARM runner: [self-hosted, macOS, ARM64] archs: arm64 - cibw_build: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 + cibw_build: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 - os: manylinux runner: [self-hosted, Ubuntu, Native] archs: auto - cibw_build: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64 + cibw_build: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 - os: windows runner: [self-hosted, Windows] archs: auto - cibw_build: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 cp313-win_amd64 + cibw_build: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 steps: - uses: actions/checkout@v4 @@ -74,4 +74,4 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.BMS_DEPLOY_PYPI_TOKEN }} - packages-dir: ./final_dist/ + packages-dir: ./final_dist/ \ No newline at end of file diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index fd1b3457..6476c9a3 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -1,113 +1,56 @@ -name: (self-hosted) Build wheels - -on: - workflow_dispatch: - inputs: - logLevel: - description: "Log level" - required: true - default: "warning" - type: choice - options: - - info - - warning - - debug +name: (self-hosted) Build and Publish Wheels +on: workflow_dispatch jobs: + build: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: true + matrix: + include: + - os: macOS-ARM + runner: [self-hosted, macOS, ARM64] + archs: arm64 + cibw_build: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 + - os: manylinux + runner: [self-hosted, Ubuntu, Native] + archs: auto + cibw_build: cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 + - os: windows + runner: [self-hosted, Windows] + archs: auto + cibw_build: cp39-win_amd64 cp310-win_amd64 cp311-win_amd64 cp312-win_amd64 - test_on_ubuntu_container: - runs-on: [self-hosted, Ubuntu, Native] steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: | - 3.9 - 3.10 - 3.11 - 3.12 - 3.13 - - name: Run Nox Quick-Tests - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade nox - python -m pip install cibuildwheel==2.16.2 - python -m nox --sessions clear_wheelhouse build_wheel build_sdist test_wheel - env: - LOG_LEVEL: ${{ github.event.inputs.logLevel }} - - name: Upload Coverage - uses: codecov/codecov-action@v3 - with: - files: coverage.xml # optional - flags: pytests # optional - fail_ci_if_error: false # optional (default = false) - verbose: true # optional (default = false) - test_on_windows: - runs-on: [self-hosted, Windows, X64] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - uses: actions/setup-python@v5 with: - python-version: | - 3.9 - 3.10 - 3.11 - 3.12 - 3.13 - - name: Run Nox Quick-Tests + python-version: 3.9 # Works as bootstrap for cibuildwheel + + - name: Install cibuildwheel and nox run: | python -m pip install --upgrade pip - python -m pip install --upgrade nox python -m pip install cibuildwheel==2.16.2 - python -m pip install setuptools==73.0.1 - python -m nox --sessions clear_wheelhouse build_wheel build_sdist test_wheel - env: - LOG_LEVEL: ${{ github.event.inputs.logLevel }} - - name: Upload Coverage - uses: codecov/codecov-action@v3 - with: - files: coverage.xml # optional - flags: pytests # optional - fail_ci_if_error: false # optional (default = false) - verbose: true # optional (default = false) + python -m pip install nox + + - name: Build sdist (only on Windows) + if: matrix.os == 'windows' + run: python -m nox --sessions build_sdist - test_on_macos: - runs-on: [self-hosted, macOS, ARM64] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: | - 3.9 - 3.10 - 3.11 - 3.12 - 3.13 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade nox - python -m pip install cibuildwheel==2.23.3 - python -m pip install setuptools==73.0.1 - brew install libomp # Ensure libomp is installed via Homebrew - - name: Clear Wheels - run: | - python -m nox --sessions clear_wheelhouse - env: - LOG_LEVEL: ${{ github.event.inputs.logLevel }} - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 - CIBW_ARCHS: arm64 - MACOSX_DEPLOYMENT_TARGET: 14.0 # Set macOS deployment target - - name: Test wheels + CIBW_BUILD: ${{ matrix.cibw_build }} + CIBW_ARCHS: ${{ matrix.archs }} + + - name: Test wheels (only on macOS ARM64) + if: matrix.os == 'macOS-ARM' run: python -m nox --sessions test_wheel + + - uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.os }} + path: ./wheelhouse/* diff --git a/.github/workflows/nanopyx_night_mechanic.yml b/.github/workflows/nanopyx_night_mechanic.yml index 42e42b24..4de87cad 100644 --- a/.github/workflows/nanopyx_night_mechanic.yml +++ b/.github/workflows/nanopyx_night_mechanic.yml @@ -33,7 +33,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -63,7 +62,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -94,12 +92,11 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade nox - python -m pip install cibuildwheel==2.23.3 + python -m pip install cibuildwheel==2.16.2 python -m pip install setuptools==73.0.1 - name: Run Nox Quick-Tests run: | @@ -110,7 +107,7 @@ jobs: run: python -m cibuildwheel --output-dir wheelhouse # to supply options, put them in 'env', like: env: - CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 cp313-macosx_arm64 + CIBW_BUILD: cp39-macosx_arm64 cp310-macosx_arm64 cp311-macosx_arm64 cp312-macosx_arm64 CIBW_ARCHS: arm64 - name: Test wheels From 5513426f53975058e444cfc3a530b3d4c788bc8d Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 19:54:25 +0100 Subject: [PATCH 77/82] removing 3.13 changes --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a8ae98d2..d53796b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,7 +170,7 @@ omit = [ [tool.cibuildwheel] # https://cibuildwheel.readthedocs.io/en/stable/options/ skip = ["pp*", "*musllinux*"] -build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] +build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*"] # it is not possible to test arm64 and the arm64 part of a universal2 wheel on this CI platform test-skip = ["*arm64"] # build-frontend = "pip" From bd233f9e577121ff4f44d595d168d2ebe6c97045 Mon Sep 17 00:00:00 2001 From: Bruno Manuel Santos Saraiva Date: Tue, 20 May 2025 21:41:29 +0100 Subject: [PATCH 78/82] removing 3.13 --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 084ea27e..86d51408 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,7 +6,7 @@ import nox DIR = Path(__file__).parent.resolve() -PYTHON_ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13"] +PYTHON_ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12"] PYTHON_DEFAULT_VERSION = "3.10" # Platform logic From 2126a3d1add56f465a8097540cd23ef3400d397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Wed, 21 May 2025 10:45:42 +0100 Subject: [PATCH 79/82] Removing debugging comments --- src/include/_c_sr_radial_gradient_convergence.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/include/_c_sr_radial_gradient_convergence.c b/src/include/_c_sr_radial_gradient_convergence.c index fcb9c0fe..7162dd46 100644 --- a/src/include/_c_sr_radial_gradient_convergence.c +++ b/src/include/_c_sr_radial_gradient_convergence.c @@ -68,15 +68,6 @@ float _c_calculate_rgc(int xM, int yM, float* imIntGx, float* imIntGy, int colsM }; - // // Calculate the linear index for the gradient images - // int linear_index = (int)((vy+xyoffset) * magnification * colsM) + (int)((vx+xyoffset) * magnification); - // // Ensure the index is within bounds - // if (linear_index < 0 || linear_index >= colsM * rowsM) { - // printf("magnification: %i, colsM: %i, xyoffset: %f\n", magnification, colsM, xyoffset); - // printf("Index out of bounds: %i, %f, %f\n", linear_index, vy, vx); - // continue; // Skip this iteration if the index is out of bounds - // } - Gx = imIntGx[(int)((correct_vy) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((correct_vx) * magnification * Gx_Gy_MAGNIFICATION)]; Gy = imIntGy[(int)((correct_vy) * magnification * Gx_Gy_MAGNIFICATION * colsM * Gx_Gy_MAGNIFICATION) + (int)((correct_vx) * magnification * Gx_Gy_MAGNIFICATION)]; From 59e30e2dba85cbe00bcab8aa17e949b517031d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Wed, 21 May 2025 10:47:05 +0100 Subject: [PATCH 80/82] Removing unused imports --- src/nanopyx/core/transform/_interpolation.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/nanopyx/core/transform/_interpolation.pyx b/src/nanopyx/core/transform/_interpolation.pyx index b09c040b..e3c6cd7c 100644 --- a/src/nanopyx/core/transform/_interpolation.pyx +++ b/src/nanopyx/core/transform/_interpolation.pyx @@ -7,9 +7,6 @@ import cython from math import floor from ._le_interpolation_catmull_rom import ShiftAndMagnify as ShiftMagnify_CR -from ._le_interpolation_bicubic import ShiftAndMagnify as ShiftMagnify_BC -from ._le_interpolation_lanczos import ShiftAndMagnify as ShiftMagnify_Lanczos -from ._le_interpolation_nearest_neighbor import ShiftAndMagnify as ShiftMagnify_NN from cython.parallel import prange From 2ae4e6d2a1ac8fbd62fae06073db24e617080055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Wed, 21 May 2025 10:53:09 +0100 Subject: [PATCH 81/82] Updating docstrings --- src/nanopyx/methods/esrrf/eSRRF_workflow.py | 1 + .../methods/esrrf_3d/eSRRF3D_workflow.py | 30 ++++++++++++++++++- src/nanopyx/methods/srrf/SRRF_workflow.py | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/nanopyx/methods/esrrf/eSRRF_workflow.py b/src/nanopyx/methods/esrrf/eSRRF_workflow.py index d6f0f3b4..48f7e250 100644 --- a/src/nanopyx/methods/esrrf/eSRRF_workflow.py +++ b/src/nanopyx/methods/esrrf/eSRRF_workflow.py @@ -25,6 +25,7 @@ def eSRRF( radius (float, optional): Radius parameter for eSRRF analysis (default is 1.5). sensitivity (float, optional): Sensitivity parameter for eSRRF analysis (default is 1). doIntensityWeighting (bool, optional): Enable intensity weighting (default is True). + macro_pixel_correction (bool, optional): Enable macro pixel correction (default is True). _force_run_type (str, optional): Force a specific run type for the analysis (default is None). Returns: diff --git a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py index 29e09f85..c524e3cc 100644 --- a/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py +++ b/src/nanopyx/methods/esrrf_3d/eSRRF3D_workflow.py @@ -19,7 +19,35 @@ def eSRRF3D( _force_run_type=None, ): """ - #TODO + Perform eSRRF3D analysis on an image. + + Args: + img (numpy.ndarray): The input image for eSRRF3D analysis. + magnification_xy (int, optional): Magnification factor in XY plane (default is 2). + magnification_z (int, optional): Magnification factor in Z plane (default is 2). + radius (float, optional): Radius parameter for eSRRF3D analysis (default is 1.5). + radius_z (float, optional): Radius parameter in Z direction for eSRRF3D analysis (default is 0.5). + voxel_ratio (float, optional): Ratio of voxel size in XY to Z direction (default is 4.0). + sensitivity (float, optional): Sensitivity parameter for eSRRF3D analysis (default is 1). + mode (str, optional): Time projection mode (default is "average"). + doIntensityWeighting (bool, optional): Enable intensity weighting (default is True). + macro_pixel_correction (bool, optional): Enable macro pixel correction (default is True). + _force_run_type (str, optional): Force a specific run type for the analysis (default is None). + + Returns: + numpy.ndarray: The result of eSRRF3D analysis, typically representing the localizations. + + Example: + result = eSRRF3D(image, magnification_xy=2, magnification_z=2, radius=1.5, sensitivity=1, doIntensityWeighting=True) + + Note: + - eSRRF3D (enhanced Super-Resolution Radial Fluctuations 3D) is a method for super-resolution localization microscopy in three dimensions. + - This function sets up a workflow to perform eSRRF3D analysis on the input image. + + See Also: + - eSRRF3D_ST: The eSRRF3D step that performs the actual analysis. + - Workflow: The class used to define and run analysis workflows. + """ _eSRRF3D = Workflow( diff --git a/src/nanopyx/methods/srrf/SRRF_workflow.py b/src/nanopyx/methods/srrf/SRRF_workflow.py index 1e31081e..d2e18f95 100644 --- a/src/nanopyx/methods/srrf/SRRF_workflow.py +++ b/src/nanopyx/methods/srrf/SRRF_workflow.py @@ -26,6 +26,7 @@ def SRRF( border (int, optional): Border parameter for radiality analysis (default is 0). radialityPositivityConstraint (bool, optional): Enable radiality positivity constraint (default is True). doIntensityWeighting (bool, optional): Enable intensity weighting (default is True). + macro_pixel_correction (bool, optional): Enable macro pixel correction (default is True). _force_run_type (str, optional): Force a specific run type for the analysis (default is None). Returns: From f81bc57b9b5a37657794b44a6977a434c8af77c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Brito?= <50997716+antmsbrito@users.noreply.github.com> Date: Wed, 21 May 2025 10:55:31 +0100 Subject: [PATCH 82/82] Removing all mentions to python 3.13 --- .github/workflows/buildtest_windows_x86.yml | 1 - .github/workflows/nanopyx_oncall_all_os.yml | 3 --- .github/workflows/nanopyx_oncall_mechanic.yml | 1 - README.md | 2 +- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/buildtest_windows_x86.yml b/.github/workflows/buildtest_windows_x86.yml index d830b8c3..fae66bf9 100644 --- a/.github/workflows/buildtest_windows_x86.yml +++ b/.github/workflows/buildtest_windows_x86.yml @@ -37,7 +37,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Install cibuildwheel run: python -m pip install cibuildwheel==2.16.2 diff --git a/.github/workflows/nanopyx_oncall_all_os.yml b/.github/workflows/nanopyx_oncall_all_os.yml index 78922da7..2eb74443 100644 --- a/.github/workflows/nanopyx_oncall_all_os.yml +++ b/.github/workflows/nanopyx_oncall_all_os.yml @@ -29,7 +29,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -58,7 +57,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip @@ -87,7 +85,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/nanopyx_oncall_mechanic.yml b/.github/workflows/nanopyx_oncall_mechanic.yml index a1913a87..ad81563e 100644 --- a/.github/workflows/nanopyx_oncall_mechanic.yml +++ b/.github/workflows/nanopyx_oncall_mechanic.yml @@ -42,7 +42,6 @@ jobs: 3.10 3.11 3.12 - 3.13 - name: Run Nox Quick-Tests run: | python -m pip install --upgrade pip diff --git a/README.md b/README.md index 905e422d..43ad0d7d 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ pip install napari-nanopyx ## Installation -`NanoPyx` is compatible and tested with Python 3.9, 3.10, 3.11, 3.12 and 3.13 in MacOS, Windows and Linux. Installation time depends on your hardware and internet connection, but should take around 5 minutes. +`NanoPyx` is compatible and tested with Python 3.9, 3.10, 3.11 and 3.12 in MacOS, Windows and Linux. Installation time depends on your hardware and internet connection, but should take around 5 minutes. You can install `NanoPyx` via [pip]: